<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Nick</title>
    <description>The latest articles on DEV Community by Nick (@homolibere).</description>
    <link>https://dev.to/homolibere</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1181392%2F50ad4a50-d1dc-4910-975d-76c4f5a8bb7a.jpeg</url>
      <title>DEV Community: Nick</title>
      <link>https://dev.to/homolibere</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/homolibere"/>
    <language>en</language>
    <item>
      <title>Part 6: Engine Internals - The Execution Pipeline</title>
      <dc:creator>Nick</dc:creator>
      <pubDate>Mon, 15 Jun 2026 08:01:00 +0000</pubDate>
      <link>https://dev.to/homolibere/part-6-engine-internals-the-execution-pipeline-20o9</link>
      <guid>https://dev.to/homolibere/part-6-engine-internals-the-execution-pipeline-20o9</guid>
      <description>&lt;p&gt;We have seen how you design a workflow and how the domain model keeps it strictly type-safe. Today, we are peeling back the most complex layer of Vyshyvanka: The Execution Pipeline. This is where the graph you drew in the browser becomes a series of actual operations executing on the server.&lt;/p&gt;

&lt;h3&gt;
  
  
  From Graph to Execution
&lt;/h3&gt;

&lt;p&gt;When you hit 'Run', the engine doesn't just start running nodes randomly. It first performs a &lt;strong&gt;topological sort&lt;/strong&gt; on your directed graph using &lt;code&gt;BuildExecutionLevels&lt;/code&gt;. This groups nodes into execution levels - nodes at the same level have no dependencies on each other and can run in parallel, while each level depends only on previous levels.&lt;/p&gt;

&lt;p&gt;If Node B needs the output of Node A, the topological sort guarantees Node A finishes first. If there's a circular dependency in your graph, our validator catches it before it ever hits the engine.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Life Cycle of an Execution
&lt;/h3&gt;

&lt;p&gt;Every run is an immutable record that transitions through a strict state machine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Pending ──→ Running ──→ Completed
  │            │
  │            ├──→ Failed
  │            │
  └──→ Cancelled ←──┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Pending:&lt;/strong&gt; The trigger has fired, and we have initialized the execution context.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Running:&lt;/strong&gt; We start processing the sorted levels of nodes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Terminal States:&lt;/strong&gt; Once a workflow hits &lt;code&gt;Completed&lt;/code&gt;, &lt;code&gt;Failed&lt;/code&gt;, or &lt;code&gt;Cancelled&lt;/code&gt;, that's it. No further transitions allowed.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We don't allow mutable state transitions back from a terminal state. This ensures that every execution record is an audit-ready snapshot that you can trust.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Pipeline at Work
&lt;/h3&gt;

&lt;p&gt;The pipeline itself is fully asynchronous. We leverage .NET's &lt;code&gt;async/await&lt;/code&gt; throughout the entire chain, passing a &lt;code&gt;CancellationToken&lt;/code&gt; into every method. If a user cancels a workflow or the server initiates a graceful shutdown, every single node in the pipeline receives that signal and halts immediately.&lt;/p&gt;

&lt;p&gt;Here is a simplified view of the core execution loop:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ExecutionResult&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;ExecuteAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;Workflow&lt;/span&gt; &lt;span class="n"&gt;workflow&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;IExecutionContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;cancellationToken&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;executionLevels&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;BuildExecutionLevels&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;workflow&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;maxParallelism&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;GetEffectiveMaxParallelism&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;workflow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MaxDegreeOfParallelism&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;level&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;executionLevels&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;cancellationToken&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ThrowIfCancellationRequested&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;levelResults&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;ExecuteLevelWithThrottlingAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;workflow&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;level&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nodeResults&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;maxParallelism&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cancellationToken&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Check StopOnFirstError policy&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;workflow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ErrorHandling&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;ErrorHandlingMode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StopOnFirstError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;failedResult&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;levelResults&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FirstOrDefault&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Success&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;failedResult&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;BuildResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="cm"&gt;/* ... */&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inside the pipeline, each node goes through several stages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Input Gathering:&lt;/strong&gt; We collect outputs from upstream nodes via connection mapping. Multi-input nodes receive a merged JSON object keyed by target port name.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Expression Evaluation:&lt;/strong&gt; All &lt;code&gt;{{ nodes.someNode.data }}&lt;/code&gt; expressions in the node configuration are resolved in real-time from the execution context before the node runs.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Node Execution:&lt;/strong&gt; We invoke &lt;code&gt;ExecuteAsync&lt;/code&gt; on the node instance. Since all nodes inherit from our base classes (&lt;code&gt;BaseTriggerNode&lt;/code&gt;, &lt;code&gt;BaseActionNode&lt;/code&gt;, &lt;code&gt;BaseLogicNode&lt;/code&gt;), the engine handles them uniformly regardless of what they actually do.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Output Storage:&lt;/strong&gt; Successful node outputs are stored in the context, ready for downstream nodes to reference.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Parallel Branch Execution
&lt;/h3&gt;

&lt;p&gt;Nodes at the same execution level run in parallel, throttled by a &lt;code&gt;SemaphoreSlim&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;NodeExecutionResult&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;]&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;ExecuteLevelWithThrottlingAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;Workflow&lt;/span&gt; &lt;span class="n"&gt;workflow&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;level&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IExecutionContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;ConcurrentBag&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;NodeExecutionResult&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;nodeResults&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;maxParallelism&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;cancellationToken&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;semaphore&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;SemaphoreSlim&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;maxParallelism&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;maxParallelism&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;tasks&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;level&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;nodeId&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;semaphore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WaitAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cancellationToken&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;ExecuteNodeInWorkflowAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;workflow&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nodeId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nodeResults&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cancellationToken&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;semaphore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Release&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;ToList&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WhenAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tasks&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The default max parallelism is &lt;code&gt;Environment.ProcessorCount * 2&lt;/code&gt;, but you can configure it per workflow.&lt;/p&gt;

&lt;h3&gt;
  
  
  Handling Failure
&lt;/h3&gt;

&lt;p&gt;What happens when a node fails? The engine supports two error handling modes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;StopOnFirstError:&lt;/strong&gt; Stops the entire workflow immediately when any node fails.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ContinueOnError:&lt;/strong&gt; Marks the failed node and continues executing independent branches. Dependent nodes on the failed branch are skipped.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Because we have that immutable execution record, you can see exactly which node caused the issue, what data it received, and the error message — all structured in the &lt;code&gt;NodeExecutionResult&lt;/code&gt; list.&lt;/p&gt;

&lt;h3&gt;
  
  
  Plugin Node Isolation
&lt;/h3&gt;

&lt;p&gt;Plugin nodes (from external assemblies) are executed through &lt;code&gt;IPluginHost.ExecuteNodeInIsolationAsync&lt;/code&gt;, which adds an additional timeout layer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;NodeOutput&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;ExecuteNodeInstanceAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;INode&lt;/span&gt; &lt;span class="n"&gt;nodeInstance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;NodeInput&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IExecutionContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TimeSpan&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_pluginHost&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;_pluginHost&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsPluginNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nodeInstance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_pluginHost&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ExecuteNodeInIsolationAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nodeInstance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;nodeInstance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ExecuteAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This ensures that a misbehaving plugin cannot hang the entire engine.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why Asynchronous?
&lt;/h3&gt;

&lt;p&gt;We chose async-first because workflow engines are inherently I/O bound. You are constantly waiting for databases, external APIs, and file systems. By using &lt;code&gt;async/await&lt;/code&gt; everywhere, we ensure that while one workflow waits for a response, the thread is returned to the pool to serve other workflows. This is how a single engine instance can manage hundreds of concurrent executions without thread pool starvation.&lt;/p&gt;

&lt;p&gt;In the next part, we will discuss Part 7: Expression Language Syntax - How dynamic data flows between your nodes. Stay tuned!&lt;/p&gt;




&lt;p&gt;Check out the project source code here: &lt;a href="https://github.com/homolibere/Vyshyvanka" rel="noopener noreferrer"&gt;https://github.com/homolibere/Vyshyvanka&lt;/a&gt;&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>architecture</category>
      <category>csharp</category>
      <category>performance</category>
    </item>
    <item>
      <title>Part 5: Designing the Designer - Building a Blazor WASM Experience</title>
      <dc:creator>Nick</dc:creator>
      <pubDate>Fri, 12 Jun 2026 08:00:00 +0000</pubDate>
      <link>https://dev.to/homolibere/part-5-designing-the-designer-building-a-blazor-wasm-experience-me7</link>
      <guid>https://dev.to/homolibere/part-5-designing-the-designer-building-a-blazor-wasm-experience-me7</guid>
      <description>&lt;p&gt;In our last deep dive, we talked about the logic and data structures that drive Vyshyvanka. But software isn't just about clean backends; it's about how the user interacts with those systems. Today, we're stepping into the browser to talk about the Vyshyvanka Designer - our Blazor WebAssembly canvas.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why Blazor WebAssembly?
&lt;/h3&gt;

&lt;p&gt;When we decided to build a visual workflow designer, we had a few options: React, Vue, or maybe a native desktop app. We chose Blazor WebAssembly for a few critical reasons:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Shared Logic:&lt;/strong&gt; By staying within the .NET ecosystem, we can share domain models and validation logic between our Engine and the Designer. When we update a node definition in the Core, the Designer understands those changes instantly without rewriting types in TypeScript.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Performance:&lt;/strong&gt; Blazor WASM gives us the performance of a compiled language inside the browser, which is crucial when you're dragging and dropping dozens of nodes or panning around a large workflow.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Developer Velocity:&lt;/strong&gt; Our team works best in C#. Having a single language for both our ASP.NET Core API and our frontend allows us to move fast without the context switching of a JavaScript-heavy stack.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  The Designer Architecture
&lt;/h3&gt;

&lt;p&gt;The Designer isn't one big component; it's a system of decomposed services designed to keep the UI snappy and maintainable:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;WorkflowStore:&lt;/strong&gt; The single source of truth. It holds your workflow data, node definitions, and the 'dirty' flag that tells the UI when you have unsaved changes.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;WorkflowEditService:&lt;/strong&gt; This is the brains of the operation. Whenever you drag a node, add a connection, or delete a step, the EditService handles the business logic. It doesn't worry about rendering; it just worries about state.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;WorkflowValidationService:&lt;/strong&gt; We run validation in the browser &lt;em&gt;as you edit&lt;/em&gt;. If you try to connect an 'Object' output to a 'Boolean' input, the UI turns red before you even let go of your mouse.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;CanvasStateService:&lt;/strong&gt; Handles the 'feel' of the application. Panning, zooming, node selection, and undo/redo stacks are managed here.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Communicating with the Engine
&lt;/h3&gt;

&lt;p&gt;We made a hard architectural choice early on: &lt;strong&gt;The Designer communicates exclusively via HTTP.&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;There are no direct WebSocket connections or shared memory paths to the Engine. By forcing all interaction through the &lt;code&gt;WorkflowApiClient&lt;/code&gt;, we ensure the Designer remains a pure client. This means we can treat the entire Designer as a standalone application that could be hosted anywhere. It doesn't care if the engine is running on your machine, in a Docker container, or in the cloud—as long as it can hit the API, it works.&lt;/p&gt;

&lt;h3&gt;
  
  
  Component Design Rules
&lt;/h3&gt;

&lt;p&gt;To keep our UI code clean, we enforce strict rules:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;No &lt;code&gt;@code&lt;/code&gt; blocks in .razor files:&lt;/strong&gt; All logic lives in &lt;code&gt;Name.razor.cs&lt;/code&gt; partial classes.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Injection over &lt;code&gt;@inject&lt;/code&gt;:&lt;/strong&gt; We use &lt;code&gt;[Inject]&lt;/code&gt; attributes in our code-behind to keep the markup clean and the dependencies explicit.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;One Component, Three Files:&lt;/strong&gt; Every visual piece follows a &lt;code&gt;Name.razor&lt;/code&gt;, &lt;code&gt;Name.razor.cs&lt;/code&gt;, and &lt;code&gt;Name.razor.css&lt;/code&gt; pattern. It keeps our styles isolated and our logic readable.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Goal: Real-Time Clarity
&lt;/h3&gt;

&lt;p&gt;The goal of the Designer isn't just to look good; it's to provide real-time clarity. You should know exactly what your workflow is doing at every moment. By integrating our validation service directly into the canvas, we turn the design phase into an active conversation between you and the system.&lt;/p&gt;

&lt;p&gt;Building a visual editor in Blazor was an adventure, but the results—a type-safe, performant, and deeply integrated experience—made it all worth it.&lt;/p&gt;

&lt;p&gt;In the next part, we'll look at the execution engine internals: &lt;strong&gt;The Pipeline&lt;/strong&gt;. We'll explain how we resolve the graph, manage node execution order, and handle the state machine from 'Pending' to 'Completed'. Stay tuned!&lt;/p&gt;




&lt;p&gt;Check out the project source code here: &lt;a href="https://github.com/homolibere/Vyshyvanka" rel="noopener noreferrer"&gt;https://github.com/homolibere/Vyshyvanka&lt;/a&gt;&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>blazor</category>
      <category>webdev</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Part 4: The Domain Model - Understanding Workflows, Nodes, and Connections</title>
      <dc:creator>Nick</dc:creator>
      <pubDate>Thu, 11 Jun 2026 08:00:00 +0000</pubDate>
      <link>https://dev.to/homolibere/part-4-the-domain-model-understanding-workflows-nodes-and-connections-425c</link>
      <guid>https://dev.to/homolibere/part-4-the-domain-model-understanding-workflows-nodes-and-connections-425c</guid>
      <description>&lt;p&gt;In our last deep dive, we looked at how Vyshyvanka is put together under the hood. Today, we're zooming in on what actually makes the engine tick: the Domain Model. At the end of the day, Vyshyvanka isn't just a collection of services; it's a system designed to manage a directed graph of operations that we call a Workflow.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Workflow: A Directed Graph
&lt;/h3&gt;

&lt;p&gt;At its heart, a workflow is a directed graph where nodes represent operations and connections represent the flow of data. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Exactly One Trigger:&lt;/strong&gt; Every workflow needs a starting point. Whether it's a webhook, a timer, or a manual button press, we enforce that there is exactly one trigger node.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Ownership:&lt;/strong&gt; Every workflow is owned by a single user, ensuring that permissions and access are managed consistently across the board.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Nodes: The Atomic Operations
&lt;/h3&gt;

&lt;p&gt;Nodes are the building blocks of your automation. We categorize them based on their behavior, which dictates their base class in our engine:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Trigger Nodes:&lt;/strong&gt; These start the workflow. They have no incoming connections because they define the entry point. Examples include our WebhookTrigger or ScheduleTrigger.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Action Nodes:&lt;/strong&gt; These do the actual work. Think of them as your worker bees - sending an email, querying a database, or calling an external HTTP API.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Logic Nodes:&lt;/strong&gt; These are the brains. They allow you to branch execution (If/Switch), create loops, or merge data streams.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When you implement a new node in Vyshyvanka, you simply inherit from the appropriate base class, override the &lt;code&gt;ExecuteAsync&lt;/code&gt; method, and register it in the &lt;code&gt;NodeRegistry&lt;/code&gt;. The engine takes care of the rest.&lt;/p&gt;

&lt;h3&gt;
  
  
  Connections and Type Safety
&lt;/h3&gt;

&lt;p&gt;One of the most common sources of bugs in automation platforms is passing incompatible data between steps. We fixed that with a robust Port Type system.&lt;/p&gt;

&lt;p&gt;A connection is valid if:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  The source port type equals the target port type.&lt;/li&gt;
&lt;li&gt;  Either port is an 'Any' type, which acts as a wildcard.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Our supported types include &lt;strong&gt;Object, Array, String, Number, and Boolean&lt;/strong&gt;. By enforcing this at the designer level, we ensure that by the time a workflow hits the engine, it's guaranteed to be syntactically and logically sound.&lt;/p&gt;

&lt;h3&gt;
  
  
  Execution: The Immutable Record
&lt;/h3&gt;

&lt;p&gt;When a workflow runs, we create an Execution record. This is an immutable snapshot of that specific run. Why immutable? Because transparency matters. Once an execution is marked as 'Completed', 'Failed', or 'Cancelled', it is final. You can trace back exactly what data was passed to each node, what the result was, and how the state machine transitioned.&lt;/p&gt;

&lt;p&gt;This transition logic is rigid:&lt;br&gt;
Pending -&amp;gt; Running -&amp;gt; (Completed | Failed | Cancelled)&lt;/p&gt;

&lt;p&gt;Terminal states are final, and that is a promise the engine keeps. This prevents any accidental state mutations or 'zombie' executions that haunt other automation systems.&lt;/p&gt;

&lt;h3&gt;
  
  
  Validation Rules
&lt;/h3&gt;

&lt;p&gt;Before a workflow can even be executed, it must pass a battery of checks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  No circular dependencies.&lt;/li&gt;
&lt;li&gt;  Every node must be reachable from the trigger.&lt;/li&gt;
&lt;li&gt;  Required properties must be set.&lt;/li&gt;
&lt;li&gt;  All connections must satisfy port compatibility.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This pre-execution validation means that when you hit 'Run', you can be confident that the workflow isn't going to fail because of a simple wiring mistake.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why This Matters
&lt;/h3&gt;

&lt;p&gt;By building a strict, type-safe domain model, we've turned what is usually a fragile scripting process into a robust software engineering task. You aren't just "writing a script"; you are defining a formal model that the system understands and can verify.&lt;/p&gt;

&lt;p&gt;In the next part, we'll look at the front end of this equation: &lt;strong&gt;The Designer&lt;/strong&gt;. We'll explain how we built a Blazor WebAssembly canvas that allows you to drag, drop, and wire up these nodes in real time. Stay tuned!&lt;/p&gt;




&lt;p&gt;Check out the project source code here: &lt;a href="https://github.com/homolibere/Vyshyvanka" rel="noopener noreferrer"&gt;https://github.com/homolibere/Vyshyvanka&lt;/a&gt;&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>architecture</category>
      <category>csharp</category>
      <category>ddd</category>
    </item>
    <item>
      <title>Part 3: Architecture Deep-Dive - A Modular Approach with .NET 10</title>
      <dc:creator>Nick</dc:creator>
      <pubDate>Wed, 10 Jun 2026 08:00:00 +0000</pubDate>
      <link>https://dev.to/homolibere/part-3-architecture-deep-dive-a-modular-approach-with-net-10-33m7</link>
      <guid>https://dev.to/homolibere/part-3-architecture-deep-dive-a-modular-approach-with-net-10-33m7</guid>
      <description>&lt;p&gt;In the last part, we talked about &lt;em&gt;why&lt;/em&gt; we built Vyshyvanka. Today, we're going to talk about &lt;em&gt;how&lt;/em&gt;. Software architecture is often the difference between a project that scales and one that becomes a tangled web of technical debt after six months. For Vyshyvanka, we knew from day one that modularity wasn't an option - it was a requirement.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Dependency Rule
&lt;/h3&gt;

&lt;p&gt;If there's one rule we follow, it's the Dependency Rule: &lt;strong&gt;Dependencies flow strictly downward.&lt;/strong&gt; &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Vyshyvanka.Core&lt;/strong&gt; is our heartbeat. It's pure C#, containing only domain models, interfaces, and contracts. It has zero dependencies on anything else. This makes our core logic incredibly easy to test and reason about - if it compiles, it's logically sound.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Vyshyvanka.Engine&lt;/strong&gt; depends only on Core. It's where the magic happens: execution pipelines, persistence, and auth.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Vyshyvanka.Api&lt;/strong&gt; and &lt;strong&gt;Vyshyvanka.Designer&lt;/strong&gt; sit at the top. They consume Core and Engine to provide the REST endpoints and the interactive Blazor WebAssembly UI, respectively.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By ensuring our core domain logic doesn't know the API or UI even &lt;em&gt;exist&lt;/em&gt;, we've created a system where we can swap out a UI framework or change how an API handles serialization without touching the workflow engine itself.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Modular Structure
&lt;/h3&gt;

&lt;p&gt;Here is how we organized the solution to keep things clean:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;src/Vyshyvanka.Core/:&lt;/strong&gt; Domain layer. This is where our entities, interfaces, and enums live. If you want to know what a 'Node' or a 'Workflow' looks like at its simplest level, this is where you go.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;src/Vyshyvanka.Engine/:&lt;/strong&gt; This is the engine room. It houses the execution logic, persistence strategies (EF Core), and the plugin loading subsystem. It's robust, tested, and entirely infrastructure-agnostic.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;src/Vyshyvanka.Api/:&lt;/strong&gt; Our ASP.NET Core REST API. It handles authorization, request/response DTOs, and middleware. It acts as the gatekeeper, ensuring that every request is valid and authenticated before hitting the engine.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;src/Vyshyvanka.Designer/:&lt;/strong&gt; Our Blazor WebAssembly frontend. This is a complete SPA. It communicates with the backend exclusively through HTTP, keeping it decoupled from the engine's internal state.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Why This Matters for You
&lt;/h3&gt;

&lt;p&gt;Why go through the effort of splitting a project into 6+ distinct projects?&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Testability:&lt;/strong&gt; Because Core and Engine are so decoupled, we can write unit tests that execute complex workflows in milliseconds, without spinning up an API or a database.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Parallel Development:&lt;/strong&gt; A team could theoretically work on the UI components and the plugin engine at the same time without fighting over file changes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Resilience:&lt;/strong&gt; If the API layer has a bug in its serialization logic, it's contained to the API. It can't accidentally corrupt your workflow engine state.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Keeping it Clean
&lt;/h3&gt;

&lt;p&gt;We use &lt;code&gt;Directory.Packages.props&lt;/code&gt; to manage dependencies across the entire solution, which prevents version drift and keeps our build times fast. Furthermore, we enforce these boundaries with code reviews and clear documentation in our &lt;code&gt;structure.md&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Building a workflow engine is complex, but keeping the architecture simple is the key to maintaining it. By forcing dependencies downward, we've built a foundation that allows us to add new integrations, new triggers, and new logic nodes without ever fearing that we're breaking the core system.&lt;/p&gt;

&lt;p&gt;In the next part, we'll look at the &lt;strong&gt;Domain Model&lt;/strong&gt; - the actual building blocks of every workflow you'll ever create in Vyshyvanka. We'll break down what makes a Node a Node, and how we handle connections that span different execution states. Stay tuned!&lt;/p&gt;




&lt;p&gt;Check out the project source code here: &lt;a href="https://github.com/homolibere/Vyshyvanka" rel="noopener noreferrer"&gt;https://github.com/homolibere/Vyshyvanka&lt;/a&gt;&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>architecture</category>
      <category>csharp</category>
      <category>design</category>
    </item>
    <item>
      <title>Part 2: The Core Philosophy - Why Build Another Workflow Engine?</title>
      <dc:creator>Nick</dc:creator>
      <pubDate>Tue, 09 Jun 2026 08:00:00 +0000</pubDate>
      <link>https://dev.to/homolibere/part-2-the-core-philosophy-why-build-another-workflow-engine-4p6a</link>
      <guid>https://dev.to/homolibere/part-2-the-core-philosophy-why-build-another-workflow-engine-4p6a</guid>
      <description>&lt;p&gt;We live in a golden age of automation. From Zapier to n8n, there are incredible tools out there to help us glue our services together. So, why on earth am I building another one? &lt;/p&gt;

&lt;p&gt;It comes down to a few things: control, performance, and the .NET ecosystem.&lt;/p&gt;

&lt;h3&gt;
  
  
  The SaaS Bottleneck
&lt;/h3&gt;

&lt;p&gt;Most automation platforms are built as black-box SaaS products. They're fantastic for quick wins, but as you scale, you hit a wall. You lose visibility into how the engine processes your workflows, you're stuck with the vendor's plugin release cycle, and you're often limited by their infrastructure constraints. When your workflow fails, you're stuck looking at a generic "Execution Failed" log without being able to step into the code to see why.&lt;/p&gt;

&lt;h3&gt;
  
  
  The .NET Advantage
&lt;/h3&gt;

&lt;p&gt;Vyshyvanka is different. It's built for the .NET 10 developer who wants to own the entire stack. By building on modern .NET, we get access to features that other platforms struggle to match:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Performance:&lt;/strong&gt; .NET 10 brings incredible improvements in throughput and memory management. Vyshyvanka uses this to handle thousands of concurrent workflow executions without breaking a sweat.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Type Safety:&lt;/strong&gt; Workflow inputs and outputs are statically typed. No more guessing what the output of a "DatabaseQuery" node looks like—it's a C# object, fully documented by the compiler.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Tooling:&lt;/strong&gt; We're leveraging the .NET ecosystem's world-class tooling. You can write your custom nodes in your favorite IDE (Rider, VS Code, VS), run unit tests on them using xUnit, and deploy them with standard CI/CD pipelines.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Why Vyshyvanka?
&lt;/h3&gt;

&lt;p&gt;This isn't about reinventing the wheel - it's about giving developers a steering wheel they can actually modify. &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Own the Stack:&lt;/strong&gt; Run the engine inside your own infrastructure. You control the data, the security, and the scaling. No more worrying about data egress fees or black-box limitations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Customize Deeply:&lt;/strong&gt; Need a custom integration? You don't need to wait for a vendor to support it. Just write a new node class in C# and drop it into the plugin folder. It's registered, validated, and ready to go instantly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Developer Experience (DX):&lt;/strong&gt; We've prioritized the developer experience. From source-generated serializers to bUnit testing for Blazor components, every part of Vyshyvanka is built to make building workflows feel like building a standard .NET application.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is a platform for people who care about how their systems work and want the power of modern software engineering at their fingertips. &lt;/p&gt;

&lt;p&gt;In the next part, we'll dive deep into our modular architecture and explain how we structured the project to ensure that everything remains maintainable as it grows. Stay tuned!&lt;/p&gt;




&lt;p&gt;Check out the project source code here: &lt;a href="https://github.com/homolibere/Vyshyvanka" rel="noopener noreferrer"&gt;https://github.com/homolibere/Vyshyvanka&lt;/a&gt;&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>automation</category>
      <category>architecture</category>
      <category>csharp</category>
    </item>
    <item>
      <title>Part 1: Introduction to Vyshyvanka - Workflow Automation for the Modern .NET Era</title>
      <dc:creator>Nick</dc:creator>
      <pubDate>Mon, 08 Jun 2026 08:00:00 +0000</pubDate>
      <link>https://dev.to/homolibere/part-1-introduction-to-vyshyvanka-workflow-automation-for-the-modern-net-era-268k</link>
      <guid>https://dev.to/homolibere/part-1-introduction-to-vyshyvanka-workflow-automation-for-the-modern-net-era-268k</guid>
      <description>&lt;p&gt;Hey friends! If you 've spent any time working in the .NET ecosystem, you know the feeling: you've got a dozen different background tasks, scheduled jobs, and webhook listeners, each living in their own silo. Managing them all gets messy fast.&lt;/p&gt;

&lt;p&gt;That's why I'm excited to introduce you to &lt;strong&gt;&lt;a href="https://github.com/homolibere/Vyshyvanka" rel="noopener noreferrer"&gt;Vyshyvanka&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Vyshyvanka is a workflow automation platform built from the ground up for .NET 10. Instead of writing boilerplate code every time you need to connect two services, you build workflows visually in our Blazor WebAssembly designer. Then, the engine handles the execution, state management, and persistence for you.&lt;/p&gt;

&lt;p&gt;In this 15-part series, we're going to pull back the curtain on the entire architecture, from the execution engine internals to our plugin system that lets you build custom nodes for whatever infrastructure you're running.&lt;/p&gt;

&lt;p&gt;Whether you're a developer looking to streamline your internal tools or an enthusiast wanting to see how a modern .NET platform is constructed, there's something here for you.&lt;/p&gt;

&lt;p&gt;Let's get building!&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>automation</category>
      <category>blazor</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Treat Your AI Agent Is a Mid-Level Developer With Amnesia</title>
      <dc:creator>Nick</dc:creator>
      <pubDate>Fri, 05 Jun 2026 07:00:00 +0000</pubDate>
      <link>https://dev.to/homolibere/treat-your-ai-agent-is-a-mid-level-developer-with-amnesia-7hd</link>
      <guid>https://dev.to/homolibere/treat-your-ai-agent-is-a-mid-level-developer-with-amnesia-7hd</guid>
      <description>&lt;p&gt;In the previous articles, I argued that AI is just the next abstraction layer, and that the key skill it demands is clear technical communication — the same skill that makes great tech leads and architects. But that’s still abstract. Let me make it concrete.&lt;/p&gt;

&lt;p&gt;Because here’s the thing: we already have a well-established workflow for communicating technical intent to other humans. And AI agent workflows are not just similar to it — they’re a direct mirror.&lt;/p&gt;

&lt;h2&gt;
  
  
  This Is Not a Coincidence
&lt;/h2&gt;

&lt;p&gt;We, humans, do not do this because of AI. We designed AI so it can understand our usual steps. The agent workflow mirrors the human team workflow because that’s what we trained it on. That’s what we optimized it for.&lt;/p&gt;

&lt;p&gt;Let me show you what I mean.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Human Team Workflow
&lt;/h2&gt;

&lt;p&gt;How do we actually do things in a position of, say, a Tech Lead or Team Lead? Let’s trace the typical workflow:&lt;/p&gt;

&lt;p&gt;Step 1: Grooming a business request. Who is involved? Product owner, a lead (tech lead or team lead), probably a developer who will be designated to implement this thing or a knowledge keeper for specific functionality. Those people basically create a design document without implementation specifics — a high-level description of what needs to be done and why.&lt;/p&gt;

&lt;p&gt;Step 2: Technical decomposition. The lead and a developer break the design into technical tasks — what components to change, what APIs to create, what data models to adjust.&lt;/p&gt;

&lt;p&gt;Step 3: Task scheduling. A project manager organizes the work into sprints, sets priorities, estimates timelines.&lt;/p&gt;

&lt;p&gt;Step 4: Code review. After development iterations, another developer or the lead examines changes. At this point, the examiner does not implement anything directly — they only look at the final result and evaluate it. Is it good? What needs to be changed? Does it match the original intent?&lt;/p&gt;

&lt;h2&gt;
  
  
  The AI Agent Workflow
&lt;/h2&gt;

&lt;p&gt;Now look at how modern AI coding tools work:&lt;/p&gt;

&lt;p&gt;Step 1: Design document. You describe what you want — the context, the goal, the constraints. This is your “grooming” phase. You’re the product owner and the tech lead in one person.&lt;/p&gt;

&lt;p&gt;Step 2: Planning/decomposition. The AI (or you, together with AI) breaks the task into steps — what files to change, what to create, in what order.&lt;/p&gt;

&lt;p&gt;Step 3: Execution. The AI works through the plan, implementing step by step.&lt;/p&gt;

&lt;p&gt;Step 4: Review. You look at the result. You don’t implement — you evaluate. Does it match your intent? What needs to change?&lt;/p&gt;

&lt;p&gt;Same workflow. Same roles. Same sequence. The only difference is that steps 2 and 3 are now done by an AI instead of a human developer.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Amnesia Problem
&lt;/h2&gt;

&lt;p&gt;But there’s one critical difference between a human teammate and an AI agent: memory.&lt;/p&gt;

&lt;p&gt;A human developer on your team remembers your project. They know the architecture. They remember that weird edge case from three months ago. They know that “the payments service” actually means two services because of that legacy split nobody documented.&lt;/p&gt;

&lt;p&gt;An AI agent knows nothing. Every conversation starts from zero. It’s a competent developer — but one with complete amnesia about your specific project.&lt;/p&gt;

&lt;p&gt;This changes everything about how you should communicate with it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;They’re competent but don’t remember your project specifics&lt;/li&gt;
&lt;li&gt;They need clear context for each task&lt;/li&gt;
&lt;li&gt;They can do great work if you explain what you want precisely&lt;/li&gt;
&lt;li&gt;They will make assumptions if you leave gaps — and those assumptions might be wrong&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The RAG Trap
&lt;/h2&gt;

&lt;p&gt;So here’s where many teams go wrong. They think: “If the problem is amnesia, let’s give it all the memory. RAG, vector databases, persistent context, the entire codebase indexed and searchable.”&lt;/p&gt;

&lt;p&gt;And yes — it helps. But it has the same drawbacks as increasing the context window: more information means more potential confusion. Greater knowledge — greater sorrow.&lt;/p&gt;

&lt;p&gt;You can give an AI agent access to your entire codebase, all your documentation, all your Jira tickets. But if your prompt is vague and poorly structured — it will hallucinate confidently in ten different directions. More context without better communication is just more noise.&lt;/p&gt;

&lt;p&gt;Think about it from the human analogy. If you dump a 500-page wiki on a new developer and say “figure it out” — they won’t. They’ll drown. But if you give them a clear task description, point them to the three relevant files, and explain the constraints — they’ll deliver.&lt;/p&gt;

&lt;p&gt;Same with AI.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Practical Framework
&lt;/h2&gt;

&lt;p&gt;Here’s what actually works. Start treating your AI interactions like you’re onboarding a new team member for every task:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Context first. Before the task, provide the relevant context. Not everything — just what’s needed. Architecture of the specific component. The relevant data models. The constraints that aren’t obvious from the code.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Clear intent. State what you want to achieve, not just what you want to change. “Add a retry mechanism to the payment service because transactions sometimes fail due to network timeouts, and we need at most 3 retries with exponential backoff” is infinitely better than “add retries to payments.”&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Constraints explicitly. What should it NOT do? What patterns should it follow? What’s the testing strategy? Don’t assume the AI will infer these from context — it might, but it might not.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Review like a tech lead. When you review the output, don’t just check if it works. Check if it matches your intent. Check if it follows your patterns. Check if it handles the edge cases you care about. This is exactly what you’d do in a code review with a human developer.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Store the Repeating, Vary the Specific
&lt;/h2&gt;

&lt;p&gt;One more practical tip. Start storing common things in markdown files — steering files, documentation, architecture decisions. That way you retain only repeating information as persistent context, and variables — the specifics of each task — will be added as you explain your current goal.&lt;/p&gt;

&lt;p&gt;This is not different from onboarding a new team member. You give them docs, you give them context, and then you give them a task. The better your docs and the clearer your task description — the better the result.&lt;/p&gt;

&lt;p&gt;The things that don’t change between tasks — your coding standards, your architecture patterns, your testing philosophy — those go into persistent documentation. The things that are unique to each task — the specific requirement, the specific constraints, the specific acceptance criteria — those you communicate fresh each time.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Developer of the Future
&lt;/h2&gt;

&lt;p&gt;In the end, I want to emphasize this: developers of the future will need not only knowledge of how to develop, but also a skill in explaining what exactly they want from an AI agent.&lt;/p&gt;

&lt;p&gt;The irony is beautiful. We spent decades building abstraction layers to avoid explaining things to machines in their language. And now the machines finally learned our language. The developers who will thrive are not the ones who memorize every API — they’re the ones who can explain what they need as clearly as a good tech lead explains a task to their team.&lt;/p&gt;

&lt;p&gt;This way you really unlock the full potential of AI agent as a tool in your hands. Not by fighting it. Not by fearing it. By talking to it the way you’d talk to a skilled colleague who just joined your team today.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>The Bosses Are Coding Again. Here’s Why That Should Worry You</title>
      <dc:creator>Nick</dc:creator>
      <pubDate>Thu, 04 Jun 2026 07:00:00 +0000</pubDate>
      <link>https://dev.to/homolibere/the-bosses-are-coding-again-heres-why-that-should-worry-you-3lpa</link>
      <guid>https://dev.to/homolibere/the-bosses-are-coding-again-heres-why-that-should-worry-you-3lpa</guid>
      <description>&lt;p&gt;In my previous article, I argued that AI is just the next abstraction layer — the same pattern we’ve seen a dozen times in software history. Each layer demands a new skill. So what does the AI layer demand?&lt;/p&gt;

&lt;p&gt;I think the answer is hiding in plain sight. And some very powerful people just demonstrated it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Something Interesting Happened Recently
&lt;/h2&gt;

&lt;p&gt;Mark Zuckerberg started coding again after a 20-year break. According to multiple reports, he moved his desk to Meta’s AI lab, spends 5 to 10 hours a week writing code, and is “coding all day long” alongside the Meta Superintelligence Labs team. The man who built Facebook in a dorm room and then spent two decades managing tens of thousands of people — is shipping diffs again.&lt;/p&gt;

&lt;p&gt;Garry Tan, CEO of Y Combinator, returned to coding after 15 years using AI tools like Claude Code. He described himself as “addicted” to it, sleeping four hours a night because he couldn’t stop building things.&lt;/p&gt;

&lt;p&gt;Sergey Brin, Google’s co-founder who stepped back from day-to-day operations years ago, came out of retirement to code on Gemini. He’s reportedly assembling an elite “coding strike team” and is directly involved in hands-on development.&lt;/p&gt;

&lt;p&gt;And there’s a quote from The New Stack that captures this perfectly: executives are building with AI because they were “tired of explaining it to somebody who was supposed to build it for me.”&lt;/p&gt;

&lt;p&gt;Why is this happening? These people haven’t written production code in over a decade. What changed?&lt;/p&gt;

&lt;h2&gt;
  
  
  The Career Ladder Was Always About Communication
&lt;/h2&gt;

&lt;p&gt;Let’s take a step back. The most common career paths for a developer are either the strict technical way — from developer to tech lead, then architect — or the management way — team lead, then head of engineering, CTO.&lt;/p&gt;

&lt;p&gt;In both ways you start from doing things yourself and gradually move to teaching — or better to say, guiding — others how to do it. Or strictly overseeing the whole process. You stop writing code and start writing explanations. You stop implementing and start reviewing. You stop building and start describing what needs to be built.&lt;/p&gt;

&lt;p&gt;A senior architect doesn’t write code. They write ADRs, design documents, system diagrams. They explain to teams what to build and why. A CTO doesn’t push commits. They articulate technical vision so clearly that hundreds of engineers can execute on it independently.&lt;/p&gt;

&lt;p&gt;The higher you go — the more your job becomes pure communication of technical intent.&lt;/p&gt;

&lt;p&gt;Sound familiar?&lt;/p&gt;

&lt;h2&gt;
  
  
  My Theory: The Explanation Bottleneck Is Gone
&lt;/h2&gt;

&lt;p&gt;My theory is simple. People who know how to explain to others what and how they exactly want to do — can now skip the extra step of explanation and do things directly. Faster. Without the telephone game of requirements passing through three layers of interpretation.&lt;/p&gt;

&lt;p&gt;Think about it. A CTO who spent years learning to articulate technical vision clearly, who mastered the art of describing systems and behaviors — that person now has a tool that actually understands those descriptions and turns them into working code. The skill they developed for managing people turned out to be the exact skill needed for managing AI.&lt;/p&gt;

&lt;p&gt;The bottleneck was never their inability to code. It was the cost of translating their vision through other people. AI removed that cost.&lt;/p&gt;

&lt;p&gt;That’s why Zuckerberg is coding again. Not because he suddenly remembered C++ syntax. But because he spent 20 years perfecting the skill of explaining what he wants — and now there’s a tool that listens.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Should Concern Mid-Level Developers
&lt;/h2&gt;

&lt;p&gt;Here’s the uncomfortable part. If the key skill for working with AI is the ability to articulate technical intent clearly and precisely — then the people who are best at it are not junior developers. They’re not even senior developers who spent their careers heads-down in code.&lt;/p&gt;

&lt;p&gt;They’re the people who spent years learning to communicate technical ideas to other humans. Tech leads. Architects. Engineering managers. The people who already climbed the ladder.&lt;/p&gt;

&lt;p&gt;This doesn’t mean mid-level developers are doomed. It means the skill you need to develop is not “learn more frameworks” or “memorize more APIs.” It’s the skill of clear technical communication. The ability to describe what you want, why you want it, what constraints exist, and what “done” looks like.&lt;/p&gt;

&lt;p&gt;This is not “prompt engineering” as some buzzword. This is the same skill that makes great tech leads, great architects, great CTOs — the ability to articulate technical intent clearly and precisely.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Skill That Compounds
&lt;/h2&gt;

&lt;p&gt;Here’s the good news. Unlike memorizing API signatures or framework quirks — communication skill compounds. It doesn’t deprecate with the next major version release. It doesn’t become obsolete when a new framework appears.&lt;/p&gt;

&lt;p&gt;A developer who can clearly explain a system’s architecture, articulate trade-offs, and describe desired behavior precisely — that developer will be effective with whatever AI tool comes next. Because the tools will only get better at understanding us. The bottleneck will always be our ability to express what we mean.&lt;/p&gt;

&lt;h2&gt;
  
  
  What This Means Practically
&lt;/h2&gt;

&lt;p&gt;If you’re a developer reading this and thinking “okay, but what do I actually do?” — I have a concrete answer for that. In the next article, I’ll break down exactly how AI agent workflows mirror the workflows we already use in human teams, and how you can leverage that understanding to work with AI dramatically more effectively.&lt;/p&gt;

&lt;p&gt;Spoiler: if you’ve ever written a good Jira ticket, you’re already halfway there.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>leadership</category>
      <category>career</category>
    </item>
    <item>
      <title>AI Is Not Killing Developers. It’s Doing What We Always Did to Ourselves</title>
      <dc:creator>Nick</dc:creator>
      <pubDate>Wed, 03 Jun 2026 16:31:26 +0000</pubDate>
      <link>https://dev.to/homolibere/ai-is-not-killing-developers-its-doing-what-we-always-did-to-ourselves-bm2</link>
      <guid>https://dev.to/homolibere/ai-is-not-killing-developers-its-doing-what-we-always-did-to-ourselves-bm2</guid>
      <description>&lt;p&gt;AI is considered as a threat for developers. Not existential — nobody thinks Skynet is coming for your VS Code. But as a “tool” that makes developers less knowledgeable about the tricks of software development. Less aware of how this or that function works under the hood. Less capable of choosing the right approach in a particular case.&lt;/p&gt;

&lt;p&gt;And honestly? I’ve heard this song before.&lt;/p&gt;

&lt;h2&gt;
  
  
  We’ve Been Here Before. Many Times.
&lt;/h2&gt;

&lt;p&gt;If you look at the path of programming languages evolution — you’ll see the same pattern repeating over and over. We moved from writing raw machine code to assembler. From assembler to procedural programming. From procedural to OOP. Then came additional layers of abstraction — frameworks, libraries, package managers. Each layer made the previous one less visible, less understood by the average developer.&lt;/p&gt;

&lt;p&gt;And now so few people can debug real assembler code. And what? Is this a failure?&lt;/p&gt;

&lt;p&gt;Really, I do not know how a &lt;code&gt;for&lt;/code&gt; loop works on the assembler level — I’m a child of high abstraction level languages. Java, C#, Python — they really discourage you from knowing how it works under the hood. Just build and ship. Use good practices and you will be golden. Nobody asks you to understand memory allocation to write a REST API. Nobody expects you to know CPU pipeline optimization to build a mobile app.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Pattern Is Always the Same
&lt;/h2&gt;

&lt;p&gt;Every generation of developers lost something and gained something.&lt;/p&gt;

&lt;p&gt;We lost the ability to write optimal assembler — we gained the ability to ship products in weeks instead of years. We lost manual memory management — we gained garbage collectors and the ability to focus on business logic. We lost hand-crafted SQL queries — we gained ORMs and the ability to switch databases without rewriting half the codebase.&lt;/p&gt;

&lt;p&gt;Each time, the old guard said: “These new developers don’t understand the fundamentals.” Each time, the new generation built things the old guard couldn’t have imagined — because they weren’t spending their cognitive budget on problems that were already solved.&lt;/p&gt;

&lt;h2&gt;
  
  
  So What Did We Actually Lose?
&lt;/h2&gt;

&lt;p&gt;Let’s be honest about what each abstraction layer took from us:&lt;/p&gt;

&lt;p&gt;Assembler -&amp;gt; C: We lost fine-grained control over registers and memory layout. We gained portability and readability.&lt;/p&gt;

&lt;p&gt;C -&amp;gt; Java/C#: We lost manual memory management and pointer arithmetic. We gained safety, productivity, and massive ecosystems.&lt;/p&gt;

&lt;p&gt;Raw code -&amp;gt; Frameworks: We lost understanding of HTTP parsing, socket management, thread pools. We gained the ability to build a working web app in a day.&lt;/p&gt;

&lt;p&gt;Manual deployment -&amp;gt; CI/CD: We lost the ritual of careful manual releases. We gained the ability to deploy 50 times a day with confidence.&lt;/p&gt;

&lt;p&gt;Notice something? Each “loss” was actually a deliberate trade. We chose to forget things that were no longer worth remembering at the level of daily work. Not because they were unimportant — but because someone already solved them well enough.&lt;/p&gt;

&lt;h2&gt;
  
  
  AI Is Just the Next Row in This Table
&lt;/h2&gt;

&lt;p&gt;AI is not doing anything new to us. It’s doing what we always did to ourselves — adding another abstraction layer. The difference is that this layer abstracts not just technical complexity, but the act of writing code itself.&lt;/p&gt;

&lt;p&gt;And yes, that feels different. More personal. More threatening. Because previous abstractions still required you to type code — just at a higher level. AI sometimes removes even that step.&lt;/p&gt;

&lt;p&gt;But the trade is the same: you lose something (the muscle memory of writing every line yourself), you gain something (the ability to build and iterate at a speed that was previously impossible for a single person).&lt;/p&gt;

&lt;h2&gt;
  
  
  The Question That Actually Matters
&lt;/h2&gt;

&lt;p&gt;The question is not whether AI makes us “worse” developers. Every abstraction layer made us “worse” at the layer below. That’s the point.&lt;/p&gt;

&lt;p&gt;The real question is: what new skill does this layer demand from us?&lt;/p&gt;

&lt;p&gt;When we moved from assembler to C, the new skill was structured thinking and algorithm design. When we moved to OOP, it was system design and architecture. When frameworks arrived, it was choosing the right tool and understanding trade-offs.&lt;/p&gt;

&lt;p&gt;What does the AI layer demand? I have a theory about that — but that’s a topic for the next article.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>softwaredevelopment</category>
      <category>programming</category>
      <category>career</category>
    </item>
    <item>
      <title>POCking AI</title>
      <dc:creator>Nick</dc:creator>
      <pubDate>Tue, 25 Nov 2025 10:37:15 +0000</pubDate>
      <link>https://dev.to/homolibere/pocking-ai-59e0</link>
      <guid>https://dev.to/homolibere/pocking-ai-59e0</guid>
      <description>&lt;p&gt;This article reflects my personal experience over 8 months of intensive AI-assisted development across multiple projects. Your mileage may vary, but the principles should translate across languages and frameworks.&lt;/p&gt;

&lt;p&gt;I was presenting this topic at Moldova DevCon 2025 and i want to share the whole story.&lt;/p&gt;

&lt;p&gt;This is my journey in pair software development with AI. This is not an advertisement or promo for any of products and any services i'm going to talk about. This is a personal opinion. I'll talk about some challenges and gains that i've encountered during these coding sessions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I Started This Journey?
&lt;/h2&gt;

&lt;p&gt;Eight months ago, I was that developer who rolled his eyes at AI coding tools. "Just fancy autocomplete," I thought. "Real developers don't need assistance." Basically, I didn't want to pay for stuff that I don't believe will be useful for me. Then we just decided to give it a try. We have PCI DSS certification, so we can't just go with any AI. We need something more or less self hosted. Nobody wants to support this kind of infrastructure, so we decided to give a go to Amazon Q Developer. At that time I had an overwhelming amount of reviews and more coming.&lt;/p&gt;

&lt;p&gt;More out of desperation than curiosity, I installed an Amazon Q Developer. Not because I believed in it, but because I was running out of time and options. What happened next wasn't magical or revolutionary - it was just... as i expected. Reviews of changes without understanding of context or domain give no result. Almost all reviews was "Looks good for me". But the ability of AI assistant to use glab console tool to get changes and that assistant understand how to use it by it owh - was impressive at least. That brought me to research or better to say learn how to use it properly. Small things at first. The AI would suggest a method name that was better than what I was about to type. It would catch a null reference before I did. It would generate test cases I hadn't thought of.&lt;/p&gt;

&lt;p&gt;But the real moment came when I get Kiro from Amazon into my hands. Spec flow approach made me excited as 8 year old on birthday. I would describe a problem, and the AI would suggest an approach. It would create a design document then I would refine the idea, and it would design technical documentation. We were actually having a conversation about code. Then, when everything looks fine - generate a step-by-step implementation list. That's when I knew something fundamental had changed in how software gets built.&lt;/p&gt;

&lt;p&gt;This is not about AI replacing developers. This is about developers and AI working together in ways that make both more effective. And yes, there are pitfalls, frustrations, and moments when you want to throw your laptop out the window. But there are also moments of genuine partnership that I never expected to experience with code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fail Fast
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Fail fast, also sometimes termed fail often or fail cheap, is a business management concept and theory of organizational psychology that argues businesses should encourage employees to use a trial-and-error process to quickly determine and assess the long-term viability of a product or strategy and move on, cutting losses rather than continuing to invest in a doomed approach. - Wikipedia.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This concept also known to developers. We try to quit from function execution as soon as result is obvious. At grooming or decomposition stage, when we just thinking how it should work and do not have practical experience, to estimate outcome of new architecture or functionality. Fail fast approach can help iterate quickly and test theories before we invest huge amount of time into it. At this point AI code assistants, especially agents with support of tools, can provide huge help.&lt;/p&gt;

&lt;p&gt;Testing a theory that we have in our mind can be quick and dirty. We do not need a clean code or onion layered architecture to get the proof of concept. We need to have it working and we need it now.&lt;/p&gt;

&lt;p&gt;Recently, I wanted to test if a new caching strategy would improve our API response times. Instead of spending days implementing a full solution, I asked the AI agent to create a quick prototype with cache integration. In 30 minutes, I had a working proof of concept that clearly showed the performance benefits. This validated the approach before committing to a full implementation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fry62q4iueg5hebjzih3n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fry62q4iueg5hebjzih3n.png" alt="Generated by Nano Banana" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Fear Of A Blank Page
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;The fear of a blank page is a common experience for many writers and artists. This anxiety often stems from the pressure to create something perfect right from the start. It can lead to procrastination and self-doubt, making it difficult to begin any creative work.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I have one. I often thinks about new pet project and what i should do and then... i just do not want to write huge amount of code just to get started. This is especially true for web projects or some kind of API hosts. I need to configure logging the way i like, metrics, health checks, authorization and authentication etc. This process makes me already tired and i don't want to do it. I've tried to use some templates, but languages and frameworks changes so fast, that this approach is not viable for me either.&lt;/p&gt;

&lt;p&gt;AI code agents can help overcome this fear or better to say, in my case, procrastination. They can create starter project with all i like and require faster. Meanwhile i'll keep thinking how i want tho do main logic of application.&lt;/p&gt;

&lt;p&gt;What used to take me 2-3 hours of boilerplate setup now takes 15-20 minutes. The AI can scaffold a complete web project with structured logging, health checks endpoints, authentication setup, containerization configuration, and basic database operations. While the AI handles the infrastructure, I can focus on the business logic that actually matters.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to Choose?
&lt;/h2&gt;

&lt;p&gt;There is a few options right now. IDE integrated agents or separate console agents. In terms of what to choose - matter of taste. I've used both and i prefer IDE integrated, because i can see what they are doing and review code during generation. While AI generating one part of project - i can review another.&lt;/p&gt;

&lt;p&gt;It's hard to find IDE that does not have AI integration of some kind. &lt;/p&gt;

&lt;p&gt;My favorite is Amazon Kiro and VS Code Copilot. I have separate window with ongoing monolog of AI agent, a window with files showing diff to me. If i spotted that things goes wrong - i can stop process, correct my prompt and restart earlier.&lt;/p&gt;

&lt;p&gt;I found that, it is a much better experience, if you use versioning tools with your project. First of all this gives you peace of mind when code generation running. You will not loose anything that was pushed to repo. Second, you can revert safely and fully then iterate again.&lt;/p&gt;

&lt;p&gt;Always work with a clean git state before starting AI sessions. Commit your working changes first.&lt;/p&gt;

&lt;h2&gt;
  
  
  Context
&lt;/h2&gt;

&lt;p&gt;This is fight between give as much as possible and receive what you expected. When you give all you have to LLM - it has to much of freedom and does not understand how to deliver what you want. This is usual problem between software developers and product owners and/or business owner. People can not understand each other and we think that our train of thoughts can be processed be LLM as we expecting. This not a case so far. Maybe in future we will get AGI and it will do even better that promt it has, but not now. Not yet. We still need to give some context and set some boundaries.&lt;/p&gt;

&lt;p&gt;On my experience, big code base is too much for an agent for now. I can not get meaningful results from LLM on one of our monorepo project with 250K+ LOC, with custom nuget packages and dependencies. It's out of agent capabilities. But when i start a new project from scratch - i constantly get an acceptable results. Yes, i still have to polish it, but it is usable.&lt;/p&gt;

&lt;p&gt;So, modern AI agents support rule sets to provide some constrains. This is very useful to keep DRY principle.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Context Strategy:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Small focused sessions&lt;/strong&gt;: Work on one feature/component at a time&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Clear boundaries&lt;/strong&gt;: Define what files/folders the AI should focus on&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Progressive disclosure&lt;/strong&gt;: Start with high-level architecture, then drill down&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rule sets help&lt;/strong&gt;: Use modern language features, follow established patterns, include documentation, use validation libraries, follow company naming conventions&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  When AI Gets It Wrong: Debugging Together
&lt;/h2&gt;

&lt;p&gt;AI isn't perfect. It makes mistakes, especially with complex business logic, framework-specific edge cases, security considerations, and performance optimizations.&lt;/p&gt;

&lt;p&gt;AI generated a database query that looked correct but had performance issues. It was fetching data in a loop instead of using proper joins, creating multiple database calls when one would suffice. After explaining the performance problem, AI corrected the approach to use a single optimized query with proper relationships included.&lt;/p&gt;

&lt;p&gt;This is how i usually approach debugging with AI:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Describe the unexpected behavior clearly&lt;/li&gt;
&lt;li&gt;Share error messages and stack traces&lt;/li&gt;
&lt;li&gt;Explain the expected vs. actual behavior&lt;/li&gt;
&lt;li&gt;Let AI propose solutions, then review together&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;And remember, AI does not learn new things. All changes that are made during debug session should be stored somewhere. It can be a separate md file that next time you need to add to context or much better way - update general documentation about project.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv9owbs7opgosbomjqwmf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv9owbs7opgosbomjqwmf.png" alt="Generated by Nano Banana" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Code Quality and Maintainability
&lt;/h2&gt;

&lt;p&gt;AI a very good at following rules, especially those related to code quality and maintainability. Specifying rules and guidelines helps AI generate code that is consistent, readable, and maintainable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What AI does well&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Consistent naming conventions. Following naming conventions helps to improve code readability and maintainability and agents usually do this by themselves. They trained on large codebases and many projects follows same conventions.&lt;/li&gt;
&lt;li&gt;Proper exception handling patterns. But sometimes they over-engineer solutions, leading to unnecessary complexity. Creating custom exception even if it's not necessary. This can lead to confusion and unnecessary complexity.&lt;/li&gt;
&lt;li&gt;Comprehensive documentation. AI does great at writing documentation on existing code, especially if this code was written by AI during the same session.&lt;/li&gt;
&lt;li&gt;Following established patterns in the codebase. If you provide examples how to do it, AI will follow them religiously. This is very useful if you have some specific preferences in file location, tests arrangement and other rules.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What needs human review&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Business logic correctness. This is an "Achilles' heel" of AI-generated code. While AI can generate code that follows patterns and conventions, it may not always understand the business logic correctly.&lt;/li&gt;
&lt;li&gt;Security implications. Generated code may not always be secure. Some times it may be vulnerable to common attacks.&lt;/li&gt;
&lt;li&gt;Performance considerations. Nova days many developers does not think about performance optimization. It's easier to add more memory and CPU resources than to optimize code. LLM's usually have a hard time with this also.&lt;/li&gt;
&lt;li&gt;Architectural decisions. Decide when to use what pattern or how to structure dependencies is a very hard task for AI. It's better to let humans make these decisions.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Testing and AI: A Complex Relationship
&lt;/h2&gt;

&lt;p&gt;AI excels at generating unit tests but struggles with integration testing nuances. Mocking classes and services is no brainer for AI, but integration testing requires understanding of the system's architecture and dependencies. AI can help with setting up mocks and stubs, but it's important to ensure that the tests cover all possible scenarios and edge cases.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Strengths:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Comprehensive test coverage for public methods&lt;/li&gt;
&lt;li&gt;Edge case identification&lt;/li&gt;
&lt;li&gt;Mock setup and teardown&lt;/li&gt;
&lt;li&gt;Test data generation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;AI generated test follows standard patterns with proper arrangement, action, and assertion phases. It includes validation for edge cases like invalid inputs and expected exception handling. The test structure is clean and follows naming conventions consistently.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Limitations:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Doesn't understand complex business workflows&lt;/li&gt;
&lt;li&gt;Struggles with database integration tests&lt;/li&gt;
&lt;li&gt;Can't validate UI behavior&lt;/li&gt;
&lt;li&gt;integration tests are difficult for AI to generate&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Economics of AI Development
&lt;/h2&gt;

&lt;p&gt;Let's talk about the elephant in the room: the cost. When you hear "AI," you might think of massive, expensive infrastructure projects. The reality of AI-assisted development is far more down-to-earth. For an individual developer or a small team, the monthly subscription for a top-tier AI assistant is often less than the cost of a few hours of a developer's time. When you factor in the productivity gains—less time spent on boilerplate, faster debugging, and quicker prototyping—the return on investment becomes obvious very quickly. It's not an extravagant expense; it's a force multiplier.&lt;/p&gt;

&lt;p&gt;However, the real cost isn't always measured in money. The biggest concern, and a valid one, is data security. Sending your proprietary source code to a third-party server is a non-starter for any organization with serious security or compliance obligations, like the PCI DSS certification we have. This is where you need to be selective. You can't just use any free or consumer-grade tool. The solution is to opt for enterprise-grade services like GitHub Copilot for Business or Amazon Q Developer, which offer commitments around data privacy, ensuring your code isn't used for training their public models and is kept confidential. The cost is slightly higher, but it's the necessary price for secure and responsible AI adoption.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fstsiw94xl7w5ee5vj9ns.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fstsiw94xl7w5ee5vj9ns.png" alt="Generated by Nano Banana" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Team Dynamics: Introducing AI to Your Development Process
&lt;/h2&gt;

&lt;p&gt;Bringing an AI assistant into a development team isn't just a technical change; it's a cultural one. You can't just send out a memo and expect everyone to embrace it. Here’s how to do it right:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Start with a Pilot Group&lt;/strong&gt;: Find a few enthusiastic developers who are open to experimenting. Let them be the pioneers. Their successes and learnings will become the best internal marketing you could ask for.&lt;br&gt;
&lt;strong&gt;Establish Clear Guidelines&lt;/strong&gt;: Don't let it be a free-for-all. Create a simple "How-To" guide: how to write effective prompts, what tasks it's best for (and what it's bad at), and most importantly, what not to share with the AI (e.g., API keys, customer data, security vulnerabilities).&lt;br&gt;
&lt;strong&gt;Reframe it as Pair Programming&lt;/strong&gt;: Encourage the team to think of the AI as a junior pair programmer. It’s fast and knows the syntax, but it lacks domain context and needs senior oversight. This framing helps manage expectations and reduces the fear of replacement.&lt;br&gt;
&lt;strong&gt;Evolve Your Code Reviews&lt;/strong&gt;: AI-generated code still needs human review. The focus of code reviews will shift from catching syntax errors to validating business logic, architectural alignment, and potential security holes. The question is no longer "Does it work?" but "Is it the right solution?"&lt;br&gt;
&lt;strong&gt;Create a Feedback Loop&lt;/strong&gt;: Set up a dedicated chat channel where team members can share wins, funny failures, and effective prompts. This builds a collaborative learning culture and helps everyone get better at using the tool together.&lt;br&gt;
&lt;strong&gt;Address the fear head-on&lt;/strong&gt;: AI isn't here to take jobs, it's here to take away the tedious, repetitive parts of the job, freeing up developers to focus on the complex, creative problem-solving that humans do best.&lt;/p&gt;

&lt;h2&gt;
  
  
  Actionable Takeaways
&lt;/h2&gt;

&lt;p&gt;If you're going to start your own journey with an AI coding partner, here are the key takeaways from my experience:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Start with a Clean Slate&lt;/strong&gt;: Always commit your changes to version control before starting a session with an AI agent. It's your ultimate undo button and safety net.&lt;br&gt;
&lt;strong&gt;Be the Architect, Let AI Be the Builder&lt;/strong&gt;: You define the "what" and the "why." Let the AI handle the "how" for the boilerplate and routine code. Your primary role is to provide direction and review the output.&lt;br&gt;
&lt;strong&gt;Master the Art of Context&lt;/strong&gt;: Don't dump your entire codebase on the AI. Give it focused, relevant information for the task at hand. Treat it like a junior developer you're onboarding to a specific feature.&lt;br&gt;
&lt;strong&gt;Trust No One&lt;/strong&gt;: AI makes mistakes. Your expertise is the final quality gate. Review every significant piece of generated code for correctness, security, and performance.&lt;br&gt;
&lt;strong&gt;Use AI to "Fail Faster"&lt;/strong&gt;: Got a new idea? Use the AI to build a quick and dirty proof-of-concept. You can validate an approach in minutes or hours, not days.&lt;br&gt;
&lt;strong&gt;Document Your Debugging Sessions&lt;/strong&gt;: When you and the AI solve a problem, update your project's documentation. This becomes part of the context for future sessions, effectively "teaching" the AI your project's nuances.&lt;br&gt;
&lt;strong&gt;Introduce It Collaboratively&lt;/strong&gt;: When bringing AI to your team, do it thoughtfully. Start small, provide guidelines, and foster a culture of shared learning.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;AI in software development isn't about replacement—it's about amplification. It amplifies your productivity, your ability to explore ideas quickly, and your capacity to handle complex projects. But it also amplifies your mistakes if you don't maintain critical thinking and code review discipline.&lt;/p&gt;

&lt;p&gt;The future belongs to developers who can effectively collaborate with AI while maintaining their core engineering skills. Start experimenting now, but remember: the goal isn't to write less code, it's to solve more problems.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Final Advice:&lt;/strong&gt; Treat AI as the best junior developer you've ever worked with — extremely capable, incredibly fast and with amnesia. Needs guidance, context, and oversight. With that mindset, you'll build a productive partnership that enhances rather than replaces your capabilities.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
    </item>
    <item>
      <title>The Dangling If-Else Problem: A Common Pitfall in Programming</title>
      <dc:creator>Nick</dc:creator>
      <pubDate>Fri, 15 Mar 2024 09:17:02 +0000</pubDate>
      <link>https://dev.to/homolibere/the-dangling-if-else-problem-a-common-pitfall-in-programming-1i1c</link>
      <guid>https://dev.to/homolibere/the-dangling-if-else-problem-a-common-pitfall-in-programming-1i1c</guid>
      <description>&lt;p&gt;One common pitfall that programmers often fall into when working with conditional statements in C# is known as the "dangling if-else problem". This issue arises when an if-else statement is not structured properly, leading to unexpected or unintended behavior in the program.&lt;/p&gt;

&lt;p&gt;The dangling if-else problem can occur when there are multiple if-else statements nested within each other, and the conditions are not properly defined or the logic is not correctly implemented. This can result in the code executing in ways that are not intended by the programmer, leading to bugs or errors in the program.&lt;/p&gt;

&lt;p&gt;To illustrate this issue, let's take a look at a simple example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;int num = 5;

if (num &amp;lt; 10)
    Console.WriteLine("Number is less than 10");
else 
    if (num &amp;lt; 5)
        Console.WriteLine("Number is less than 5");
    else
        Console.WriteLine("Number is equal to or greater than 5");
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this code snippet, the intent is to print out a message based on the value of the variable &lt;code&gt;num&lt;/code&gt;. However, due to the improper nesting of the if-else statements, the program will actually print out "Number is less than 10" even if &lt;code&gt;num&lt;/code&gt; is equal to or greater than 5. This is because the second if statement (&lt;code&gt;num &amp;lt; 5&lt;/code&gt;) is nested within the else block of the first if statement, causing it to be skipped over.&lt;/p&gt;

&lt;p&gt;To avoid the dangling if-else problem, it is important to carefully structure your if-else statements and ensure that the conditions are properly defined. One way to avoid this issue is to use braces &lt;code&gt;{}&lt;/code&gt; to explicitly define the blocks of code within the if-else statements, like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;int num = 5;

if (num &amp;lt; 10)
{
    Console.WriteLine("Number is less than 10");
}
else 
{
    if (num &amp;lt; 5)
    {
        Console.WriteLine("Number is less than 5");
    }
    else
    {
        Console.WriteLine("Number is equal to or greater than 5");
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By using braces to clearly define the blocks of code within each if-else statement, you can prevent the dangling if-else problem and ensure that your program behaves as expected. Remember to always test your code thoroughly and pay attention to the structure of your conditional statements to avoid running into unexpected issues.&lt;/p&gt;

</description>
      <category>csharp</category>
    </item>
    <item>
      <title>What is DateTime Manipulation in C#?</title>
      <dc:creator>Nick</dc:creator>
      <pubDate>Thu, 14 Mar 2024 10:39:09 +0000</pubDate>
      <link>https://dev.to/homolibere/what-is-datetime-manipulation-in-c-30a8</link>
      <guid>https://dev.to/homolibere/what-is-datetime-manipulation-in-c-30a8</guid>
      <description>&lt;p&gt;DateTime manipulation in C# refers to the ability to perform various operations on date and time values, such as adding or subtracting time, extracting specific components of a date, formatting dates in different ways, and comparing different dates.&lt;/p&gt;

&lt;p&gt;One of the most common tasks when working with dates and times is to manipulate them in some way. C# provides a DateTime struct that represents a specific point in time and allows us to perform various operations on dates and times.&lt;/p&gt;

&lt;p&gt;Here are some common examples of date and time manipulation in C#:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Adding or subtracting time:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;DateTime&lt;/span&gt; &lt;span class="n"&gt;currentDate&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Now&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;DateTime&lt;/span&gt; &lt;span class="n"&gt;futureDate&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;currentDate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddDays&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;7&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Add 7 days to the current date&lt;/span&gt;
&lt;span class="n"&gt;DateTime&lt;/span&gt; &lt;span class="n"&gt;pastDate&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;currentDate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddMonths&lt;/span&gt;&lt;span class="p"&gt;(-&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Subtract 1 month from the current date&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Extracting components of a date:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;DateTime&lt;/span&gt; &lt;span class="n"&gt;currentDate&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Now&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;day&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;currentDate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Day&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Get the day of the month&lt;/span&gt;
&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;month&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;currentDate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Month&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Get the month&lt;/span&gt;
&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;year&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;currentDate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Year&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Get the year&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Formatting dates:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;DateTime&lt;/span&gt; &lt;span class="n"&gt;currentDate&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Now&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;formattedDate&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;currentDate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"MM/dd/yyyy"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Format the date as "MM/dd/yyyy"&lt;/span&gt;
&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;formattedTime&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;currentDate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"hh:mm:ss tt"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Format the time as "hh:mm:ss tt"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Comparing dates:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;DateTime&lt;/span&gt; &lt;span class="n"&gt;date1&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;2022&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;31&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;DateTime&lt;/span&gt; &lt;span class="n"&gt;date2&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Now&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;date1&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;date2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Date1 is before Date2"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;date1&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;date2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Date1 is after Date2"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Date1 and Date2 are equal"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;DateTime manipulation is an essential skill for any C# developer working with date and time values. By understanding how to manipulate dates and times effectively, you can perform a wide range of tasks and create more dynamic and flexible applications.&lt;/p&gt;

</description>
      <category>csharp</category>
    </item>
  </channel>
</rss>
