<?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: Daniel Balcarek</title>
    <description>The latest articles on DEV Community by Daniel Balcarek (@gramli).</description>
    <link>https://dev.to/gramli</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%2F3669374%2F0ad6f20b-8faa-45a4-a8ef-ef83e702d37b.png</url>
      <title>DEV Community: Daniel Balcarek</title>
      <link>https://dev.to/gramli</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/gramli"/>
    <language>en</language>
    <item>
      <title>.NET Reflection Benchmarks: Enum Attribute Performance (Part 1)</title>
      <dc:creator>Daniel Balcarek</dc:creator>
      <pubDate>Mon, 13 Apr 2026 13:06:35 +0000</pubDate>
      <link>https://dev.to/gramli/net-reflection-benchmarks-enum-attribute-performance-part-1-1c44</link>
      <guid>https://dev.to/gramli/net-reflection-benchmarks-enum-attribute-performance-part-1-1c44</guid>
      <description>&lt;p&gt;&lt;strong&gt;.NET reflection&lt;/strong&gt; has a reputation for being slow, hard to read and something to avoid. But does this still apply in newer .NET versions like .NET 8+?&lt;/p&gt;

&lt;p&gt;When I was a junior developer, I often heard that reflection should not be used, just avoid it. Over time, I encountered many scenarios where reflection was not only convenient, but also the most maintainable solution.&lt;/p&gt;

&lt;p&gt;In this series, I’ll &lt;strong&gt;benchmark common reflection use cases&lt;/strong&gt; to understand its real performance characteristics and trade-offs. The goal is simple: &lt;strong&gt;separate myths from measurable cost&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In this first part, we focus on a very common scenario, retrieving custom attributes from enum values and compare reflection against alternatives such as &lt;code&gt;Dictionary&lt;/code&gt; and &lt;code&gt;FrozenDictionary&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Get Custom Attribute
&lt;/h2&gt;

&lt;p&gt;Sometimes we need human-readable descriptions for enum values, and one of the simplest approaches is to use attributes. Reflection provides a convenient way to retrieve these attributes at runtime.&lt;/p&gt;

&lt;p&gt;In this benchmark, we evaluate the performance of a generic extension method that retrieves custom attributes from enum values.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reflection Implementation
&lt;/h3&gt;

&lt;p&gt;A basic extension method for retrieving a custom attribute from an enum looks like this:&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;static&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EnumExtensions&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="n"&gt;GetCustomAttribute&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="n"&gt;Enum&lt;/span&gt; &lt;span class="n"&gt;customEnumValue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Attribute&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;enumType&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;customEnumValue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetType&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;enumType&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Enum&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;enumType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;customEnumValue&lt;/span&gt;&lt;span class="p"&gt;)!)!&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetCustomAttribute&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()!;&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;Since we are creating a benchmark, we should also implement a more performant version. We can improve performance by caching results using a &lt;code&gt;ConcurrentDictionary&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;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EnumExtensions&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;ConcurrentDictionary&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;(&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt; &lt;span class="n"&gt;EnumType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Type&lt;/span&gt; &lt;span class="n"&gt;AttributeType&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;MemberName&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;Attribute&lt;/span&gt;&lt;span class="p"&gt;?&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_cache&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;GetCustomAttributeCached&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="n"&gt;Enum&lt;/span&gt; &lt;span class="n"&gt;customEnumValue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Attribute&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;enumType&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;customEnumValue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetType&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;key&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;enumType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;Enum&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;enumType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;customEnumValue&lt;/span&gt;&lt;span class="p"&gt;)!);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;?)&lt;/span&gt;&lt;span class="n"&gt;_cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetOrAdd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
            &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;EnumType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MemberName&lt;/span&gt;&lt;span class="p"&gt;)?.&lt;/span&gt;&lt;span class="nf"&gt;GetCustomAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AttributeType&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: &lt;code&gt;ConcurrentDictionary&lt;/code&gt; is used here to ensure thread-safe access, as this extension method may be called concurrently from multiple threads.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Dictionary-Based Alternative
&lt;/h3&gt;

&lt;p&gt;To compare against a faster alternative, I created a static dictionary that maps &lt;code&gt;CustomEnum&lt;/code&gt; values to their corresponding descriptions:&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;static&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CustomEnumMap&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;Dictionary&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;CustomEnum&lt;/span&gt;&lt;span class="p"&gt;,&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;Map&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Dictionary&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;CustomEnum&lt;/span&gt;&lt;span class="p"&gt;,&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="p"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Starting with .NET 8, we can use &lt;code&gt;FrozenDictionary&lt;/code&gt;, which is optimized for read-heavy, write-once scenarios. This allows us to compare it with a standard &lt;code&gt;Dictionary&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;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;FrozenDictionary&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;CustomSmallEnum&lt;/span&gt;&lt;span class="p"&gt;,&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;FrozenSmallMap&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToFrozenDictionary&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: Another possible approach is to use a &lt;strong&gt;source generator&lt;/strong&gt;, which can eliminate manual maintenance of mappings like &lt;code&gt;FrozenDictionary&lt;/code&gt;. However, this comes at the cost of increased complexity and more difficult debugging. I am not covering it here, as it would likely produce performance similar to the &lt;code&gt;FrozenDictionary&lt;/code&gt; approach.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Enum definition
&lt;/h3&gt;

&lt;p&gt;To evaluate whether enum size has any impact on performance, I created three enums of different sizes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;CustomLargeEnum&lt;/strong&gt; with 35 values &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CustomEnum&lt;/strong&gt; with 16 values&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CustomSmallEnum&lt;/strong&gt; with 7 values&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;CustomEnumAttribute&lt;/code&gt; is very simple:&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="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;AttributeUsage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AttributeTargets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CustomEnumAttribute&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Attribute&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Description&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;CustomEnumAttribute&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;description&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;description&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Benchmark code
&lt;/h3&gt;

&lt;p&gt;The benchmark iterates over pre-generated arrays of random enum values and calls &lt;code&gt;GetCustomAttribute&lt;/code&gt; for each item.&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="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;SimpleJob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RuntimeMoniker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Net10_0&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Orderer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BenchmarkDotNet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SummaryOrderPolicy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FastestToSlowest&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;MemoryDiagnoser&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GetEnumAttributeBenchmark&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Params&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="m"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="m"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="m"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;Count&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;CustomEnum&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;_values&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;CustomSmallEnum&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;_smallValues&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;CustomLargeEnum&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;_largeValues&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="n"&gt;GlobalSetup&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Setup&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;rnd&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;Random&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;42&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;all&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Enum&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetValues&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;CustomEnum&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;smallAll&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Enum&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetValues&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;CustomSmallEnum&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;largeAll&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Enum&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetValues&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;CustomLargeEnum&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;

            &lt;span class="n"&gt;_values&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[..&lt;/span&gt; &lt;span class="n"&gt;Enumerable&lt;/span&gt;
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Count&lt;/span&gt;&lt;span class="p"&gt;)&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="n"&gt;_&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;all&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;rnd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;all&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Length&lt;/span&gt;&lt;span class="p"&gt;)])];&lt;/span&gt;

            &lt;span class="n"&gt;_smallValues&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[..&lt;/span&gt; &lt;span class="n"&gt;Enumerable&lt;/span&gt;
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Count&lt;/span&gt;&lt;span class="p"&gt;)&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="n"&gt;_&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;smallAll&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;rnd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;smallAll&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Length&lt;/span&gt;&lt;span class="p"&gt;)])];&lt;/span&gt;

            &lt;span class="n"&gt;_largeValues&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[..&lt;/span&gt; &lt;span class="n"&gt;Enumerable&lt;/span&gt;
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Count&lt;/span&gt;&lt;span class="p"&gt;)&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="n"&gt;_&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;largeAll&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;rnd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;largeAll&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Length&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;Benchmark&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Baseline&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;CustomEnum&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;for&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;i&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;_values&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;++)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_values&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;GetCustomAttribute&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;CustomEnumAttribute&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;().&lt;/span&gt;&lt;span class="n"&gt;Description&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="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Benchmark&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;CustomLargeEnum&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;for&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;i&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;_largeValues&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;++)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_largeValues&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;GetCustomAttribute&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;CustomEnumAttribute&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;().&lt;/span&gt;&lt;span class="n"&gt;Description&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="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;a href="https://github.com/Gramli/ReflectionBenchmark/tree/main/src/ReflectionBenchmark/GetEnumAttribute" rel="noopener noreferrer"&gt;Source Solution&lt;/a&gt;
&lt;/h3&gt;

&lt;h3&gt;
  
  
  Results
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;BenchmarkDotNet v0.15.8, Windows 10 (10.0.19045.6466/22H2/2022Update)
Intel Core i5-6400 CPU 2.70GHz (Skylake), 1 CPU, 4 logical and 4 physical cores
.NET SDK 10.0.201
  [Host]    : .NET 10.0.5 (10.0.5, 10.0.526.15411), X64 RyuJIT x86-64-v3
  .NET 10.0 : .NET 10.0.5 (10.0.5, 10.0.526.15411), X64 RyuJIT x86-64-v3

Job=.NET 10.0  Runtime=.NET 10.0
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Count&lt;/th&gt;
&lt;th&gt;Mean&lt;/th&gt;
&lt;th&gt;Allocated&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;CustomEnumFrozenMap&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;0.5172 ns&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CustomLargeEnumFrozenMap&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;0.5200 ns&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CustomSmallEnumFrozenMap&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;0.5209 ns&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CustomSmallEnumMap&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;4.5348 ns&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CustomLargeEnumMap&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;4.5409 ns&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CustomEnumMap&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;4.5477 ns&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CustomLargeEnumCached&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;55.2759 ns&lt;/td&gt;
&lt;td&gt;24 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CustomSmallEnumCached&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;55.8674 ns&lt;/td&gt;
&lt;td&gt;24 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CustomEnumCached&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;63.9393 ns&lt;/td&gt;
&lt;td&gt;24 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CustomSmallEnum&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;886.7600 ns&lt;/td&gt;
&lt;td&gt;280 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CustomEnum&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;887.6823 ns&lt;/td&gt;
&lt;td&gt;272 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CustomLargeEnum&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;888.5621 ns&lt;/td&gt;
&lt;td&gt;280 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CustomEnumFrozenMap&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;99.5479 ns&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CustomLargeEnumFrozenMap&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;99.5514 ns&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CustomSmallEnumFrozenMap&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;99.8055 ns&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CustomLargeEnumMap&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;336.8072 ns&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CustomSmallEnumMap&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;337.0285 ns&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CustomEnumMap&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;337.2214 ns&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CustomSmallEnumCached&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;5,473.7805 ns&lt;/td&gt;
&lt;td&gt;2401 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CustomLargeEnumCached&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;5,571.3201 ns&lt;/td&gt;
&lt;td&gt;2402 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CustomEnumCached&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;6,463.1792 ns&lt;/td&gt;
&lt;td&gt;2401 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CustomSmallEnum&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;90,100.0065 ns&lt;/td&gt;
&lt;td&gt;28026 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CustomLargeEnum&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;93,418.7069 ns&lt;/td&gt;
&lt;td&gt;28174 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CustomEnum&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;94,719.9186 ns&lt;/td&gt;
&lt;td&gt;27112 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CustomEnumFrozenMap&lt;/td&gt;
&lt;td&gt;1000&lt;/td&gt;
&lt;td&gt;949.7209 ns&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CustomSmallEnumFrozenMap&lt;/td&gt;
&lt;td&gt;1000&lt;/td&gt;
&lt;td&gt;955.7943 ns&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CustomLargeEnumFrozenMap&lt;/td&gt;
&lt;td&gt;1000&lt;/td&gt;
&lt;td&gt;963.3053 ns&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CustomLargeEnumMap&lt;/td&gt;
&lt;td&gt;1000&lt;/td&gt;
&lt;td&gt;3,266.3050 ns&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CustomEnumMap&lt;/td&gt;
&lt;td&gt;1000&lt;/td&gt;
&lt;td&gt;3,267.1102 ns&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CustomSmallEnumMap&lt;/td&gt;
&lt;td&gt;1000&lt;/td&gt;
&lt;td&gt;3,270.5155 ns&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CustomSmallEnumCached&lt;/td&gt;
&lt;td&gt;1000&lt;/td&gt;
&lt;td&gt;56,106.7629 ns&lt;/td&gt;
&lt;td&gt;24006 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CustomLargeEnumCached&lt;/td&gt;
&lt;td&gt;1000&lt;/td&gt;
&lt;td&gt;57,870.7619 ns&lt;/td&gt;
&lt;td&gt;24015 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CustomEnumCached&lt;/td&gt;
&lt;td&gt;1000&lt;/td&gt;
&lt;td&gt;70,576.5613 ns&lt;/td&gt;
&lt;td&gt;24010 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CustomSmallEnum&lt;/td&gt;
&lt;td&gt;1000&lt;/td&gt;
&lt;td&gt;909,513.7370 ns&lt;/td&gt;
&lt;td&gt;280254 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CustomEnum&lt;/td&gt;
&lt;td&gt;1000&lt;/td&gt;
&lt;td&gt;921,044.2253 ns&lt;/td&gt;
&lt;td&gt;271621 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CustomLargeEnum&lt;/td&gt;
&lt;td&gt;1000&lt;/td&gt;
&lt;td&gt;925,368.7630 ns&lt;/td&gt;
&lt;td&gt;281798 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CustomLargeEnumFrozenMap&lt;/td&gt;
&lt;td&gt;10000&lt;/td&gt;
&lt;td&gt;9,240.9962 ns&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CustomEnumFrozenMap&lt;/td&gt;
&lt;td&gt;10000&lt;/td&gt;
&lt;td&gt;9,243.3792 ns&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CustomSmallEnumFrozenMap&lt;/td&gt;
&lt;td&gt;10000&lt;/td&gt;
&lt;td&gt;9,249.7069 ns&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CustomSmallEnumMap&lt;/td&gt;
&lt;td&gt;10000&lt;/td&gt;
&lt;td&gt;32,852.3596 ns&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CustomEnumMap&lt;/td&gt;
&lt;td&gt;10000&lt;/td&gt;
&lt;td&gt;32,912.0222 ns&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CustomLargeEnumMap&lt;/td&gt;
&lt;td&gt;10000&lt;/td&gt;
&lt;td&gt;32,914.4958 ns&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CustomLargeEnumCached&lt;/td&gt;
&lt;td&gt;10000&lt;/td&gt;
&lt;td&gt;554,776.4648 ns&lt;/td&gt;
&lt;td&gt;240152 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CustomSmallEnumCached&lt;/td&gt;
&lt;td&gt;10000&lt;/td&gt;
&lt;td&gt;559,146.3867 ns&lt;/td&gt;
&lt;td&gt;240057 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CustomEnumCached&lt;/td&gt;
&lt;td&gt;10000&lt;/td&gt;
&lt;td&gt;671,990.9701 ns&lt;/td&gt;
&lt;td&gt;240102 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CustomSmallEnum&lt;/td&gt;
&lt;td&gt;10000&lt;/td&gt;
&lt;td&gt;8,941,992.9688 ns&lt;/td&gt;
&lt;td&gt;2802559 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CustomEnum&lt;/td&gt;
&lt;td&gt;10000&lt;/td&gt;
&lt;td&gt;9,313,023.7981 ns&lt;/td&gt;
&lt;td&gt;2716716 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CustomLargeEnum&lt;/td&gt;
&lt;td&gt;10000&lt;/td&gt;
&lt;td&gt;9,354,293.2292 ns&lt;/td&gt;
&lt;td&gt;2818074 B&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Let's start with the &lt;code&gt;Count = 1&lt;/code&gt; scenario.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;FrozenDictionary&lt;/code&gt; appears to be the fastest approach, with measured times in the sub-nanosecond range. However, results at this scale should be interpreted cautiously, as they are highly sensitive to JIT optimizations.&lt;/p&gt;

&lt;p&gt;A standard &lt;code&gt;Dictionary&lt;/code&gt; performs consistently at around &lt;strong&gt;~4.5 ns&lt;/strong&gt; per lookup, which is still extremely fast and effectively negligible in most applications.&lt;/p&gt;

&lt;p&gt;Cached reflection shows a significant improvement over uncached reflection, reducing execution time from &lt;strong&gt;~888 ns&lt;/strong&gt; to &lt;strong&gt;~55-65 ns&lt;/strong&gt; per call. This demonstrates that caching eliminates the majority of reflection overhead.&lt;/p&gt;

&lt;p&gt;Uncached reflection is by far the slowest approach, with roughly 15-16x higher latency compared to cached reflection and orders of magnitude slower than dictionary-based solutions.&lt;/p&gt;

&lt;p&gt;In terms of memory allocations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Dictionary-based approaches allocate no memory during lookup&lt;/li&gt;
&lt;li&gt;Cached reflection allocates ~24 B per call. This is caused by boxing the enum value when using the &lt;code&gt;Enum&lt;/code&gt; type. A zero-allocation alternative is possible using a generic constraint, but it results in a more verbose API:
&lt;/li&gt;
&lt;/ul&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;static&lt;/span&gt; &lt;span class="n"&gt;TAttribute&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;GetCustomAttributeCached&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TEnum&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TAttribute&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="n"&gt;TEnum&lt;/span&gt; &lt;span class="k"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
    &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;TEnum&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Enum&lt;/span&gt; 
    &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;TAttribute&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Attribute&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Uncached reflection allocates ~270-280 B per call&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When scaling to higher &lt;code&gt;Count&lt;/code&gt; values, the relative differences remain consistent. Execution time increases linearly for all approaches, but the absolute gap between them becomes more pronounced due to the higher per-call cost of reflection.&lt;/p&gt;

&lt;p&gt;Finally, the size of the enum does not have a measurable impact on performance in this benchmark, which is expected given the constant-time nature of the underlying lookup mechanisms.&lt;/p&gt;

&lt;h3&gt;
  
  
  Summary
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Dictionary-based solutions&lt;/strong&gt; provide the &lt;strong&gt;best raw performance&lt;/strong&gt; and avoid allocations during lookups, but require manual maintenance whenever enum values change. This could be avoided with source generators, which provide zero-reflection, zero-allocation, near-native performance. However, they introduce additional complexity and make debugging more difficult, which may be unnecessary for many scenarios.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reflection&lt;/strong&gt; is slower and introduces allocations, but when &lt;strong&gt;combined with caching, the performance cost becomes negligible&lt;/strong&gt; for low-frequency operations. In many real-world scenarios, the improved maintainability outweighs the performance difference.&lt;/p&gt;

&lt;p&gt;Reflection should therefore be avoided in hot paths but remains a practical and maintainable solution for metadata access.&lt;/p&gt;

</description>
      <category>performance</category>
      <category>dotnet</category>
      <category>csharp</category>
      <category>softwaredevelopment</category>
    </item>
    <item>
      <title>Google Translate Who? The Only Translator You’ll Ever Need, Powered by Neural Networks &amp; 90s Scams</title>
      <dc:creator>Daniel Balcarek</dc:creator>
      <pubDate>Tue, 07 Apr 2026 12:30:30 +0000</pubDate>
      <link>https://dev.to/gramli/google-translate-who-the-only-translator-youll-ever-need-powered-by-neural-networks-90s-scams-3c7i</link>
      <guid>https://dev.to/gramli/google-translate-who-the-only-translator-youll-ever-need-powered-by-neural-networks-90s-scams-3c7i</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/aprilfools-2026"&gt;DEV April Fools Challenge&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I don’t have much time right now because “summer is coming” in our timezone, but I just can’t miss this challenge!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;What happens when you treat an absolutely ridiculous idea with enterprise-level seriousness?&lt;/p&gt;

&lt;p&gt;For the &lt;strong&gt;DEV April Fools Challenge&lt;/strong&gt;, I built an app that nobody asked for, but implemented as if it were a &lt;strong&gt;mission-critical production system&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Please review it with the level of &lt;strong&gt;seriousness it absolutely does&lt;/strong&gt; not &lt;strong&gt;deserve&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;Everyone knows Google Translate so I made a &lt;strong&gt;direct competitor&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It’s the best translator in the whole world! Not just text translation, but also speech-to-text and text-to-speech. It can translate into &lt;strong&gt;vending machine, borked regex, yoda style&lt;/strong&gt; and many more.&lt;/p&gt;

&lt;p&gt;Just try it below, but beware of classic 90s internet scams and don’t waste your precious tokens. Remember, premium tokens are extremely limited.&lt;/p&gt;

&lt;p&gt;And if you want to translate something, forget pressing Enter. This translator reacts only to the &lt;strong&gt;Scroll Lock&lt;/strong&gt; or &lt;strong&gt;Insert&lt;/strong&gt; keys.&lt;/p&gt;

&lt;p&gt;But translation is only the beginning of my &lt;strong&gt;innovation journey&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Need the current time in a random timezone measured in microseconds?&lt;br&gt;
Of course you do.&lt;/p&gt;

&lt;p&gt;Still not enough productivity? The app also includes carefully preserved cheats for classic 90s games, because documentation is temporary, &lt;strong&gt;cheat codes are forever&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;And for maximum workplace efficiency, an evil paperclip assistant is always ready to help whether you want it or not.&lt;/p&gt;

&lt;p&gt;I tested adding more features, but &lt;strong&gt;things escalated quickly&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;There’s also a &lt;strong&gt;“Works on My Machine”&lt;/strong&gt; button.&lt;br&gt;
I don’t fully understand what it does, but it increases developer confidence by 841.347%.&lt;/p&gt;

&lt;p&gt;But true innovation isn’t measured by features, it’s measured by recognition.&lt;br&gt;
That’s why the app includes a completely serious achievement system called &lt;strong&gt;Definitely Not Dev Badges™&lt;/strong&gt;. These badges are awarded for outstanding professionalism and each automatically increases your seniority!&lt;/p&gt;

&lt;p&gt;Wanna spread the chaos? Use &lt;strong&gt;the buttons&lt;/strong&gt;, they format your text for maximum professionalism.&lt;/p&gt;

&lt;p&gt;Finally, I strongly recommend &lt;strong&gt;not using &lt;code&gt;Ctrl+V&lt;/code&gt;&lt;/strong&gt;, previous tests resulted in a catastrophe.&lt;/p&gt;

&lt;p&gt;After all this innovation, there’s only one thing left to say.&lt;/p&gt;

&lt;p&gt;As viewers in Central and Eastern Europe might remember from TV salesman Horst Fuchs — famous for enthusiastic 90s infomercials:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;“And that’s worth it!”&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;




&lt;div class="ltag__cloud-run"&gt;
  &lt;iframe height="600px" src="https://absurd-text-transformer-768859394911.europe-west1.run.app/"&gt;
  &lt;/iframe&gt;
&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Unicorm Resource Locator:&lt;/strong&gt; &lt;a href="https://absurd-text-transformer-768859394911.europe-west1.run.app/" rel="noopener noreferrer"&gt;absurd-text-transformer&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/Gramli" rel="noopener noreferrer"&gt;
        Gramli
      &lt;/a&gt; / &lt;a href="https://github.com/Gramli/text-transformer" rel="noopener noreferrer"&gt;
        text-transformer
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;🦄 Absurd Text Transformer&lt;/h1&gt;
&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;A chaotic, feature-packed text transformer built for maximum nonsense. Transform your boring text into Yoda-speak, Brainrot, Zalgo corruption, Corporate Synergy, Pirate lingo, and many more absurd formats — complete with Text-to-Speech, voice dictation, cursor trails, an evil floating Clippy, and 90s cheat codes.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Built for the &lt;strong&gt;DEV April Fools Challenge&lt;/strong&gt; 🎉&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;What It Does&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;The Absurd Text Transformer takes normal text and ruins it — beautifully. It offers &lt;strong&gt;17 transformation modes&lt;/strong&gt;, each with a distinct personality, unique theme, and its own TTS voice settings.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Transformation Modes&lt;/h3&gt;
&lt;/div&gt;
&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Mode&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;🤡 Silly English&lt;/td&gt;
&lt;td&gt;Randomized caps, emoji spam, shuffled words&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🌠 Yoda Style&lt;/td&gt;
&lt;td&gt;Sentence structure reversed, Jedi-style&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🏴‍☠️ Pirate&lt;/td&gt;
&lt;td&gt;Ahoy! Full pirate vocabulary substitution&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;👶 Baby Talk&lt;/td&gt;
&lt;td&gt;Uwu-fied cute baby babble&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🐱 Cat&lt;/td&gt;
&lt;td&gt;Pure meow-based communication&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🐶 Dog&lt;/td&gt;
&lt;td&gt;Woof enthusiastically repeated&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🐹 Hamster&lt;/td&gt;
&lt;td&gt;Squeak-inflected vocabulary chaos&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🦆 Duck&lt;/td&gt;
&lt;td&gt;Quack, quack, quack&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🐄 Cow&lt;/td&gt;
&lt;td&gt;Moo. Just moo.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🎰 Vending&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;…&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/Gramli/text-transformer" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  How I Built It
&lt;/h2&gt;

&lt;p&gt;The translator is powered by a highly advanced neural network trained on archived forum posts, expired trial software popups, and suspicious email chains from 1998–2003.&lt;/p&gt;

&lt;p&gt;Early benchmarks show it performs 300% worse than existing solutions, which confirms we are innovating in the right direction.&lt;/p&gt;

&lt;p&gt;After months of research, optimization, and absolutely no investor funding, the final architecture emerged.&lt;/p&gt;

&lt;p&gt;Behind all this groundbreaking technology lies a shocking truth:&lt;/p&gt;

&lt;p&gt;It’s just &lt;strong&gt;Svelte running on Google Cloud Run&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prize Category
&lt;/h2&gt;

&lt;p&gt;I want to be the very best&lt;br&gt;
Like no one ever was&lt;br&gt;
To catch them is my real test&lt;br&gt;
To train them is my cause&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Community Favorite, I choose you!"&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Exit(0)
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Remember:&lt;/strong&gt; if the translation looks wrong, the problem is probably between keyboard and chair.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Happy DEV April Fools Challenge&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>418challenge</category>
      <category>showdev</category>
      <category>jokes</category>
    </item>
    <item>
      <title>Clean Unit Testing in .NET: Practical xUnit and Moq Tips</title>
      <dc:creator>Daniel Balcarek</dc:creator>
      <pubDate>Tue, 31 Mar 2026 14:12:24 +0000</pubDate>
      <link>https://dev.to/gramli/clean-unit-testing-in-net-practical-xunit-and-moq-tips-5bki</link>
      <guid>https://dev.to/gramli/clean-unit-testing-in-net-practical-xunit-and-moq-tips-5bki</guid>
      <description>&lt;p&gt;When writing unit tests in .NET, the basics are easy to learn. Frameworks like &lt;strong&gt;xUnit&lt;/strong&gt; and &lt;strong&gt;Moq&lt;/strong&gt; make it straightforward to create tests, mock dependencies and verify behavior. The real challenges appear later, when you start testing real production code.&lt;/p&gt;

&lt;p&gt;Sooner or later, you run into practical questions that documentation rarely answers clearly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What is the cleanest way to configure &lt;code&gt;IOptions&amp;lt;T&amp;gt;&lt;/code&gt; in unit tests?&lt;/li&gt;
&lt;li&gt;How can you test internal classes without making them public?&lt;/li&gt;
&lt;li&gt;How do you verify &lt;code&gt;ILogger&lt;/code&gt; calls using &lt;code&gt;Moq&lt;/code&gt; without verbose boilerplate?&lt;/li&gt;
&lt;li&gt;How can you test ILogger calls without Moq at all?&lt;/li&gt;
&lt;li&gt;How do you log output in xUnit tests?&lt;/li&gt;
&lt;li&gt;How can you keep &lt;code&gt;[Theory]&lt;/code&gt; tests type-safe?&lt;/li&gt;
&lt;li&gt;How can you assert objects created internally using Moq &lt;code&gt;.Callback&lt;/code&gt;?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are not complex architectural problems, just practical issues that appear repeatedly in real projects. &lt;/p&gt;

&lt;p&gt;This article shows &lt;strong&gt;practical unit testing tips and tricks for .NET developers&lt;/strong&gt;, helping you write cleaner, more maintainable tests with &lt;strong&gt;xUnit&lt;/strong&gt; and &lt;strong&gt;Moq&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to use IOptions in Unit Tests (Without Mocking)
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Avoids unnecessary mocking and keeps your tests simple, type-safe, and easy to configure.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Many developers try to use &lt;code&gt;new Mock&amp;lt;IOptions&amp;lt;T&amp;gt;&amp;gt;()&lt;/code&gt;, but that's unnecessary overhead. &lt;code&gt;The Microsoft.Extensions.Options&lt;/code&gt; library provides a built-in helper specifically for this. You can simply use the &lt;code&gt;Create&lt;/code&gt; extension method from the &lt;code&gt;Microsoft.Extensions.Options.Options&lt;/code&gt; class to easily generate the required options object instead.&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;readonly&lt;/span&gt; &lt;span class="n"&gt;IOptions&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;DistributedLockOptions&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_options&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// ...&lt;/span&gt;

&lt;span class="n"&gt;_options&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Microsoft&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Extensions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;DistributedLockOptions&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// configure properties&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Make Internals Visible to Test Projects via .csproj
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Enables testing internal logic without exposing implementation details publicly, keeping your code encapsulated.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You shouldn't make a class &lt;code&gt;public&lt;/code&gt; just to test it. You can keep your logic &lt;code&gt;internal&lt;/code&gt; and still grant your test project access by adding a simple attribute to your source project's &lt;code&gt;.csproj&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;    &lt;span class="nt"&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;AssemblyAttribute&lt;/span&gt; &lt;span class="na"&gt;Include=&lt;/span&gt;&lt;span class="s"&gt;"System.Runtime.CompilerServices.InternalsVisibleToAttribute"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;_Parameter1&amp;gt;&lt;/span&gt;Your.Test.Project.Name&lt;span class="nt"&gt;&amp;lt;/_Parameter1&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/AssemblyAttribute&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Example
&lt;/h3&gt;

&lt;h4&gt;
  
  
  File.Core.csproj:
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;    &lt;span class="nt"&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;AssemblyAttribute&lt;/span&gt; &lt;span class="na"&gt;Include=&lt;/span&gt;&lt;span class="s"&gt;"System.Runtime.CompilerServices.InternalsVisibleToAttribute"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;_Parameter1&amp;gt;&lt;/span&gt;File.Core.UnitTests&lt;span class="nt"&gt;&amp;lt;/_Parameter1&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/AssemblyAttribute&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Alternatively, you can place &lt;code&gt;[assembly: InternalsVisibleTo("Your.Test.Project")]&lt;/code&gt; in an &lt;code&gt;AssemblyInfo.cs&lt;/code&gt; file (or any source file). This approach is common in older .NET Framework projects. In SDK-style projects, configuring the attribute in the &lt;code&gt;.csproj&lt;/code&gt; file avoids introducing an additional file and keeps assembly configuration centralized.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In this example, &lt;code&gt;File.Core.UnitTests&lt;/code&gt; is the destination test project name. See a &lt;a href="https://github.com/Gramli/FileApi/blob/main/src/File.Core/File.Core.csproj" rel="noopener noreferrer"&gt;project example here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mock ILogger with Moq: Simplify Log Verification
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Simplifies verifying logs in tests, reducing boilerplate and making assertions readable and maintainable.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Mocking &lt;code&gt;ILogger&amp;lt;T&amp;gt;&lt;/code&gt; is notoriously difficult because the commonly used methods like &lt;code&gt;LogInformation()&lt;/code&gt; or &lt;code&gt;LogError()&lt;/code&gt; are extension methods, Moq cannot intercept those directly. You must verify calls against the underlying interface method &lt;code&gt;Log&amp;lt;TState&amp;gt;()&lt;/code&gt;, which has a complex generic signature.&lt;/p&gt;

&lt;p&gt;Here is a useful example of an extension class that simplifies verifying logger method calls:&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;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.Extensions.Logging&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;Moq&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;Extensions&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MoqLoggerExtensions&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="n"&gt;VerifyLog&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="n"&gt;Mock&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ILogger&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;loggerMock&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;LogLevel&lt;/span&gt; &lt;span class="n"&gt;logLevel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;EventId&lt;/span&gt; &lt;span class="n"&gt;eventId&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;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Times&lt;/span&gt; &lt;span class="n"&gt;times&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;loggerMock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Verify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
               &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                   &lt;span class="n"&gt;It&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Is&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;LogLevel&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;logLevel&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
                   &lt;span class="n"&gt;It&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Is&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;EventId&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;eventId&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
                   &lt;span class="n"&gt;It&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Is&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;It&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsAnyType&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;((&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;o&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="n"&gt;StringComparison&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InvariantCultureIgnoreCase&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
                   &lt;span class="n"&gt;It&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsAny&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(),&lt;/span&gt;
                   &lt;span class="n"&gt;It&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsAny&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Func&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;It&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsAnyType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;?,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;()),&lt;/span&gt;
               &lt;span class="n"&gt;times&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="n"&gt;VerifyLog&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="n"&gt;Mock&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ILogger&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;loggerMock&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;LogLevel&lt;/span&gt; &lt;span class="n"&gt;logLevel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;EventId&lt;/span&gt; &lt;span class="n"&gt;eventId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Times&lt;/span&gt; &lt;span class="n"&gt;times&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;loggerMock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Verify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
               &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                   &lt;span class="n"&gt;It&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Is&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;LogLevel&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;logLevel&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
                   &lt;span class="n"&gt;It&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Is&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;EventId&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;eventId&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
                   &lt;span class="n"&gt;It&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsAny&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;It&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsAnyType&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(),&lt;/span&gt;
                   &lt;span class="n"&gt;It&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsAny&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(),&lt;/span&gt;
                   &lt;span class="n"&gt;It&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsAny&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Func&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;It&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsAnyType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;?,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;()),&lt;/span&gt;
               &lt;span class="n"&gt;times&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example usage in a &lt;a href="https://github.com/Gramli/FileApi/blob/main/src/Tests/UnitTests/File.UnitTests.Common/Extensions/MoqLoggerExtensions.cs" rel="noopener noreferrer"&gt;project&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: Prefer &lt;code&gt;FakeLogger&amp;lt;T&amp;gt;&lt;/code&gt; for new projects targeting .NET 8+. Use the Moq approach when you're already using Moq throughout your test suite or need to target older .NET versions.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Test ILogger Without Moq Using FakeLogger
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Removes the need for a mocking framework entirely when asserting log output, using a first-party in-memory logger built for testing.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Since .NET 8, the &lt;code&gt;Microsoft.Extensions.Logging.Testing&lt;/code&gt; NuGet package ships a built-in &lt;code&gt;FakeLogger&amp;lt;T&amp;gt;&lt;/code&gt; class designed specifically for unit tests. It collects all log records in memory, so you can assert on them directly without setting up any mocks or extension methods.&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;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.Extensions.Logging.Testing&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;logger&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;FakeLogger&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;OrderService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;service&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;OrderService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ProcessOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;orderId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;Assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Equal&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="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Collector&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Count&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;Assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LogLevel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Warning&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Collector&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LatestRecord&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;Assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"invalid order"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Collector&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LatestRecord&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;StringComparison&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OrdinalIgnoreCase&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;FakeLogger&amp;lt;T&amp;gt;&lt;/code&gt; exposes a &lt;code&gt;Collector&lt;/code&gt; property (&lt;code&gt;FakeLogCollector&lt;/code&gt;) that holds all captured records. Each &lt;code&gt;FakeLogRecord&lt;/code&gt; gives you direct access to &lt;code&gt;Level&lt;/code&gt;, &lt;code&gt;Message&lt;/code&gt;, &lt;code&gt;Exception&lt;/code&gt;, &lt;code&gt;EventId&lt;/code&gt;, and more.&lt;/p&gt;

&lt;p&gt;Example usage in a &lt;a href="https://github.com/Gramli/WeatherApi-VSA/blob/main/src/Tests/Weather.API.UnitTests/Features/AddFavorites/AddFavoriteHandlerTests.cs" rel="noopener noreferrer"&gt;project&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Log in xUnit Tests using ITestOutputHelper
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Ensures test logs are visible regardless of the test runner, improving debugging and test diagnostics.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In xUnit, &lt;code&gt;Console.WriteLine&lt;/code&gt; won't show up in your Test Explorer or on some CI logs. You must use &lt;code&gt;ITestOutputHelper&lt;/code&gt; to capture diagnostic data.&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;class&lt;/span&gt; &lt;span class="nc"&gt;CalculatorTests&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;ITestOutputHelper&lt;/span&gt; &lt;span class="n"&gt;_output&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;CalculatorTests&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ITestOutputHelper&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_output&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;output&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="n"&gt;Fact&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Add_ShouldLogExecution&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_output&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;"Starting addition test..."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="c1"&gt;// Test logic goes here&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;Example usage in a &lt;a href="https://github.com/Gramli/FileApi/blob/main/src/Tests/SystemTests/File.API.SystemTests/Tests/ConvertTests.cs" rel="noopener noreferrer"&gt;project&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Type-Safe [MemberData] using TheoryData
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Preserves type safety in parameterized tests, preventing runtime errors and improving code readability.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Using &lt;code&gt;IEnumerable&amp;lt;object[]&amp;gt;&lt;/code&gt; for parameterized tests is error-prone. xUnit provides &lt;code&gt;TheoryData&amp;lt;...&amp;gt;&lt;/code&gt;, which provides compile-time safety for your test parameters.&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;static&lt;/span&gt; &lt;span class="n"&gt;TheoryData&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ValidScenarios&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&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="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Active"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Pending"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Closed"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;false&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="n"&gt;Theory&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;MemberData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ValidScenarios&lt;/span&gt;&lt;span class="p"&gt;))]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;ShouldProcessStatus&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;id&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;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;isValid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Type-safe parameters, no object[] casting required!&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example usage in a &lt;a href="https://github.com/Gramli/FileApi/blob/main/src/Tests/UnitTests/File.Infrastructure.UnitTests/FileConversions/Converters/JsonToYamlFileConverterTests.cs" rel="noopener noreferrer"&gt;project&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use .Callback to Assert Internal Objects
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Allows you to inspect and assert on objects that are constructed inside the method under test and passed directly to a dependency.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Sometimes a method creates a complex object internally and passes it to a dependency. To verify that "hidden" object, use Moq's &lt;code&gt;.Callback&lt;/code&gt; to capture it.&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="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;mockRepo&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Mock&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IUserRepository&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;span class="n"&gt;User&lt;/span&gt; &lt;span class="n"&gt;capturedUser&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;mockRepo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;It&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsAny&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;User&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;Callback&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;capturedUser&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user&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;service&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;UserService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mockRepo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"test@test.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"password"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Now assert the internal state of the captured object&lt;/span&gt;
&lt;span class="n"&gt;Assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;NotNull&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;capturedUser&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;Assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"test@test.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;capturedUser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Email&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: &lt;code&gt;.Callback&lt;/code&gt; is useful for observing internal interactions, but relying on it heavily may signal a design smell. Prefer designs where important results are observable through return values or public behavior rather than mock interception.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>unittest</category>
      <category>dotnet</category>
      <category>csharp</category>
      <category>testing</category>
    </item>
    <item>
      <title>How Much AI-Generated Code Are We Actually Shipping to Production? My Reality So Far</title>
      <dc:creator>Daniel Balcarek</dc:creator>
      <pubDate>Tue, 24 Mar 2026 12:37:29 +0000</pubDate>
      <link>https://dev.to/gramli/reality-check-how-much-ai-generated-code-is-actually-used-in-production-fhj</link>
      <guid>https://dev.to/gramli/reality-check-how-much-ai-generated-code-is-actually-used-in-production-fhj</guid>
      <description>&lt;p&gt;Every day I see posts where people claim they barely code anymore or that in a few months most production code will be &lt;em&gt;vibe-coded&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Not only on dev.to, but also on Reddit, X and LinkedIn.&lt;/p&gt;

&lt;p&gt;My experience looks very different.&lt;/p&gt;

&lt;p&gt;In hobby projects, maybe &lt;strong&gt;75% of my code is AI-generated&lt;/strong&gt;.&lt;br&gt;
At work, I am still coding a lot, I think that there is like &lt;strong&gt;25% code generated by AI&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Don’t get me wrong, AI has definitely changed how I work. Whether it’s a hobby project or a sprint at the office, my workflow isn't the same as it was a year ago. However, many of the current predictions still feel heavily &lt;strong&gt;'AI-hyped'&lt;/strong&gt; compared to the boots-on-the-ground reality of software engineering.&lt;/p&gt;

&lt;p&gt;The difference becomes especially clear when comparing legacy systems with modern codebases.&lt;/p&gt;

&lt;h2&gt;
  
  
  Older Codebases
&lt;/h2&gt;

&lt;p&gt;We maintain several older solutions built on .NET Framework. Honestly, some parts are a mess. I’ve cursed the original authors many times.&lt;/p&gt;

&lt;p&gt;In these systems, I rarely use AI for new features because it simply isn’t very helpful.&lt;/p&gt;

&lt;p&gt;The problems are familiar to anyone working with legacy software:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;inconsistent architecture&lt;/li&gt;
&lt;li&gt;missing context&lt;/li&gt;
&lt;li&gt;hidden dependencies&lt;/li&gt;
&lt;li&gt;business rules scattered across the codebase&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Even developers who have worked on these systems for 10+ years are sometimes afraid to touch certain areas. In this environment, AI struggles because &lt;strong&gt;understanding the system matters more than generating syntax.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Newer Codebases
&lt;/h2&gt;

&lt;p&gt;In newer projects that follow good standards and clearer architecture, AI becomes much more useful.&lt;/p&gt;

&lt;p&gt;Here is where I actually use it regularly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;generating SQL (PostgreSQL functions, tables, indexes)&lt;/li&gt;
&lt;li&gt;creating unit test drafts for backend and frontend&lt;/li&gt;
&lt;li&gt;generating boilerplate code from prompts we keep inside the repository&lt;/li&gt;
&lt;li&gt;discussing performance ideas or refactoring options&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;However, for complex business features, I still write most of the code myself. Many tasks are too domain-specific to describe well in a prompt.&lt;/p&gt;

&lt;h2&gt;
  
  
  My Question to the Community
&lt;/h2&gt;

&lt;p&gt;I’m interested in how AI is used in production environments, not demos or hobby projects, but daily engineering work.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Which AI coding assistant do you use most often? Do you combine multiple tools?&lt;/li&gt;
&lt;li&gt;Roughly how much of your production code is AI-generated?&lt;/li&gt;
&lt;li&gt;Does AI help equally in legacy and modern codebases for you?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Maybe I’m behind the trend or maybe real-world usage simply looks different from online predictions.&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>ai</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>Node.js Dependency &amp; Version Management with nvm, npm ci and Registries</title>
      <dc:creator>Daniel Balcarek</dc:creator>
      <pubDate>Wed, 18 Mar 2026 14:44:28 +0000</pubDate>
      <link>https://dev.to/gramli/nodejs-dependency-version-management-with-nvm-npm-ci-and-registries-3o55</link>
      <guid>https://dev.to/gramli/nodejs-dependency-version-management-with-nvm-npm-ci-and-registries-3o55</guid>
      <description>&lt;p&gt;Working with multiple &lt;code&gt;Node.js&lt;/code&gt; projects often means switching between different Node versions, reinstalling dependencies and remembering various &lt;code&gt;nvm&lt;/code&gt; and &lt;code&gt;npm&lt;/code&gt; commands. This becomes especially painful when moving between legacy and modern codebases.&lt;/p&gt;

&lt;p&gt;This article provides a practical overview of my most common &lt;code&gt;Node.js&lt;/code&gt; workflow tasks: &lt;strong&gt;switching Node versions&lt;/strong&gt; using &lt;code&gt;nvm&lt;/code&gt;, &lt;strong&gt;cleaning and reinstalling dependencies&lt;/strong&gt; and using the &lt;strong&gt;correct npm installation strategy&lt;/strong&gt; for local development and CI/CD environments, and &lt;strong&gt;checking and updating dependencies&lt;/strong&gt; regularly.&lt;/p&gt;

&lt;p&gt;If you regularly jump between projects and forget the exact commands, this guide provides a simple, repeatable workflow you can reuse every day.&lt;/p&gt;




&lt;h2&gt;
  
  
  Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
Switching to a Different Node Version

&lt;ul&gt;
&lt;li&gt;Check the required version&lt;/li&gt;
&lt;li&gt;.nvmrc approach&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

Clean and Reinstall Dependencies

&lt;ul&gt;
&lt;li&gt;Installation Strategy Comparison&lt;/li&gt;
&lt;li&gt;Reinstall with locked versions&lt;/li&gt;
&lt;li&gt;Full reinstall&lt;/li&gt;
&lt;li&gt;Corrupted npm Cache&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Change registry&lt;/li&gt;

&lt;li&gt;

Checking and Updating Dependencies

&lt;ul&gt;
&lt;li&gt;Check outdated packages&lt;/li&gt;
&lt;li&gt;Update dependencies&lt;/li&gt;
&lt;li&gt;Recommended workflow&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;h2&gt;
  
  
  Switching to a Different Node Version
&lt;/h2&gt;

&lt;p&gt;First, determine which Node version the project requires.&lt;/p&gt;

&lt;h3&gt;
  
  
  Check the required version
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;package.json&lt;/code&gt; declares supported Node versions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"engines"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"node"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;gt;=18"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use the terminal to verify which Node.js version is active locally:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node &lt;span class="nt"&gt;-v&lt;/span&gt;
&lt;span class="c"&gt;# or&lt;/span&gt;
node &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once you know which version is required, list locally installed Node&lt;br&gt;
versions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nvm &lt;span class="nb"&gt;ls&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the required version is not installed locally:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nvm &lt;span class="nb"&gt;install&lt;/span&gt; &amp;lt;version&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then switch to the desired version:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nvm use &amp;lt;version&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;.nvmrc&lt;/code&gt; approach
&lt;/h3&gt;

&lt;p&gt;Instead of manually checking the required Node version in &lt;code&gt;package.json&lt;/code&gt;, you can define it using an &lt;code&gt;.nvmrc&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;The file contains only the Node version:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;18.19.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the file exists, switching versions becomes simpler:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nvm use
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running &lt;code&gt;nvm use&lt;/code&gt; inside a directory containing &lt;code&gt;.nvmrc&lt;/code&gt; switches to the specified version. Automatic switching requires shell integration (e.g., zsh hooks or tools like avn).&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;code&gt;.nvmrc&lt;/code&gt; vs &lt;code&gt;package.json&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;package.json&lt;/code&gt; and &lt;code&gt;.nvmrc&lt;/code&gt; solve related but different problems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the &lt;code&gt;engines&lt;/code&gt; field in &lt;code&gt;package.json&lt;/code&gt; declares supported Node versions&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;.nvmrc&lt;/code&gt; allows &lt;code&gt;nvm&lt;/code&gt; to automatically switch the runtime version&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Using both helps keep development environments consistent across projects and teams.&lt;/p&gt;




&lt;h2&gt;
  
  
  Clean and Reinstall Dependencies
&lt;/h2&gt;

&lt;p&gt;There are two main approaches to cleaning and reinstalling dependencies, depending on whether you want to &lt;strong&gt;preserve exact versions&lt;/strong&gt; or &lt;strong&gt;update them&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Reinstall with locked versions&lt;/strong&gt; – Reinstalls &lt;code&gt;node_modules&lt;/code&gt; while keeping the exact versions specified in &lt;code&gt;package-lock.json&lt;/code&gt;. This approach is recommended for teams and CI/CD environments, as it ensures reproducible builds.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Full reinstall&lt;/strong&gt; – Removes &lt;code&gt;node_modules&lt;/code&gt; and &lt;code&gt;package-lock.json&lt;/code&gt; before reinstalling all dependencies. This is useful when performing major dependency upgrades or resolving inconsistencies in the dependency graph.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Before diving into the commands, here’s a quick table summarizing when to use each strategy:&lt;/p&gt;

&lt;h3&gt;
  
  
  Installation Strategy Comparison
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scenario&lt;/th&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Lockfile Behavior&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Standard CI/CD Build&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;npm ci&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Unchanged (strictly enforced)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Team Development&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;npm ci&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Unchanged (strictly enforced)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Maintenance / Updates&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;npm install&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Updates within semver; rewrites lockfile&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Full Reset&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;rm -rf node_modules package-lock.json &amp;amp;&amp;amp; npm install&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Fully regenerated&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Reinstall with locked versions
&lt;/h3&gt;

&lt;p&gt;This approach is preferred for teams and CI/CD pipelines because it preserves exact dependency versions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm ci
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Why &lt;code&gt;npm ci&lt;/code&gt;?
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;  installs exact versions from &lt;code&gt;package-lock.json&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  produces reproducible builds&lt;/li&gt;
&lt;li&gt;  faster and deterministic installs&lt;/li&gt;
&lt;li&gt;  preferred for teams and CI environments&lt;/li&gt;
&lt;li&gt;  removes &lt;code&gt;node_modules&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt; &lt;code&gt;npm ci&lt;/code&gt; fails if &lt;code&gt;package.json&lt;/code&gt; and &lt;code&gt;package-lock.json&lt;/code&gt; are out of sync, preventing accidental dependency drift.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Full reinstall
&lt;/h3&gt;

&lt;p&gt;Full reinstall is useful when performing major dependency upgrades, switching Node versions or resolving dependency graph inconsistencies.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Unix/Bash&lt;/span&gt;
&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; node_modules &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;rm &lt;/span&gt;package-lock.json &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm i
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To completely reinstall dependencies, remove &lt;code&gt;node_modules&lt;/code&gt; and &lt;code&gt;package-lock.json&lt;/code&gt;, then run &lt;code&gt;npm install&lt;/code&gt;. This regenerates &lt;code&gt;package-lock.json&lt;/code&gt;, so review the changes carefully before committing them to Git to keep dependencies consistent across the team and CI environments.&lt;/p&gt;

&lt;p&gt;In team settings, avoid deleting &lt;code&gt;package-lock.json&lt;/code&gt; casually, as it updates the entire dependency graph for everyone working on the project.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;code&gt;rm -rf node_modules&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;Removes the &lt;code&gt;node_modules&lt;/code&gt; directory recursively.&lt;/p&gt;

&lt;h5&gt;
  
  
  Why delete &lt;code&gt;node_modules&lt;/code&gt;?
&lt;/h5&gt;

&lt;p&gt;It may contain:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  corrupted installs&lt;/li&gt;
&lt;li&gt;  incompatible binaries&lt;/li&gt;
&lt;li&gt;  dependency conflicts&lt;/li&gt;
&lt;li&gt;  leftover versions after upgrades&lt;/li&gt;
&lt;li&gt;  OS‑specific builds&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Removing it forces a clean rebuild.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;code&gt;rm package-lock.json&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;Deletes the lock file and forces a fresh dependency resolution.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why remove it?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;recompute dependency versions&lt;/li&gt;
&lt;li&gt;resolve a new dependency graph&lt;/li&gt;
&lt;li&gt;potentially upgrade sub-dependencies&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: Deleting &lt;code&gt;package-lock.json&lt;/code&gt; triggers a "fresh" resolution of all sub-dependencies. Only do this if you intend to update your entire dependency tree.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  &lt;code&gt;npm i&lt;/code&gt; (&lt;code&gt;npm install&lt;/code&gt;)
&lt;/h4&gt;

&lt;p&gt;This command:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  reads &lt;code&gt;package.json&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  resolves dependencies&lt;/li&gt;
&lt;li&gt;  downloads packages&lt;/li&gt;
&lt;li&gt;  creates a new &lt;code&gt;node_modules&lt;/code&gt; directory&lt;/li&gt;
&lt;li&gt;  generates a new &lt;code&gt;package-lock.json&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Corrupted npm Cache
&lt;/h3&gt;

&lt;p&gt;In rare cases, npm installs may fail due to a corrupted local cache. Typical symptoms include integrity errors, failed package extraction, or repeated installation failures.&lt;/p&gt;

&lt;h4&gt;
  
  
  Verify the cache (recommended first step)
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm cache verify
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This checks the cache integrity and removes invalid entries automatically.&lt;/p&gt;

&lt;h3&gt;
  
  
  Clean the cache
&lt;/h3&gt;

&lt;p&gt;If problems persist, you can clear the npm cache:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm cache clean &lt;span class="nt"&gt;--force&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;--force&lt;/code&gt; flag is required because npm protects the cache by default. After cleaning the cache, run &lt;code&gt;npm install&lt;/code&gt; again to rebuild dependencies.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt; Clearing the cache is rarely necessary and should only be used when troubleshooting installation issues. The npm cache is stored in &lt;code&gt;~/.npm&lt;/code&gt; (Linux/macOS) or &lt;code&gt;%AppData%/npm-cache&lt;/code&gt; (Windows).&lt;br&gt;
In modern npm versions (v7+), cache corruption is uncommon because npm performs strict integrity verification and automatically removes invalid cache entries.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Change registry
&lt;/h2&gt;

&lt;p&gt;In some environments especially when working with private packages or company infrastructure, you may need to configure npm to use a custom registry. This is typically done once when setting up a development machine.&lt;/p&gt;

&lt;h3&gt;
  
  
  Log in to a registry
&lt;/h3&gt;

&lt;p&gt;Authenticate against a custom registry:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm login &lt;span class="nt"&gt;--registry&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;registry-url&amp;gt; &lt;span class="nt"&gt;--auth-type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;web
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This stores authentication credentials (usually as an auth token) in your local npm configuration.&lt;/p&gt;

&lt;h3&gt;
  
  
  Set the default registry
&lt;/h3&gt;

&lt;p&gt;Configure npm to use a specific registry by default:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm config &lt;span class="nb"&gt;set &lt;/span&gt;registry &amp;lt;registry-url&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This updates your user-level npm configuration.&lt;/p&gt;

&lt;p&gt;You can verify the current registry with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm config get registry
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Show current npm configuration
&lt;/h3&gt;

&lt;p&gt;Display the active npm configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm config list
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This shows configuration values from all scopes (global, user, and project).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt; Some projects define a registry inside a project-level .npmrc, which overrides global settings.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Checking and Updating Dependencies
&lt;/h2&gt;

&lt;p&gt;Even with deterministic installs (&lt;code&gt;npm ci&lt;/code&gt;) and controlled Node.js versions (&lt;code&gt;nvm&lt;/code&gt;), dependencies should be checked and updated regularly. npm provides simple commands for this.&lt;/p&gt;

&lt;h3&gt;
  
  
  Check outdated packages
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm outdated
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example output:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Package   Current   Wanted   Latest
express   4.18.1    4.18.3   5.0.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Current&lt;/strong&gt; — installed version&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Wanted&lt;/strong&gt; — newest version allowed by &lt;code&gt;package.json&lt;/code&gt; (safe update)&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Latest&lt;/strong&gt; — newest published version (may contain breaking changes)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Update dependencies
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm update
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This updates packages &lt;strong&gt;within the semver range&lt;/strong&gt; defined in &lt;code&gt;package.json&lt;/code&gt;, applying patches and minor updates without upgrading to breaking major versions. It also regenerates &lt;code&gt;package-lock.json&lt;/code&gt; to reflect the new resolved versions.&lt;/p&gt;

&lt;p&gt;Update a single package:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm update express
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Recommended workflow
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm outdated
npm update
npm ci
npm &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This keeps dependencies up to date while maintaining reproducible builds.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>node</category>
      <category>npm</category>
      <category>programming</category>
    </item>
    <item>
      <title>Echoes of Experience: Don’t Be "Too Senior" to Ask</title>
      <dc:creator>Daniel Balcarek</dc:creator>
      <pubDate>Wed, 11 Mar 2026 16:51:21 +0000</pubDate>
      <link>https://dev.to/gramli/echoes-of-experience-dont-be-too-senior-to-ask-4ake</link>
      <guid>https://dev.to/gramli/echoes-of-experience-dont-be-too-senior-to-ask-4ake</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/wecoded-2026"&gt;2026 WeCoded Challenge&lt;/a&gt;: Echoes of Experience&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;As a junior developer, I was afraid to ask questions.&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;As a senior developer, I stopped questioning the obvious.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Both mistakes cost me more hours, sometimes days than I care to admit.&lt;/p&gt;

&lt;p&gt;One lesson I had to learn the hard way is this: &lt;strong&gt;ask the “stupid” questions&lt;/strong&gt;. Yes, you read that right.&lt;/p&gt;

&lt;p&gt;For most of my career, I hesitated to ask questions that felt too obvious. I didn’t want to look inexperienced, so sometimes I pretended I understood a topic or stayed quiet during discussions.&lt;/p&gt;

&lt;p&gt;Today, I see it differently. In most cases, it’s far better to ask. &lt;strong&gt;A single question can reveal hidden assumptions, prevent future production problems, or even lead to a better solution.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Over the years, three particular experiences have stayed with me, each reinforcing this lesson in its own way:&lt;/p&gt;

&lt;h2&gt;
  
  
  Is the Server Running?
&lt;/h2&gt;

&lt;p&gt;It was a long time ago (Back when Windows XP was refusing to die in corporate offices). I was a university student when I got my first job as a junior C# developer. Most of my work involved creating new controls for WinForms applications.&lt;/p&gt;

&lt;p&gt;One day my mentor, a senior C# developer in the company, gave me a new task. I needed to connect our application to an internal service using WCF. I received the connection details and was told that the service was already running.&lt;/p&gt;

&lt;p&gt;The first thing I did was check the address in the browser. Normally, a WCF service shows a metadata page or some information confirming that the service is alive. But this time I didn’t see anything like that.&lt;/p&gt;

&lt;p&gt;Still, my mentor said the service was running, so I assumed I must be doing something wrong. I didn’t want to bother him with what I thought might be a stupid question.&lt;/p&gt;

&lt;p&gt;So I started implementing the client code. When I tried to call the service, I got an error.&lt;/p&gt;

&lt;p&gt;Hmm… what went wrong?&lt;/p&gt;

&lt;p&gt;I changed the configuration slightly and tried again.&lt;/p&gt;

&lt;p&gt;Same error.&lt;/p&gt;

&lt;p&gt;I adjusted the implementation.&lt;/p&gt;

&lt;p&gt;Same error again.&lt;/p&gt;

&lt;p&gt;This went on for three days. I kept experimenting with configuration, bindings, and client code, but nothing worked. Eventually, I even asked one of my university professors to look at the code with me. After reviewing it, he said the code looked fine.&lt;/p&gt;

&lt;p&gt;Then he asked a simple question:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;“Is the server actually running?”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;After that, I finally emailed my mentor.&lt;/p&gt;

&lt;p&gt;His reply was short:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;“No, it actually isn’t running.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If I had simply asked that “stupid” question at the beginning, I could have saved three days of work.&lt;/p&gt;

&lt;h2&gt;
  
  
  It Was Just HTTP
&lt;/h2&gt;

&lt;p&gt;At that time, I was a senior developer in a mid-sized company, maintaining backend APIs and the administration system used across the company.&lt;/p&gt;

&lt;p&gt;One day I received what seemed like a simple task: connect to a new external service and build an administration interface for it. We used Azure as our identity provider, so I first implemented the code to obtain an access token and then added the client code to call the service.&lt;/p&gt;

&lt;p&gt;When everything was ready, I started debugging.&lt;/p&gt;

&lt;p&gt;Authentication error.&lt;/p&gt;

&lt;p&gt;Hmm… what did I do wrong?&lt;/p&gt;

&lt;p&gt;I stopped the debugger, reviewed the code, and tried again.&lt;/p&gt;

&lt;p&gt;Same authentication error.&lt;/p&gt;

&lt;p&gt;I checked logs, tracing, configuration, and infrastructure settings. Everything looked correct. After a few hours of debugging, I asked another senior developer for help. He reviewed the code but didn’t see any problem either. Together we spent another hour searching for the root cause, still nothing.&lt;/p&gt;

&lt;p&gt;The next day, another senior joined us. Three experienced developers were now standing in front of my screen, brainstorming possible causes.&lt;/p&gt;

&lt;p&gt;While we were discussing authentication flows and token validation, one of our junior developers walked by, glanced at the screen, and casually asked:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;“Why are you using HTTP instead of HTTPS?”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;That question hit instantly.&lt;/p&gt;

&lt;p&gt;Three senior developers analyzing authentication and security were stopped by a junior who simply noticed the protocol in the URL, revealing how our experience had made us blind to the simplest explanation.&lt;/p&gt;

&lt;p&gt;Changing &lt;code&gt;http&lt;/code&gt; to &lt;code&gt;https&lt;/code&gt; fixed the problem immediately.&lt;/p&gt;

&lt;p&gt;If we had asked that simple question earlier and checked the configuration properly, we could have saved hours of work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Performance
&lt;/h2&gt;

&lt;p&gt;This happened recently while I was working as a Senior Engineer in a corporate environment, developing a large business application with several critical components.&lt;/p&gt;

&lt;p&gt;One of those components was designed with a strong safety-first approach. It was reliable and almost bulletproof, but also very slow. We were already preparing major architectural changes for the next major release, but we also needed a significant performance improvement for an upcoming minor version.&lt;/p&gt;

&lt;p&gt;I started by proposing safe optimizations that wouldn’t risk breaking existing logic: adding database indexes, introducing bulk operations, and improving several inefficient parts of the process. After implementation, the performance improved by about one third, but our goal was at least three times faster.&lt;/p&gt;

&lt;p&gt;We discussed the problem repeatedly with other senior engineers, brainstorming new optimizations. Every idea helped a little, but none came close to the required performance target.&lt;/p&gt;

&lt;p&gt;While we were discussing this in our open office, one of our business analysts a non-technical colleague joined the conversation and asked a simple question:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;“Do you really need to modify the existing process?”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;That question changed everything.&lt;/p&gt;

&lt;p&gt;Instead of optimizing the old logic further, I realized we could create a completely separate processing flow: new code designed purely for bulk execution, running independently. If anything failed, we could simply roll back the transaction and fall back to the original safe process.&lt;/p&gt;

&lt;p&gt;The result was fast, safe, and significantly simpler than what we had been trying to achieve.&lt;/p&gt;

&lt;p&gt;A non-technical colleague, just by listening to our discussion, helped us find the breakthrough we had been missing.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Lesson Behind the Stories
&lt;/h2&gt;

&lt;p&gt;Looking back, these situations happened at very different stages of my career, as a junior developer, as a senior engineer and while working with experienced teams on complex systems. Yet &lt;strong&gt;the lesson was always the same.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In the first story, I was afraid to ask a question because I didn’t want to look inexperienced. In the second, several senior developers overlooked a simple detail because we assumed the complex parts must be wrong. In the third, we were so focused on improving the existing solution that it took a non-technical perspective to help us rethink the problem entirely.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Experience teaches us how to solve difficult problems, but it can also make us blind to simple questions.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Explaining a problem out loud, asking something that feels obvious, or inviting someone outside the technical context into the discussion often reveals what hours of debugging cannot.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sometimes the most valuable question in the room is the one that sounds the simplest.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;So today, I ask sooner. I discuss more. I never underestimate the power of the obvious, because usually, the “stupid” question is the only thing standing between you and the solution.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>wecoded</category>
      <category>dei</category>
      <category>career</category>
    </item>
    <item>
      <title>What’s the Worst Advice AI Has Given You? Here’s Mine.</title>
      <dc:creator>Daniel Balcarek</dc:creator>
      <pubDate>Wed, 04 Mar 2026 15:02:08 +0000</pubDate>
      <link>https://dev.to/gramli/whats-the-worst-advice-ai-has-given-you-heres-mine-58j4</link>
      <guid>https://dev.to/gramli/whats-the-worst-advice-ai-has-given-you-heres-mine-58j4</guid>
      <description>&lt;p&gt;I recently saw a meme about terrible legacy code on platform X and it gave me an idea for a discussion topic.&lt;/p&gt;

&lt;p&gt;A year ago, the classic developer question was: &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“What’s the worst code you’ve ever seen?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But our day-to-day work has changed. Maybe the real question now is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“What is the worst suggestion AI has ever given you?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I’ll start.&lt;/p&gt;

&lt;p&gt;I’ve happily survived plenty of questionable code: from “fast hotfixes” that didn’t even touch the root cause, to refactorings that added more complexity than my 15-years-younger self on OOP steroids.&lt;/p&gt;

&lt;p&gt;But this happened about a year ago and it still sticks in my mind:&lt;/p&gt;

&lt;h2&gt;
  
  
  API key in a public Docker image
&lt;/h2&gt;

&lt;p&gt;I was working on a GitHub Action that builds a Docker image from my .NET REST API, pushes it to Docker Hub as a public image and then deploys it to Azure. Pretty straightforward, right?&lt;/p&gt;

&lt;p&gt;There was one small catch: the API uses a private API key to communicate with a third-party service.&lt;/p&gt;

&lt;p&gt;So I asked ChatGPT for advice.&lt;/p&gt;

&lt;p&gt;Its suggestion:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“You can store this API key as an environment variable in your Docker image.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Wait… what?&lt;/p&gt;

&lt;p&gt;Put a private API key inside a public Docker image?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;To be clear, environment variables themselves are fine.&lt;br&gt;
The problem was baking the secret into the image during build time, which would expose it to anyone pulling or inspecting the public image.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I explained this to ChatGPT.&lt;/p&gt;

&lt;p&gt;It responded with the classic:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“You are right!”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;…and suggested storing it securely in Azure.&lt;/p&gt;

&lt;p&gt;End of the story?&lt;/p&gt;

&lt;p&gt;Of course not.&lt;/p&gt;

&lt;p&gt;Just a few messages later, in the same context window, ChatGPT again suggested putting the private API key into the public Docker image as an environment variable.&lt;/p&gt;

&lt;p&gt;That was the moment I realized:&lt;/p&gt;

&lt;p&gt;AI isn’t production-ready yet, at least for security advice. 😄&lt;/p&gt;




&lt;h2&gt;
  
  
  The interesting shift
&lt;/h2&gt;

&lt;p&gt;We used to review junior developers’ code carefully.&lt;/p&gt;

&lt;p&gt;Now we also need to review code written by something that sounds like a senior engineer but occasionally behaves like an intern on their first day.&lt;/p&gt;




&lt;h2&gt;
  
  
  Discussion
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;What’s the most ridiculous suggestion AI or ChatGPT has ever given you?&lt;/li&gt;
&lt;li&gt;Do you review AI-generated code differently than human-written code?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I’d love to hear real examples from the community.&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>ai</category>
      <category>chatgpt</category>
      <category>security</category>
    </item>
    <item>
      <title>.NET: CopyToAsync vs WriteAsync: The Benchmark You Didn’t Know You Needed</title>
      <dc:creator>Daniel Balcarek</dc:creator>
      <pubDate>Mon, 02 Mar 2026 12:31:27 +0000</pubDate>
      <link>https://dev.to/gramli/net-copytoasync-vs-writeasync-the-benchmark-you-didnt-know-you-needed-1g39</link>
      <guid>https://dev.to/gramli/net-copytoasync-vs-writeasync-the-benchmark-you-didnt-know-you-needed-1g39</guid>
      <description>&lt;p&gt;I had a task where one small part involved writing a byte array to a &lt;code&gt;MemoryStream&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Since this was a trivial task, I let Copilot generate the code. But Copilot used &lt;code&gt;CopyToAsync&lt;/code&gt; instead of &lt;code&gt;WriteAsync&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That raised a question, so I asked why.&lt;/p&gt;

&lt;p&gt;It kept trying to convince me that &lt;code&gt;CopyToAsync&lt;/code&gt; was a better fit for this scenario (a 1 MB file), and I quote:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;&lt;code&gt;CopyToAsync&lt;/code&gt; might offer better performance due to its internal optimizations for copying data between streams. The performance difference between these two approaches can be minimal for small to moderately sized data.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;According to Copilot:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Small to medium data: 12 KB – 32 KB&lt;/li&gt;
&lt;li&gt;Large data: 5 MB – 10 MB&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But here’s the important detail:&lt;/p&gt;

&lt;p&gt;I wasn’t copying between two independent streams.&lt;br&gt;
I already had a byte array in memory.&lt;/p&gt;

&lt;p&gt;In that case, wrapping it in a &lt;code&gt;MemoryStream&lt;/code&gt; just to call &lt;code&gt;CopyToAsync&lt;/code&gt; adds an extra abstraction layer. So I was skeptical.&lt;/p&gt;

&lt;p&gt;Copilot was confident.&lt;br&gt;
I was suspicious.&lt;/p&gt;

&lt;p&gt;As a responsible developer, &lt;strong&gt;I couldn’t sleep until I proved my point&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;So I wrote a benchmark.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Benchmark
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;BenchmarkDotNet v0.15.8, Windows 10 &lt;span class="o"&gt;(&lt;/span&gt;10.0.19045.6466/22H2/2022Update&lt;span class="o"&gt;)&lt;/span&gt;
Intel Core i5-6400 CPU 2.70GHz &lt;span class="o"&gt;(&lt;/span&gt;Skylake&lt;span class="o"&gt;)&lt;/span&gt;, 1 CPU, 4 logical and 4 physical cores
.NET SDK 10.0.102
  &lt;span class="o"&gt;[&lt;/span&gt;Host]    : .NET 10.0.2 &lt;span class="o"&gt;(&lt;/span&gt;10.0.2, 10.0.225.61305&lt;span class="o"&gt;)&lt;/span&gt;, X64 RyuJIT x86-64-v3
  .NET 10.0 : .NET 10.0.2 &lt;span class="o"&gt;(&lt;/span&gt;10.0.2, 10.0.225.61305&lt;span class="o"&gt;)&lt;/span&gt;, X64 RyuJIT x86-64-v3

&lt;span class="nv"&gt;Job&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;.NET 10.0  &lt;span class="nv"&gt;Runtime&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;.NET 10.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;I tested four methods:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;MemoryStream.CopyToAsync&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;MemoryStream.CopyTo&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;MemoryStream.WriteAsync&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;MemoryStream.Write&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Across file sizes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;12 KB&lt;/li&gt;
&lt;li&gt;32 KB&lt;/li&gt;
&lt;li&gt;5 MB&lt;/li&gt;
&lt;li&gt;10 MB&lt;/li&gt;
&lt;li&gt;25 MB&lt;/li&gt;
&lt;li&gt;50 MB&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  The Code
&lt;/h2&gt;

&lt;p&gt;The benchmark uses &lt;strong&gt;BenchmarkDotNet&lt;/strong&gt; and tests copying from a byte array into a &lt;code&gt;MemoryStream&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;using&lt;/span&gt; &lt;span class="nn"&gt;BenchmarkDotNet.Attributes&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;BenchmarkDotNet.Jobs&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;GeneralBenchmark.CopyToAndWrite&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;SimpleJob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RuntimeMoniker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Net90&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Orderer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BenchmarkDotNet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SummaryOrderPolicy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Declared&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;MemoryDiagnoser&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CopyToAndWriteBenchmark&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IEnumerable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;CopyToAndWriteBenchmarkData&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;Data&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;CopyToAndWriteBenchmarkData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1024&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="k"&gt;yield&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;CopyToAndWriteBenchmarkData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1024&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="m"&gt;32&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;CopyToAndWriteBenchmarkData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1024&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="m"&gt;1024&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;CopyToAndWriteBenchmarkData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1024&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="m"&gt;1024&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;CopyToAndWriteBenchmarkData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1024&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="m"&gt;1024&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="m"&gt;25&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;CopyToAndWriteBenchmarkData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1024&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="m"&gt;1024&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="m"&gt;50&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="n"&gt;Benchmark&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;ArgumentsSource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Data&lt;/span&gt;&lt;span class="p"&gt;))]&lt;/span&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="nf"&gt;MemoryStream_CopyTo_Async&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CopyToAndWriteBenchmarkData&lt;/span&gt; &lt;span class="n"&gt;data&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;outerStream&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;MemoryStream&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;innerStream&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;MemoryStream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ByteData&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;innerStream&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CopyToAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;outerStream&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="n"&gt;Benchmark&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;ArgumentsSource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Data&lt;/span&gt;&lt;span class="p"&gt;))]&lt;/span&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="nf"&gt;ByteArray_Write_Async&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CopyToAndWriteBenchmarkData&lt;/span&gt; &lt;span class="n"&gt;data&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;outerStream&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;MemoryStream&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;outerStream&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ByteData&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="n"&gt;Benchmark&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;ArgumentsSource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Data&lt;/span&gt;&lt;span class="p"&gt;))]&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;MemoryStream_CopyTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CopyToAndWriteBenchmarkData&lt;/span&gt; &lt;span class="n"&gt;data&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;outerStream&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;MemoryStream&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;innerStream&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;MemoryStream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ByteData&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;innerStream&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CopyTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;outerStream&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="n"&gt;Benchmark&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;ArgumentsSource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Data&lt;/span&gt;&lt;span class="p"&gt;))]&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;ByteArray_Write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CopyToAndWriteBenchmarkData&lt;/span&gt; &lt;span class="n"&gt;data&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;outerStream&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;MemoryStream&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="n"&gt;outerStream&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ByteData&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="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://github.com/Gramli/Gramli.Framework/blob/main/src/GeneralBenchmark/CopyToAndWrite/CopyToAndWriteBenchmark.cs" rel="noopener noreferrer"&gt;Link To Code&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Results
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;data&lt;/th&gt;
&lt;th&gt;Mean&lt;/th&gt;
&lt;th&gt;Allocated&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;MemoryStream_CopyTo_Async&lt;/td&gt;
&lt;td&gt;12 KB&lt;/td&gt;
&lt;td&gt;951.9 ns&lt;/td&gt;
&lt;td&gt;12.15 KB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ByteArray_Write_Async&lt;/td&gt;
&lt;td&gt;12 KB&lt;/td&gt;
&lt;td&gt;916.6 ns&lt;/td&gt;
&lt;td&gt;12.09 KB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MemoryStream_CopyTo&lt;/td&gt;
&lt;td&gt;12 KB&lt;/td&gt;
&lt;td&gt;905.7 ns&lt;/td&gt;
&lt;td&gt;12.15 KB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ByteArray_Write&lt;/td&gt;
&lt;td&gt;12 KB&lt;/td&gt;
&lt;td&gt;891.1 ns&lt;/td&gt;
&lt;td&gt;12.09 KB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MemoryStream_CopyTo_Async&lt;/td&gt;
&lt;td&gt;32 KB&lt;/td&gt;
&lt;td&gt;2,535.8 ns&lt;/td&gt;
&lt;td&gt;32.15 KB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ByteArray_Write_Async&lt;/td&gt;
&lt;td&gt;32 KB&lt;/td&gt;
&lt;td&gt;2,511.8 ns&lt;/td&gt;
&lt;td&gt;32.09 KB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MemoryStream_CopyTo&lt;/td&gt;
&lt;td&gt;32 KB&lt;/td&gt;
&lt;td&gt;2,456.8 ns&lt;/td&gt;
&lt;td&gt;32.15 KB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ByteArray_Write&lt;/td&gt;
&lt;td&gt;32 KB&lt;/td&gt;
&lt;td&gt;2,461.5 ns&lt;/td&gt;
&lt;td&gt;32.09 KB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MemoryStream_CopyTo_Async&lt;/td&gt;
&lt;td&gt;5120 KB&lt;/td&gt;
&lt;td&gt;1,820,022.6 ns&lt;/td&gt;
&lt;td&gt;5120.27 KB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ByteArray_Write_Async&lt;/td&gt;
&lt;td&gt;5120 KB&lt;/td&gt;
&lt;td&gt;1,826,398.4 ns&lt;/td&gt;
&lt;td&gt;5120.21 KB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MemoryStream_CopyTo&lt;/td&gt;
&lt;td&gt;5120 KB&lt;/td&gt;
&lt;td&gt;1,819,132.4 ns&lt;/td&gt;
&lt;td&gt;5120.27 KB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ByteArray_Write&lt;/td&gt;
&lt;td&gt;5120 KB&lt;/td&gt;
&lt;td&gt;1,807,830.4 ns&lt;/td&gt;
&lt;td&gt;5120.21 KB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MemoryStream_CopyTo_Async&lt;/td&gt;
&lt;td&gt;10240 KB&lt;/td&gt;
&lt;td&gt;3,588,365.1 ns&lt;/td&gt;
&lt;td&gt;10240.27 KB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ByteArray_Write_Async&lt;/td&gt;
&lt;td&gt;10240 KB&lt;/td&gt;
&lt;td&gt;3,545,750.1 ns&lt;/td&gt;
&lt;td&gt;10240.21 KB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MemoryStream_CopyTo&lt;/td&gt;
&lt;td&gt;10240 KB&lt;/td&gt;
&lt;td&gt;3,528,154.4 ns&lt;/td&gt;
&lt;td&gt;10240.27 KB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ByteArray_Write&lt;/td&gt;
&lt;td&gt;10240 KB&lt;/td&gt;
&lt;td&gt;3,530,808.4 ns&lt;/td&gt;
&lt;td&gt;10240.21 KB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MemoryStream_CopyTo_Async&lt;/td&gt;
&lt;td&gt;25600 KB&lt;/td&gt;
&lt;td&gt;8,796,565.6 ns&lt;/td&gt;
&lt;td&gt;25600.26 KB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ByteArray_Write_Async&lt;/td&gt;
&lt;td&gt;25600 KB&lt;/td&gt;
&lt;td&gt;8,808,362.9 ns&lt;/td&gt;
&lt;td&gt;25600.2 KB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MemoryStream_CopyTo&lt;/td&gt;
&lt;td&gt;25600 KB&lt;/td&gt;
&lt;td&gt;8,815,669.9 ns&lt;/td&gt;
&lt;td&gt;25600.26 KB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ByteArray_Write&lt;/td&gt;
&lt;td&gt;25600 KB&lt;/td&gt;
&lt;td&gt;8,787,913.1 ns&lt;/td&gt;
&lt;td&gt;25600.2 KB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MemoryStream_CopyTo_Async&lt;/td&gt;
&lt;td&gt;51200 KB&lt;/td&gt;
&lt;td&gt;8,704,199.7 ns&lt;/td&gt;
&lt;td&gt;51200.26 KB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ByteArray_Write_Async&lt;/td&gt;
&lt;td&gt;51200 KB&lt;/td&gt;
&lt;td&gt;8,718,245.6 ns&lt;/td&gt;
&lt;td&gt;51200.2 KB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MemoryStream_CopyTo&lt;/td&gt;
&lt;td&gt;51200 KB&lt;/td&gt;
&lt;td&gt;8,738,011.5 ns&lt;/td&gt;
&lt;td&gt;51200.26 KB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ByteArray_Write&lt;/td&gt;
&lt;td&gt;51200 KB&lt;/td&gt;
&lt;td&gt;8,721,815.7 ns&lt;/td&gt;
&lt;td&gt;51200.2 KB&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The difference? &lt;strong&gt;Statistically negligible&lt;/strong&gt; for both mean execution time and allocations.&lt;br&gt;
&lt;code&gt;Write&lt;/code&gt; avoids creating the extra &lt;code&gt;MemoryStream&lt;/code&gt; wrapper, but the practical impact remains minimal even at 50 MB.&lt;/p&gt;

&lt;p&gt;This doesn’t mean &lt;code&gt;CopyTo&lt;/code&gt; is bad. It’s the correct API to use when copying between two arbitrary streams.&lt;/p&gt;

&lt;p&gt;But it is not “magically faster.”&lt;/p&gt;

&lt;p&gt;In fact, when your source is already a byte array, &lt;code&gt;Write&lt;/code&gt; is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Simpler&lt;/li&gt;
&lt;li&gt;More direct&lt;/li&gt;
&lt;li&gt;Semantically correct&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Real Lesson
&lt;/h2&gt;

&lt;p&gt;Yeah, I put all this effort into investigating a single line of abstraction suggested by Copilot and even wrote a post about it. But this wasn’t about proving Copilot wrong, it was about something more important:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AI suggestions are hypotheses, not conclusions.&lt;/strong&gt; Measure when it matters. Trust evidence over assumptions.&lt;/p&gt;

&lt;p&gt;If you already have the buffer, use the buffer API.&lt;/p&gt;

&lt;p&gt;The right choice depends on the scenario, not on AI suggestions about “internal optimizations.”&lt;/p&gt;

&lt;p&gt;And yes… &lt;strong&gt;now I can sleep&lt;/strong&gt;.&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>csharp</category>
      <category>performance</category>
      <category>ai</category>
    </item>
    <item>
      <title>Clean Architecture in .NET: Real-World Pros, Cons and Trade-offs</title>
      <dc:creator>Daniel Balcarek</dc:creator>
      <pubDate>Tue, 24 Feb 2026 13:15:34 +0000</pubDate>
      <link>https://dev.to/gramli/clean-architecture-in-net-real-world-pros-cons-and-trade-offs-3m9i</link>
      <guid>https://dev.to/gramli/clean-architecture-in-net-real-world-pros-cons-and-trade-offs-3m9i</guid>
      <description>&lt;p&gt;I have experience with &lt;strong&gt;Clean Architecture&lt;/strong&gt; across multiple projects, both when creating new applications and when maintaining existing ones. Based on this experience, I decided to write about the &lt;strong&gt;pros and cons&lt;/strong&gt; of using Clean Architecture, &lt;strong&gt;focusing on real-world examples&lt;/strong&gt; rather than repeating the same theoretical descriptions found in many articles on this topic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
Pros

&lt;ul&gt;
&lt;li&gt;UI, Framework, and Database Independence&lt;/li&gt;
&lt;li&gt;Highly Testable&lt;/li&gt;
&lt;li&gt;Maintainability and Extensibility&lt;/li&gt;
&lt;li&gt;Clear Boundaries&lt;/li&gt;
&lt;li&gt;Scalability&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

Cons

&lt;ul&gt;
&lt;li&gt;Understanding and Complexity&lt;/li&gt;
&lt;li&gt;Heavy Structure&lt;/li&gt;
&lt;li&gt;Performance&lt;/li&gt;
&lt;li&gt;Initial Setup Time&lt;/li&gt;
&lt;li&gt;Data Model Duplication&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

Conclusion &lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Pros
&lt;/h2&gt;

&lt;p&gt;The advantages of Clean Architecture go beyond theoretical principles. In practice, it improves maintainability, testability, and scalability, especially in applications with complex business logic. The following sections highlight &lt;strong&gt;real-world examples&lt;/strong&gt; of how these benefits are realized in day-to-day development.&lt;/p&gt;

&lt;h3&gt;
  
  
  UI/Framework/Database Independence
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Business logic is kept at the core, enabling external frameworks and databases to be changed with minimal disruption.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Many articles mention this advantage, but I think the &lt;strong&gt;common explanation is misleading&lt;/strong&gt;. How often do you actually change the database, framework, or UI? Sure, the Core is independent of these layers, so in theory you could change them, but the real advantage is more specific:&lt;/p&gt;

&lt;p&gt;Thanks to the Infrastructure layer and abstractions, you can &lt;strong&gt;easily replace an external library&lt;/strong&gt; or &lt;strong&gt;switch to a different external service&lt;/strong&gt; without touching the Core, because the implementation is hidden behind an abstraction and resides in the Infrastructure layer.&lt;/p&gt;

&lt;h4&gt;
  
  
  Independence in Practice
&lt;/h4&gt;

&lt;p&gt;We had a requirement to read Excel files, so we chose a library and implemented the feature.&lt;/p&gt;

&lt;p&gt;Later, the requirements changed and we needed to support password protected Excel files, which our original library could not handle. &lt;/p&gt;

&lt;p&gt;Because we followed Clean Architecture, the Excel library was used only in the Infrastructure layer. We were able to replace the library by updating that layer only, while the business logic in the Core layer remained unchanged.&lt;/p&gt;




&lt;h3&gt;
  
  
  Highly Testable
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Business logic in Clean Architecture is isolated from external concerns, making unit testing easy and straightforward.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This benefit is straightforward and one of the strongest advantages of Clean Architecture. Thanks to &lt;strong&gt;separation of concerns and extensive use of abstractions&lt;/strong&gt; you can &lt;strong&gt;easily mock and test&lt;/strong&gt; every layer independently especially the Core layer.&lt;/p&gt;

&lt;h4&gt;
  
  
  Highly Testable In practice
&lt;/h4&gt;

&lt;p&gt;This unit test demonstrates how Clean Architecture makes the system easy to test in isolation.&lt;/p&gt;

&lt;p&gt;The request handler lives in the Core layer, because in a REST API the core application logic is implemented in request handlers and business services.&lt;/p&gt;

&lt;p&gt;The handler depends only on abstractions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;IRequestValidator&amp;lt;T&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;IWeatherService&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because these are interfaces, we can mock all dependencies in the test and fully control their behavior.&lt;/p&gt;

&lt;p&gt;Although implementation of &lt;code&gt;IWeatherService&lt;/code&gt; belongs to the Infrastructure layer and calls an external weather API, the handler does not depend on any HTTP client or third-party library directly. Thanks to this abstraction, the handler can be tested without making real HTTP calls.&lt;/p&gt;

&lt;p&gt;As a result:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The test is fast&lt;/li&gt;
&lt;li&gt;The test is deterministic&lt;/li&gt;
&lt;li&gt;All success and failure scenarios can be covered easily
&lt;/li&gt;
&lt;/ul&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;class&lt;/span&gt; &lt;span class="nc"&gt;GetCurrentWeatherHandlerTests&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;Mock&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IRequestValidator&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;GetCurrentWeatherQuery&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_getCurrentWeatherQueryValidatorMock&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;Mock&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IRequestValidator&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;CurrentWeatherDto&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_currentWeatherValidatorMock&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;Mock&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IWeatherService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_weatherServiceMock&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;IStatusRequestHandler&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;CurrentWeatherDto&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;GetCurrentWeatherQuery&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_uut&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;GetCurrentWeatherHandlerTests&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_getCurrentWeatherQueryValidatorMock&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;_currentWeatherValidatorMock&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;_weatherServiceMock&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="n"&gt;_uut&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;GetCurrentWeatherHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;_getCurrentWeatherQueryValidatorMock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
            &lt;span class="n"&gt;_currentWeatherValidatorMock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
            &lt;span class="n"&gt;_weatherServiceMock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
            &lt;span class="n"&gt;_loggerMock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Object&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="n"&gt;Fact&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&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="nf"&gt;Success&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;//Arrange&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;getCurrentWeatherQuery&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;GetCurrentWeatherQuery&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="m"&gt;1&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;currentWeather&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;CurrentWeatherDto&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="n"&gt;_getCurrentWeatherQueryValidatorMock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Validate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;It&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsAny&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;GetCurrentWeatherQuery&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;())).&lt;/span&gt;&lt;span class="nf"&gt;Returns&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;RequestValidationResult&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;IsValid&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

        &lt;span class="c1"&gt;//Act&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;result&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;_uut&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;HandleAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;getCurrentWeatherQuery&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="n"&gt;None&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;//Assert&lt;/span&gt;
        &lt;span class="n"&gt;Assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HandlerStatusCode&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="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusCode&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;_getCurrentWeatherQueryValidatorMock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Verify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Validate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;It&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Is&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;GetCurrentWeatherQuery&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;getCurrentWeatherQuery&lt;/span&gt;&lt;span class="p"&gt;))),&lt;/span&gt; &lt;span class="n"&gt;Times&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Once&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Maintainability and Extensibility
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Clean Architecture provides a clear structure that keeps changes isolated and predictable, improving maintainability and extensibility.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When Clean Architecture is followed together with SOLID principles &lt;strong&gt;each part of the system has one clear responsibility&lt;/strong&gt; so when you are fixing a bug or implementing a new feature you are &lt;strong&gt;working in isolated part&lt;/strong&gt; of the solution so you usually don’t break others.&lt;/p&gt;

&lt;h4&gt;
  
  
  Maintainability in practice
&lt;/h4&gt;

&lt;p&gt;We had a bug related to user login.&lt;/p&gt;

&lt;p&gt;Because responsibilities were clearly defined, we knew the issue belonged to the API layer, where the login endpoint and request handling logic live.&lt;/p&gt;

&lt;p&gt;We were able to quickly locate the relevant code, fix the bug, and deploy the change without affecting other parts of the system.&lt;/p&gt;

&lt;h4&gt;
  
  
  Extensibility in practice
&lt;/h4&gt;

&lt;p&gt;We needed to add a new endpoint for fetching data from the database.&lt;/p&gt;

&lt;p&gt;Following Clean Architecture:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I added a new endpoint in the API layer&lt;/li&gt;
&lt;li&gt;Created a new handler in the Core layer to handle the use case&lt;/li&gt;
&lt;li&gt;Implemented a new query in the Infrastructure layer to access the database through a repository&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No existing logic had to be modified, and the structure made it obvious where each piece of code belonged.&lt;/p&gt;




&lt;h3&gt;
  
  
  Clear Boundaries
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Establishes well-defined boundaries between components, which helps teams divide work effectively.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Each part of the system has a clear responsibility&lt;/strong&gt; and components communicate through well defined abstractions. As a result, critical changes in the Infrastructure layer do not necessarily impact the Core layer for example.&lt;/p&gt;

&lt;h4&gt;
  
  
  Clear Boundaries in practice
&lt;/h4&gt;

&lt;p&gt;We were integrated with an external service that was part of a very old legacy system. &lt;/p&gt;

&lt;p&gt;Over time, the team owning that service rewrote it using a new technology stack and the entire communication contract changed. As a result, we had to adapt our system to work with the new contract.&lt;/p&gt;

&lt;p&gt;All of the changes were done in the Infrastructure layer, where integration and communication with the external service are handled.&lt;br&gt;&lt;br&gt;
The Core layer, which contains business rules and data processing logic, was not modified at all.&lt;/p&gt;


&lt;h3&gt;
  
  
  Scalability
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Clean Architecture promotes scalability by keeping functionality modular, making it easier to adapt or extract parts of the system when necessary.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Clean Architecture’s modular structure and use of abstractions make it easier to extract parts of the system and &lt;strong&gt;scale them independently&lt;/strong&gt; from the rest of the application.&lt;/p&gt;
&lt;h4&gt;
  
  
  Scalability in Practice
&lt;/h4&gt;

&lt;p&gt;We were using a legacy library to generate large DOC, PDF, and Excel files, but it consumed a significant amount of memory and did not release resources properly. In some cases, we even had to call &lt;code&gt;GC.Collect&lt;/code&gt; explicitly to avoid memory pressure.&lt;/p&gt;

&lt;p&gt;To address this, we decided to move file generation into a separate microservice that encapsulates this library and handles document generation independently.&lt;/p&gt;

&lt;p&gt;Thanks to Clean Architecture and its modular design, we were able to reuse the existing file generation code and move it into the new microservice without changing the Core layer.&lt;/p&gt;


&lt;h2&gt;
  
  
  Cons
&lt;/h2&gt;

&lt;p&gt;While Clean Architecture offers many benefits, it is not without drawbacks. In reality, the added structure, abstractions and layer separations can introduce &lt;strong&gt;complexity, additional boilerplate and upfront development overhead&lt;/strong&gt;. The next sections explore these challenges with concrete examples from real projects.&lt;/p&gt;
&lt;h3&gt;
  
  
  Understanding and Complexity
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Understanding the architecture can be challenging, especially for developers or teams unfamiliar with it.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Deciding how to &lt;strong&gt;split responsibilities&lt;/strong&gt; and &lt;strong&gt;assign code to the correct layer&lt;/strong&gt; often requires &lt;strong&gt;experience and careful reasoning&lt;/strong&gt;, not just following dependencies or namespaces.&lt;/p&gt;
&lt;h4&gt;
  
  
  Understanding in practice
&lt;/h4&gt;

&lt;p&gt;Let’s look at the example below. All referenced dependencies implementations &lt;code&gt;IDataCommandRepository&lt;/code&gt; and &lt;code&gt;IAuditLogger&lt;/code&gt; come from the Infrastructure layer. Because of this, many developers might assume that &lt;code&gt;DataService&lt;/code&gt; should also belong to the Infrastructure layer.&lt;/p&gt;

&lt;p&gt;However, this class actually &lt;strong&gt;contains business logic&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It decides how data would be updated (setting &lt;code&gt;State&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;It applies business rules (ignoring empty input)&lt;/li&gt;
&lt;li&gt;It coordinates persistence and auditing as part of a business operation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For these reasons, &lt;code&gt;DataService&lt;/code&gt; belongs in the Core layer, even though it depends on abstractions that are implemented in Infrastructure layer.&lt;/p&gt;

&lt;p&gt;This example highlights one of the main challenges of Clean Architecture: &lt;strong&gt;layer placement is determined by responsibility and intent&lt;/strong&gt;. Understanding this distinction often takes time and experience.&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;internal&lt;/span&gt; &lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DataService&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IDataService&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;IDataCommandRepository&lt;/span&gt; &lt;span class="n"&gt;_dataCommandRepository&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;IAuditLogger&lt;/span&gt; &lt;span class="n"&gt;_auditLogger&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&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;Result&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;FinishDataSafeAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IReadOnlyCollection&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;dataIds&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;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dataIds&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt; &lt;span class="p"&gt;||&lt;/span&gt; &lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="n"&gt;dataIds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Ok&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;updateCommands&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dataIds&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="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;UpdateDataCommand&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;Id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;State&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DataState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Finish&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;updateResult&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;_dataCommandRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UpdateDataSafeAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;updateCommands&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;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;updateResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsFailed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Fail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Update data failed."&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;WithErrors&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;updateResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Errors&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;_auditLogger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;updateCommands&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Ok&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Heavy Structure
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Clean Architecture enforces a strict layered structure and extensive use of abstractions.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;While this improves long-term maintainability, it also introduces &lt;strong&gt;significant boilerplate and ceremony&lt;/strong&gt;, which can feel excessive for small or simple applications, especially basic CRUD based applications.&lt;/p&gt;

&lt;h4&gt;
  
  
  Heavy Structure in Practice
&lt;/h4&gt;

&lt;p&gt;In Clean Architecture, even a simple CRUD operation often requires changes across multiple layers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Creating an endpoint in the API layer&lt;/li&gt;
&lt;li&gt;Implementing a handler or use case in the Core layer&lt;/li&gt;
&lt;li&gt;Adding a repository method in the Infrastructure layer&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now imagine that the controller, Minimal API file or repository class does not exist yet, you first have to create all of them before implementing the actual functionality.&lt;/p&gt;

&lt;p&gt;On top of that, if you enforce code coverage standards, you also need to write unit tests for these components.&lt;/p&gt;

&lt;p&gt;As a result, a very &lt;strong&gt;simple CRUD operation&lt;/strong&gt; can require a &lt;strong&gt;large amount of code&lt;/strong&gt; and setup, which may feel unnecessarily heavy for straightforward use cases.&lt;/p&gt;




&lt;h3&gt;
  
  
  Performance
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;The layered structure and extensive use of abstractions may cause minor performance overhead compared to simpler, more direct architectures.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The additional layers of abstraction in Clean Architecture can introduce slight performance overhead due to increased indirection and mapping between layers. While this is rarely an issue for most business applications, performance critical systems may require careful optimization.&lt;/p&gt;

&lt;h4&gt;
  
  
  Performance in Practice
&lt;/h4&gt;

&lt;p&gt;Let’s look at a performance critical CRUD endpoint in a .NET application.&lt;/p&gt;

&lt;p&gt;For every request, a deep call stack is executed involving multiple allocations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;RequestHandler&lt;/code&gt; resolves &lt;code&gt;IValidator&lt;/code&gt; and &lt;code&gt;IRepository&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;Repository&lt;/code&gt; resolves &lt;code&gt;IConnectionProvider&lt;/code&gt; and &lt;code&gt;IMapper&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Data is mapped from &lt;code&gt;DataEntity&lt;/code&gt; (Infrastructure) to &lt;code&gt;Data&lt;/code&gt; (Core).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each step is small, but collectively they introduce extra allocations, method calls, and indirection compared to a more direct approach. In high-throughput scenarios, this additional overhead can become noticeable under load.&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;internal&lt;/span&gt; &lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RequestHandler&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IRequestHandler&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;IValidator&lt;/span&gt; &lt;span class="n"&gt;_validator&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;IRepository&lt;/span&gt; &lt;span class="n"&gt;_repository&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;RequestHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IValidator&lt;/span&gt; &lt;span class="n"&gt;validator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IRepository&lt;/span&gt; &lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_validator&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;validator&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;_repository&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&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;Result&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IEnumerable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Data&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;HandleAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;Request&lt;/span&gt; &lt;span class="n"&gt;request&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;if&lt;/span&gt; &lt;span class="p"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;_validator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsValid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Fail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Invalid request"&lt;/span&gt;&lt;span class="p"&gt;);&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;_repository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetDataSafeAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;internal&lt;/span&gt; &lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Repository&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IRepository&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;IConnectionProvider&lt;/span&gt; &lt;span class="n"&gt;_provider&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;IMapper&lt;/span&gt; &lt;span class="n"&gt;_mapper&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;Repository&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IConnectionProvider&lt;/span&gt; &lt;span class="n"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IMapper&lt;/span&gt; &lt;span class="n"&gt;mapper&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_provider&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;_mapper&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mapper&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&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;Result&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IEnumerable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Data&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;GetDataSafeAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;Request&lt;/span&gt; &lt;span class="n"&gt;request&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;try&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;connection&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_provider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetOpenedConnection&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;data&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;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ExecuteQueryAsync&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;DataEntity&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(...);&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_mapper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Data&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DbException&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Fail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Database command failed"&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; In practice, this downside is &lt;strong&gt;arguably negligible for 99% of business applications&lt;/strong&gt;. For most I/O bound web APIs, database latency far outweighs the cost of a few extra interface allocations.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  Initial Setup Time
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Clean Architecture requires a significant upfront investment in structure, abstractions, and conventions&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Before delivering visible features, teams must &lt;strong&gt;define layers, interfaces, dependency rules and project organization&lt;/strong&gt;, which can slow down early development, especially for greenfield projects.&lt;/p&gt;

&lt;h4&gt;
  
  
  Initial Setup Time in practice
&lt;/h4&gt;

&lt;p&gt;We started a greenfield application with heavy business logic and the initial development phase was noticeably slow. Every new feature required creating a full structure: Minimal API endpoints, application handlers, business services, repositories and external proxy services.&lt;/p&gt;

&lt;p&gt;Because these were new features, we had to introduce new abstractions and classes for almost every layer. Additionally, to maintain a minimal level of quality, we implemented unit tests for each component.&lt;/p&gt;

&lt;p&gt;As a result, &lt;strong&gt;progress in the early stages felt slow&lt;/strong&gt;, even though the architecture paid off later as the system grew.&lt;/p&gt;




&lt;h3&gt;
  
  
  Data Model Duplication
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Strict separation of concerns often leads to duplicating similar data structures across layers, increasing the maintenance burden.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To keep the Core layer independent of the Database and the UI, you normally cannot use the same class for all three. This &lt;strong&gt;forces you to create separate classes for Database Entities, Domain Models, and API DTOs,&lt;/strong&gt; even if they look almost identical.&lt;/p&gt;

&lt;h4&gt;
  
  
  Duplication in Practice
&lt;/h4&gt;

&lt;p&gt;In a previous project, we had a &lt;code&gt;User&lt;/code&gt; object. To follow Clean Architecture strictness, we had:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;UserEntity&lt;/code&gt; (Infrastructure for fetching data from the database)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;User&lt;/code&gt; (Core, rich domain model)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;UserDto&lt;/code&gt; (API, exposed to clients)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every time we added a new field like &lt;code&gt;PhoneNumber&lt;/code&gt;, we had to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Add it to all three classes.&lt;/li&gt;
&lt;li&gt;Update the mapper from Infrastructure to Core.&lt;/li&gt;
&lt;li&gt;Update the mapper from Core to API.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This "mapping tax" consumed a lot of development time for fields that were just being passed through the system without valid business logic. However, &lt;strong&gt;this does not apply to all cases&lt;/strong&gt;, in many scenarios only &lt;code&gt;UserEntity&lt;/code&gt; and &lt;code&gt;UserDto&lt;/code&gt; are sufficient.&lt;/p&gt;




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

&lt;p&gt;Clean Architecture is &lt;strong&gt;not a silver bullet&lt;/strong&gt;, and it shouldn't be the default choice for every single project.&lt;/p&gt;

&lt;p&gt;If you are building a &lt;strong&gt;small operational tool, a POC or a simple CRUD application&lt;/strong&gt;, the overhead of Clean Architecture will likely slow you down without providing much value. In these cases, a simpler approach like &lt;strong&gt;Vertical Slice Architecture&lt;/strong&gt; or a straightforward layered monolith is often a better fit.&lt;/p&gt;

&lt;p&gt;However, for &lt;strong&gt;complex, long-lived enterprise systems&lt;/strong&gt; where business rules change frequently and technology needs to evolve, Clean Architecture is an invaluable investment. The initial cost of setup and boilerplate pays dividends in the form of a system that is testable, adaptable, and resistant to rot over time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The key is to understand the trade-offs.&lt;/strong&gt; Don't adopt it blindly because it's popular, adopt it when your problem domain is complex enough to warrant the solution.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>dotnet</category>
      <category>csharp</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>Angular: HTTP Caching with RxJS shareReplay</title>
      <dc:creator>Daniel Balcarek</dc:creator>
      <pubDate>Mon, 16 Feb 2026 13:08:56 +0000</pubDate>
      <link>https://dev.to/gramli/angular-http-caching-with-rxjs-sharereplay-3mm0</link>
      <guid>https://dev.to/gramli/angular-http-caching-with-rxjs-sharereplay-3mm0</guid>
      <description>&lt;p&gt;At some point in every Angular application repeated HTTP calls start to hurt, slower page loads, unnecessary backend load, and wasted network traffic.&lt;/p&gt;

&lt;p&gt;The obvious solution is &lt;strong&gt;caching&lt;/strong&gt;. But in Angular, caching is not just about storing data, it’s about &lt;strong&gt;managing observables correctly&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Done wrong, it leads to duplicate requests, stale data, or permanently cached errors. Done right, it eliminates redundant calls entirely.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Naive Caching Approach
&lt;/h2&gt;

&lt;p&gt;The simplest way to cache data in Angular, without knowing much about RxJS, is to use a field in a service class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Injectable&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;providedIn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;root&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AppService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;vehiclesDataCache&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;VehiclesApiResponse&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;HttpClient&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;getVehiclesData&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;Observable&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;VehiclesApiResponse&lt;/span&gt;&lt;span class="o"&gt;&amp;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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vehiclesDataCache&lt;/span&gt;&lt;span class="p"&gt;)&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;of&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vehiclesDataCache&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;observable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;VehiclesApiResponse&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://starwars-databank-server.vercel.app/api/v1/vehicles&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;observable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="na"&gt;response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;VehiclesApiResponse&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vehiclesDataCache&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;observable&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Although this solution works, it is not very elegant and has several problems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If &lt;code&gt;getVehiclesData()&lt;/code&gt; is called multiple times before the first request completes, each call triggers a new HTTP request instead of sharing the in-flight one.&lt;/li&gt;
&lt;li&gt;The code subscribes manually and &lt;strong&gt;never unsubscribes&lt;/strong&gt;, which can lead to &lt;strong&gt;memory leaks&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  A Better Solution: shareReplay
&lt;/h2&gt;

&lt;p&gt;Angular applications rely heavily on &lt;strong&gt;RxJS&lt;/strong&gt; and RxJS provides a much cleaner solution: the &lt;code&gt;shareReplay&lt;/code&gt; operator.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;shareReplay&lt;/code&gt; is an &lt;strong&gt;RxJS operator&lt;/strong&gt; designed to cache the latest emitted value from an observable so that multiple subscribers can share it without triggering multiple backend calls.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://www.learnrxjs.io/learn-rxjs/operators/multicasting/sharereplay" rel="noopener noreferrer"&gt;sharereplay&lt;/a&gt; &lt;em&gt;- Share source and replay specified number of emissions on subscription.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This makes it ideal for caching relatively static data such as enums, configuration values, or reference data. Once the frontend has fetched this data, it doesn’t need to call the backend again on every page visit.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Basic Example:&lt;/strong&gt;&lt;br&gt;
The most basic use of &lt;code&gt;shareReplay&lt;/code&gt; looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Injectable&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;providedIn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;root&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AppService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;vehiclesDataShareReplay$&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Observable&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;VehiclesApiResponse&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;HttpClient&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;getVehicleShareReplay&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;Observable&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;VehiclesApiResponse&lt;/span&gt;&lt;span class="o"&gt;&amp;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="o"&gt;!&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vehiclesDataShareReplay$&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vehiclesDataShareReplay$&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;VehiclesApiResponse&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://starwars-databank-server.vercel.app/api/v1/vehicles&lt;/span&gt;&lt;span class="dl"&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;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;shareReplay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;));&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;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vehiclesDataShareReplay$&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This caches the last emitted value (&lt;code&gt;shareReplay(1)&lt;/code&gt;). The subscription to the source (&lt;code&gt;HttpClient&lt;/code&gt;) remains active until the request completes. This is the most common pattern for caching HTTP requests in Angular services.&lt;/p&gt;

&lt;p&gt;However, while this approach looks correct, &lt;strong&gt;it hides a subtle issue&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The "Cached Error" Problem
&lt;/h2&gt;

&lt;p&gt;A common pitfall with &lt;code&gt;shareReplay&lt;/code&gt; is that it also &lt;strong&gt;caches errors&lt;/strong&gt;. If the HTTP request fails, every future subscriber will immediately receive the same error, and no new HTTP request will ever be made.&lt;/p&gt;

&lt;p&gt;A typical solution is to use &lt;code&gt;retry&lt;/code&gt; and &lt;code&gt;catchError&lt;/code&gt; before &lt;code&gt;shareReplay&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Injectable&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;providedIn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;root&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AppService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;vehiclesDataShareReplay$&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Observable&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;VehiclesApiResponse&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;HttpClient&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;getVehicleShareReplay&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;Observable&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;VehiclesApiResponse&lt;/span&gt;&lt;span class="o"&gt;&amp;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="o"&gt;!&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vehiclesDataShareReplay$&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vehiclesDataShareReplay$&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;VehiclesApiResponse&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://starwars-databank-server.vercel.app/api/v1/vehicles&lt;/span&gt;&lt;span class="dl"&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;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="nf"&gt;retry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
          &lt;span class="nf"&gt;catchError&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vehiclesDataShareReplay$&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&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;throwError&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="p"&gt;}),&lt;/span&gt;
          &lt;span class="nf"&gt;shareReplay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&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="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vehiclesDataShareReplay$&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, &lt;code&gt;retry(2)&lt;/code&gt; means the request is attempted up to three times in total. If all attempts fail, &lt;code&gt;catchError&lt;/code&gt; resets the cached observable before the error is replayed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cache with Refresh
&lt;/h2&gt;

&lt;p&gt;The biggest limitation of &lt;code&gt;shareReplay(1)&lt;/code&gt; is that the &lt;strong&gt;cached data can become stale&lt;/strong&gt;. In some scenarios, you want to refresh the data while still benefiting from caching.&lt;/p&gt;

&lt;p&gt;A common solution is the “cache with refresh” pattern using a &lt;code&gt;BehaviorSubject&lt;/code&gt; and &lt;code&gt;switchMap&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Injectable&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;providedIn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;root&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AppService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;vehiclesDataBehavior$&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Observable&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;VehiclesApiResponse&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;refreshTrigger$&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;BehaviorSubject&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;HttpClient&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;getVehicleBehavior&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;Observable&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;VehiclesApiResponse&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// 1. Check if we already created this observable&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vehiclesDataBehavior$&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// 2. Create the observable (only once)&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vehiclesDataBehavior$&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;refreshTrigger$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="c1"&gt;// 3. Listen to refresh trigger&lt;/span&gt;
        &lt;span class="nf"&gt;switchMap&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
          &lt;span class="c1"&gt;// 4. When trigger fires, make HTTP request&lt;/span&gt;
          &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;VehiclesApiResponse&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://starwars-databank-server.vercel.app/api/v1/vehicles&lt;/span&gt;&lt;span class="dl"&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;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
              &lt;span class="c1"&gt;// 5. If request fails, retry 2 times&lt;/span&gt;
              &lt;span class="nf"&gt;retry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
              &lt;span class="c1"&gt;// 6. If still fails after retries, catch error&lt;/span&gt;
              &lt;span class="nf"&gt;catchError&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Error fetching vehicles:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;EMPTY&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="p"&gt;),&lt;/span&gt;
        &lt;span class="c1"&gt;// 7. Cache the most recent successful HTTP response and replay it to all current and future subscribers&lt;/span&gt;
        &lt;span class="nf"&gt;shareReplay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&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;// 8. Return the cached observable&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vehiclesDataBehavior$&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;refreshVehiclesBehavior&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Trigger a new fetch by calling next() on the BehaviorSubject&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;refreshTrigger$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;undefined&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, the &lt;code&gt;BehaviorSubject&lt;/code&gt; triggers an HTTP request whenever &lt;code&gt;next()&lt;/code&gt; is called. The &lt;code&gt;switchMap&lt;/code&gt; ensures that if a refresh is triggered while a request is still in flight, the previous request is canceled and replaced with a new one.&lt;/p&gt;

&lt;p&gt;However, let’s take a closer look at &lt;strong&gt;error handling in this pattern&lt;/strong&gt;. We cannot simply reset the cached observable and rethrow the error like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;catchError&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vehiclesDataShareReplay$&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&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;throwError&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;error&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;When &lt;code&gt;throwError()&lt;/code&gt; is called inside &lt;code&gt;switchMap&lt;/code&gt;, &lt;strong&gt;it completes the entire stream&lt;/strong&gt;. Once completed, the stream is &lt;strong&gt;dead forever&lt;/strong&gt;. Any future calls to &lt;code&gt;next()&lt;/code&gt; on the &lt;code&gt;BehaviorSubject&lt;/code&gt; will have no effect, because there is no longer an active subscription. Setting &lt;code&gt;vehiclesDataBehavior$ = null&lt;/code&gt; does not revive the stream, the observable pipeline has already terminated.&lt;/p&gt;

&lt;p&gt;That's why we use &lt;code&gt;EMPTY&lt;/code&gt; instead. When an error occurs, &lt;code&gt;EMPTY&lt;/code&gt; completes the inner observable without emitting a value. Since there's no value to emit, the outer stream stays alive and listening to the &lt;code&gt;BehaviorSubject&lt;/code&gt;. This means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Subscribers are NOT notified of the error&lt;/strong&gt; – they retain the last successful cached value&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The refresh mechanism stays active&lt;/strong&gt; – users can trigger another fetch with &lt;code&gt;refreshVehiclesBehavior()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Trade-off&lt;/strong&gt;: Users see stale data instead of an error message, which may or may not be desirable depending on your use case.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you prefer to notify subscribers of errors instead, you'd need a different approach (e.g., &lt;code&gt;Subject&amp;lt;VehiclesApiResponse | Error&amp;gt;&lt;/code&gt; to explicitly model both success and failure states).&lt;/p&gt;

&lt;h2&gt;
  
  
  Choosing the Right &lt;code&gt;shareReplay&lt;/code&gt; Configuration
&lt;/h2&gt;

&lt;p&gt;To avoid memory leaks, it’s important to understand how different &lt;code&gt;shareReplay&lt;/code&gt; configurations affect subscriptions and memory usage.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. &lt;code&gt;shareReplay()&lt;/code&gt; - no arguments
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Buffer&lt;/strong&gt;: Caches all emitted values (infinite buffer).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subscription&lt;/strong&gt;: Stays active until the source completes, even if there are no subscribers.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Use Case&lt;/strong&gt;: Suitable only for finite streams where you need the full emission history.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. &lt;code&gt;shareReplay(1)&lt;/code&gt; - buffer size only
&lt;/h3&gt;

&lt;p&gt;This is the most common usage for HTTP requests.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Buffer&lt;/strong&gt;: Caches the last emitted value.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subscription&lt;/strong&gt;: Remains active until the source completes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use Case&lt;/strong&gt;: Ideal for global singleton data fetched once and reused across the app.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. &lt;code&gt;shareReplay({ bufferSize: 1, refCount: true })&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;refCount: true&lt;/code&gt; is better for streams where you want cleanup when nobody's listening (WebSockets, timers), not necessarily for component-level data.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Buffer&lt;/strong&gt;: Caches the last emitted value.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subscription (Magic of &lt;code&gt;refCount&lt;/code&gt;)&lt;/strong&gt;: Automatically unsubscribes from the source when the subscriber count drops to zero.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use Case&lt;/strong&gt;: Recommended for long-lived or infinite streams (e.g., WebSockets), or when you want cached data to reset when users leave a feature.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  When NOT to Use &lt;code&gt;shareReplay&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;While &lt;code&gt;shareReplay&lt;/code&gt; is powerful, it's inappropriate for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;User-specific data&lt;/strong&gt;: Risk of showing cached data from one user to another&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Real-time data&lt;/strong&gt;: Stock prices, live scores, etc. need fresh data on every request&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Large payloads&lt;/strong&gt;: Caching large responses increases memory footprint&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security-sensitive data&lt;/strong&gt;: Ensure proper cache invalidation on logout&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Caching in Angular is not just about improving performance, but also about &lt;strong&gt;controlling side effects, avoiding duplicate work, and managing application state predictably&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;shareReplay&lt;/code&gt; provides a powerful and elegant way to cache HTTP responses, but it must be used with a clear understanding of its behavior:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;shareReplay(1)&lt;/code&gt; is ideal for global, static data that rarely changes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Error handling is mandatory&lt;/strong&gt;, otherwise failures can be permanently cached.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stale data requires an explicit refresh strategy&lt;/strong&gt;, such as a BehaviorSubject combined with switchMap.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;refCount&lt;/code&gt; matters when dealing with long-lived or infinite streams and component lifecycles.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There is no single “correct” configuration for &lt;code&gt;shareReplay&lt;/code&gt;. The right approach depends on &lt;strong&gt;how long the data should live, who consumes it, and whether it needs to be refreshed&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;When used deliberately, &lt;code&gt;shareReplay&lt;/code&gt; can eliminate redundant HTTP calls, simplify state management, and significantly improve application performance, without introducing hidden bugs or memory leaks.&lt;/p&gt;

&lt;p&gt;A working example project is available in my 📁 &lt;strong&gt;&lt;a href="https://github.com/Gramli/Gramli.Framework/tree/main/src/angular-shareReplay" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>angular</category>
      <category>performance</category>
      <category>rxjs</category>
      <category>typescript</category>
    </item>
    <item>
      <title>A Silly Idea: A Star Wars Lightsaber Duel in the Terminal with GitHub Copilot CLI</title>
      <dc:creator>Daniel Balcarek</dc:creator>
      <pubDate>Mon, 09 Feb 2026 12:57:19 +0000</pubDate>
      <link>https://dev.to/gramli/a-silly-idea-a-star-wars-lightsaber-duel-in-the-terminal-with-github-copilot-cli-1ki7</link>
      <guid>https://dev.to/gramli/a-silly-idea-a-star-wars-lightsaber-duel-in-the-terminal-with-github-copilot-cli-1ki7</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/github-2026-01-21"&gt;GitHub Copilot CLI Challenge&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  🎬 What I Built
&lt;/h2&gt;

&lt;p&gt;In a previous submission for a &lt;a href="https://dev.to/gramli/msg-rocket-from-diff-to-decision-with-github-copilot-cli-1ba8"&gt;productivity tool&lt;/a&gt;, I sneaked in a small Easter egg inspired by &lt;strong&gt;The Matrix&lt;/strong&gt;. That led to a slightly silly thought, what if I built an actual movie scene using &lt;strong&gt;GitHub Copilot CLI&lt;/strong&gt;, rendered &lt;strong&gt;entirely in a terminal&lt;/strong&gt; with Unicode characters?&lt;/p&gt;

&lt;p&gt;That idea stuck with me. &lt;strong&gt;It sounded unnecessary, impractical…&lt;/strong&gt; and kind of &lt;strong&gt;awesome&lt;/strong&gt; 🙂&lt;/p&gt;

&lt;p&gt;So I decided to create a &lt;strong&gt;Star Wars inspired lightsaber duel in pure CLI.&lt;/strong&gt; My very first idea was the Lord of the Rings “Ride of the Rohirrim” scene 🐎🐎, but I quickly realized I should keep the scope smaller — after all, it’s still just a terminal and Unicode characters. 😆&lt;/p&gt;

&lt;h2&gt;
  
  
  📽️ Teaser
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;The full animation is a bit too long for this post, so I prepared a short “teaser” instead.&lt;/em&gt;&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%2Fvr46bo9ajojducqrqj4a.gif" 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%2Fvr46bo9ajojducqrqj4a.gif" alt="Duel of the CLI - Teaser" width="969" height="478"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;a href="https://github.com/Gramli/star-wars-animation/blob/master/Assets/star-wars-animation-full.gif" rel="noopener noreferrer"&gt;View the complete version (~65 seconds) on GitHub&lt;/a&gt;
&lt;/h4&gt;

&lt;h4&gt;
  
  
  🏃‍♂️ Run it locally
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/Gramli/star-wars-animation.git
&lt;span class="nb"&gt;cd &lt;/span&gt;star-wars-animation
dotnet run
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;ul&gt;
&lt;li&gt;You’ll need &lt;strong&gt;.NET 10 SDK&lt;/strong&gt; installed.&lt;/li&gt;
&lt;li&gt;Use a font with good Unicode support (e.g., &lt;strong&gt;Cascadia Code&lt;/strong&gt;).&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;Visual Studio Console&lt;/strong&gt; or &lt;strong&gt;VS Code terminal&lt;/strong&gt; render the block characters best -&amp;gt; they’re true Jedi-level terminals 😄&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ Running in classic &lt;code&gt;cmd.exe&lt;/code&gt; or other limited terminals may break the animation. Consider it a &lt;strong&gt;training exercise for Padawans&lt;/strong&gt;, only the worthy (and properly equipped) terminals will experience the duel in all its glory.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  🎥 My Experience with GitHub Copilot CLI
&lt;/h2&gt;

&lt;p&gt;I brought the idea and created several &lt;a href="https://github.com/Gramli/star-wars-animation/tree/master/.github/prompts" rel="noopener noreferrer"&gt;detailed prompts&lt;/a&gt;. Copilot handled most of the code generation and quickly produced a nice draft version. However, something was missing — it lacked soul.&lt;/p&gt;

&lt;p&gt;At that point, &lt;strong&gt;Copilot stopped being just a code generator&lt;/strong&gt; and became a &lt;strong&gt;Creative Director.&lt;/strong&gt; I kept refining prompts, pushing for stronger visuals, better animation flow, and more cinematic moments.&lt;/p&gt;

&lt;p&gt;Here’s a real example of how our “conversation” looked:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Me:&lt;/strong&gt;&lt;br&gt;
&lt;em&gt;The animation works and we already have some nice effects, but we need to take it to the next level an Oscar-movie level. We need a real wow effect that makes this animation stunning. Can you suggest 3–5 ideas?&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Copilot:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Here are a few ways to push the animation toward a more cinematic feel, even within CLI constraints:

1. Lightsaber motion trails to make fast swings feel fluid and powerful  
2. Gradient-based lighting to simulate glow and light falloff  
3. Impact shockwaves that briefly distort the screen on heavy clashes  
4. Molten damage on walls and floors that cool over time  
5. Cinematic letterboxing during key moments to focus attention  

I’d start with motion trails and molten damage for the biggest visual impact.
Which one would you like me to implement first?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Copilot responded with ideas like motion trails for lightsabers, gradient lighting, shockwave effects, molten damage on walls, and even cinematic letterboxing. Some of them made it into the final version, others stayed on the cutting-room floor, but the &lt;strong&gt;process itself was surprisingly fun&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I was genuinely impressed. Copilot helped transform a simple ASCII-style drawing into a small but entertaining show. Sure, it’s not an Oscar-winning movie, but for a playful, slightly ridiculous idea, I think it turned out pretty cool.&lt;/p&gt;

&lt;h3&gt;
  
  
  💥 When We Failed (And Pivoted)
&lt;/h3&gt;

&lt;p&gt;Things went smoothly… until I had a &lt;strong&gt;very bad idea&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;“What if we add a camera rotation illusion?”&lt;/strong&gt; 😅&lt;/p&gt;

&lt;p&gt;Yeah. That was ambitious.&lt;/p&gt;

&lt;p&gt;This turned out to be a real challenge, even for Copilot. After many new sessions and repeatedly reworking prompts, we eventually had to pivot. A full Y-axis camera rotation just wasn’t readable with simple ASCII silhouettes, so we ended up experimenting with X-axis rotation and a top-down view instead.&lt;/p&gt;

&lt;p&gt;At that point, the silhouettes are so minimal that the viewer has to mentally accept the perspective shift, but that’s part of the charm (and limitation) of doing cinematic nonsense in a CLI.&lt;/p&gt;

&lt;p&gt;And honestly? That experimentation, even when it didn’t fully work was one of the most fun parts of the project.&lt;/p&gt;

&lt;h2&gt;
  
  
  💭 Final Thoughts
&lt;/h2&gt;

&lt;p&gt;I really enjoyed this ride. Using &lt;strong&gt;GitHub Copilot CLI&lt;/strong&gt; for something completely &lt;strong&gt;non-practical, visual, and slightly absurd&lt;/strong&gt; turned out to be a &lt;strong&gt;great experience&lt;/strong&gt;. It pushed me to think differently about prompts, iteration, and collaboration with an AI, not just as a tool, but as a &lt;strong&gt;creative partner&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;May the Source be with you!&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>githubchallenge</category>
      <category>cli</category>
      <category>githubcopilot</category>
    </item>
    <item>
      <title>.NET: Exceptions vs Result Pattern - Performance Benchmark</title>
      <dc:creator>Daniel Balcarek</dc:creator>
      <pubDate>Thu, 05 Feb 2026 12:59:59 +0000</pubDate>
      <link>https://dev.to/gramli/net-throwing-exceptions-vs-result-pattern-benchmark-4a62</link>
      <guid>https://dev.to/gramli/net-throwing-exceptions-vs-result-pattern-benchmark-4a62</guid>
      <description>&lt;p&gt;In the realm of &lt;strong&gt;error handling&lt;/strong&gt; and &lt;strong&gt;control flow&lt;/strong&gt;, developers often choose between two primary approaches: &lt;strong&gt;throwing exceptions&lt;/strong&gt; and using the &lt;strong&gt;Result Pattern&lt;/strong&gt;. Throwing exceptions relies on the standard language mechanism to handle errors, whereas the Result Pattern involves returning a structured object containing both the operation's outcome and any error details.&lt;/p&gt;

&lt;p&gt;Both approaches have distinct trade-offs, and the &lt;strong&gt;optimal choice often depends on the specific project context&lt;/strong&gt;. Based on my experience with both, I was curious about the performance differences between them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Benchmark Methodology
&lt;/h2&gt;

&lt;p&gt;To provide a comprehensive comparison, the benchmark evaluates &lt;strong&gt;two execution scenarios&lt;/strong&gt;: a failure path and a success path. The failure path measures the overhead of propagating errors through the call stack, highlighting the cost of throwing exceptions versus returning structured results. The success path measures the overhead during normal execution when no errors occur. Evaluating both scenarios offers a clearer perspective on the trade-offs and illustrates how each approach behaves under different runtime conditions.&lt;/p&gt;

&lt;p&gt;For the benchmark, I created two handlers: &lt;code&gt;ResultHandler&lt;/code&gt; and &lt;code&gt;ThrowExceptionHandler&lt;/code&gt;. Both perform the same task but use different error handling mechanisms. Each processes generated data through multiple validation layers. To introduce asynchronous suspension without performing real I/O, &lt;code&gt;Task.Yield()&lt;/code&gt; is used. This forces the method to asynchronously yield execution, allowing the benchmark to measure error propagation across async boundaries.&lt;/p&gt;

&lt;p&gt;📁 &lt;a href="https://github.com/Gramli/Gramli.Framework/tree/main/src/GeneralBenchmark/ExceptionsAndResult" rel="noopener noreferrer"&gt;Benchmark Repository&lt;/a&gt; - all benchmark source code is available on GitHub.&lt;/p&gt;

&lt;h3&gt;
  
  
  Benchmark class
&lt;/h3&gt;

&lt;p&gt;To reflect control flow overhead rather than test data processing, &lt;strong&gt;the benchmark uses small datasets&lt;/strong&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="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;SimpleJob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RuntimeMoniker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Net10_0&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Orderer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BenchmarkDotNet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SummaryOrderPolicy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Declared&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;MemoryDiagnoser&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ExceptionAndResultBenchmark&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;ResultHandler&lt;/span&gt; &lt;span class="n"&gt;_resultHandler&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;ThrowExceptionHandler&lt;/span&gt; &lt;span class="n"&gt;_throwExceptionHandler&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IEnumerable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ExceptionAndResultBenchmarkData&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;Data&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ExceptionAndResultBenchmarkData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;100&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ExceptionAndResultBenchmarkData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;250&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ExceptionAndResultBenchmarkData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;500&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ExceptionAndResultBenchmarkData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;750&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IEnumerable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ExceptionAndResultBenchmarkData&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;ValidOnlyData&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ExceptionAndResultBenchmarkData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ExceptionAndResultBenchmarkData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;250&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ExceptionAndResultBenchmarkData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ExceptionAndResultBenchmarkData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;750&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;ExceptionAndResultBenchmark&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_resultHandler&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;ResultHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Instance&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;_throwExceptionHandler&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;ThrowExceptionHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Instance&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="n"&gt;Benchmark&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;ArgumentsSource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Data&lt;/span&gt;&lt;span class="p"&gt;))]&lt;/span&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="nf"&gt;Result_Pattern&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ExceptionAndResultBenchmarkData&lt;/span&gt; &lt;span class="n"&gt;data&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;_&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;_resultHandler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RequestData&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="n"&gt;Benchmark&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;ArgumentsSource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Data&lt;/span&gt;&lt;span class="p"&gt;))]&lt;/span&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="nf"&gt;Exception_Throw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ExceptionAndResultBenchmarkData&lt;/span&gt; &lt;span class="n"&gt;data&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;_&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;_throwExceptionHandler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RequestData&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="n"&gt;Benchmark&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;ArgumentsSource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ValidOnlyData&lt;/span&gt;&lt;span class="p"&gt;))]&lt;/span&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="nf"&gt;Result_Pattern_ValidOnly&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ExceptionAndResultBenchmarkData&lt;/span&gt; &lt;span class="n"&gt;data&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;_&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;_resultHandler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RequestData&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="n"&gt;Benchmark&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;ArgumentsSource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ValidOnlyData&lt;/span&gt;&lt;span class="p"&gt;))]&lt;/span&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="nf"&gt;Exception_Throw_ValidOnly&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ExceptionAndResultBenchmarkData&lt;/span&gt; &lt;span class="n"&gt;data&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;_&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;_throwExceptionHandler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RequestData&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Result pattern handler
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;internal&lt;/span&gt; &lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ResultHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Logger&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;Logger&lt;/span&gt; &lt;span class="n"&gt;_logger&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;ValueTask&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HandleDto&lt;/span&gt;&lt;span class="p"&gt;?&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;Handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IEnumerable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;RequestItem&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;data&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;data&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt; &lt;span class="p"&gt;||&lt;/span&gt; &lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;())&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;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;processResult&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;ProcessData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&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;processResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsFailed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;_logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;processResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Errors&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;default&lt;/span&gt;&lt;span class="p"&gt;;&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;new&lt;/span&gt; &lt;span class="n"&gt;HandleDto&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;Sum&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;processResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;ValueTask&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;ProcessData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IEnumerable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;RequestItem&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;data&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;getValidDataResult&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;GetValidData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&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;getValidDataResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsFailed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;.&lt;/span&gt;&lt;span class="nf"&gt;Fail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;getValidDataResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Errors&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;positiveDataResult&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;GetPositiveData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;getValidDataResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&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;positiveDataResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsFailed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;.&lt;/span&gt;&lt;span class="nf"&gt;Fail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;positiveDataResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Errors&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;.&lt;/span&gt;&lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;positiveDataResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;ValueTask&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IEnumerable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;RequestItem&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;GetValidData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IEnumerable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;RequestItem&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;inputData&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;inputData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;All&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&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;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsValid&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IEnumerable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;RequestItem&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;.&lt;/span&gt;&lt;span class="nf"&gt;Fail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"All data are invalid"&lt;/span&gt;&lt;span class="p"&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;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Yield&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;item&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;inputData&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;isValidSecondLayerResult&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;IsValidSecondLayer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&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;isValidSecondLayerResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsFailed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IEnumerable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;RequestItem&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;.&lt;/span&gt;&lt;span class="nf"&gt;Fail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Second level validation is invalid."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithErrors&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;isValidSecondLayerResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Errors&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="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IEnumerable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;RequestItem&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;.&lt;/span&gt;&lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;inputData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsValid&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;IsValidSecondLayer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RequestItem&lt;/span&gt; &lt;span class="n"&gt;item&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;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsValidSecondLayer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;.&lt;/span&gt;&lt;span class="nf"&gt;Fail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Item is not valid in the second layer"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;.&lt;/span&gt;&lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;ValueTask&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IEnumerable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;RequestItem&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;GetPositiveData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IEnumerable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;RequestItem&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;inputData&lt;/span&gt;&lt;span class="p"&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;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Yield&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;inputData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;All&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IEnumerable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;RequestItem&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;.&lt;/span&gt;&lt;span class="nf"&gt;Fail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"All data are smaller than 0"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IEnumerable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;RequestItem&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;.&lt;/span&gt;&lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;inputData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;0&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While using the &lt;strong&gt;Result Pattern&lt;/strong&gt; introduces a lot of repetition—you must check &lt;code&gt;IsFailed&lt;/code&gt; after nearly every method call, this trade-off leads to highly &lt;strong&gt;predictable&lt;/strong&gt; code and explicit control flow.&lt;/p&gt;

&lt;h4&gt;
  
  
  Result implementation
&lt;/h4&gt;

&lt;p&gt;As the &lt;code&gt;Result&amp;lt;T&amp;gt;&lt;/code&gt; object is a crucial component of the Result pattern, its implementation is included below.&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;internal&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nc"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&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;public&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;Value&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;IsFailed&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IEnumerable&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;Errors&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nf"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="k"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;IsFailed&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;Errors&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="k"&gt;private&lt;/span&gt; &lt;span class="nf"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IEnumerable&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;errors&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Value&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="n"&gt;IsFailed&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;Errors&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nf"&gt;Result&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;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Value&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="n"&gt;IsFailed&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;Errors&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;WithErrors&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IEnumerable&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;errors&lt;/span&gt;&lt;span class="p"&gt;)&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;new&lt;/span&gt; &lt;span class="n"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;Errors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Concat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="k"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;Fail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IEnumerable&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;errors&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;Fail&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;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;ToString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Empty&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Errors&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Throw exception handler
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;internal&lt;/span&gt; &lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ThrowExceptionHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Logger&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;Logger&lt;/span&gt; &lt;span class="n"&gt;_logger&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;ValueTask&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HandleDto&lt;/span&gt;&lt;span class="p"&gt;?&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;Handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IEnumerable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;RequestItem&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;data&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;data&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt; &lt;span class="p"&gt;||&lt;/span&gt; &lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;())&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;default&lt;/span&gt;&lt;span class="p"&gt;;&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="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;processResult&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;ProcessData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&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;new&lt;/span&gt; &lt;span class="n"&gt;HandleDto&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;Sum&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;processResult&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;_logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ex&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;default&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="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;ValueTask&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;ProcessData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IEnumerable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;RequestItem&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;data&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;getValidDataResult&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;GetValidData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&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;positiveDataResult&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;GetPositiveData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;getValidDataResult&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;positiveDataResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;ValueTask&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IEnumerable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;RequestItem&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;GetValidData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IEnumerable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;RequestItem&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;inputData&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;inputData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;All&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&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;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsValid&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ArgumentException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"All data are invalid"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;inputData&lt;/span&gt;&lt;span class="p"&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;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Yield&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;item&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;inputData&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;IsValidSecondLayer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;inputData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsValid&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;IsValidSecondLayer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RequestItem&lt;/span&gt; &lt;span class="n"&gt;item&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;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsValidSecondLayer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ArgumentException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Item is not valid in the second layer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&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="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;ValueTask&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IEnumerable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;RequestItem&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;GetPositiveData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IEnumerable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;RequestItem&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;inputData&lt;/span&gt;&lt;span class="p"&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;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Yield&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;inputData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;All&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ArgumentException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"All data are smaller than 0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;inputData&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;inputData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;0&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The exception-based implementation is &lt;strong&gt;simpler and cleaner at first glance&lt;/strong&gt;. However, I have observed many times that throwing exceptions can lead to a cycle of catch-and-rethrow, which quickly transforms to &lt;strong&gt;spaghetti code&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Benchmark results
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;BenchmarkDotNet v0.15.8, Windows 10 &lt;span class="o"&gt;(&lt;/span&gt;10.0.19045.6466/22H2/2022Update&lt;span class="o"&gt;)&lt;/span&gt;
Intel Core i5-6400 CPU 2.70GHz &lt;span class="o"&gt;(&lt;/span&gt;Skylake&lt;span class="o"&gt;)&lt;/span&gt;, 1 CPU, 4 logical and 4 physical cores
.NET SDK 10.0.102
  &lt;span class="o"&gt;[&lt;/span&gt;Host]    : .NET 9.0.9 &lt;span class="o"&gt;(&lt;/span&gt;9.0.9, 9.0.925.41916&lt;span class="o"&gt;)&lt;/span&gt;, X64 RyuJIT x86-64-v3
  .NET 10.0 : .NET 10.0.2 &lt;span class="o"&gt;(&lt;/span&gt;10.0.2, 10.0.225.61305&lt;span class="o"&gt;)&lt;/span&gt;, X64 RyuJIT x86-64-v3

&lt;span class="nv"&gt;Job&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;.NET 10.0  &lt;span class="nv"&gt;Runtime&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;.NET 10.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;data&lt;/th&gt;
&lt;th&gt;Mean&lt;/th&gt;
&lt;th&gt;Error&lt;/th&gt;
&lt;th&gt;StdDev&lt;/th&gt;
&lt;th&gt;Gen0&lt;/th&gt;
&lt;th&gt;Allocated&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Result_Pattern&lt;/td&gt;
&lt;td&gt;100 items&lt;/td&gt;
&lt;td&gt;1.384 us&lt;/td&gt;
&lt;td&gt;0.0227 us&lt;/td&gt;
&lt;td&gt;0.0212 us&lt;/td&gt;
&lt;td&gt;0.2251&lt;/td&gt;
&lt;td&gt;704 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Exception_Throw&lt;/td&gt;
&lt;td&gt;100 items&lt;/td&gt;
&lt;td&gt;57.909 us&lt;/td&gt;
&lt;td&gt;0.6635 us&lt;/td&gt;
&lt;td&gt;0.6206 us&lt;/td&gt;
&lt;td&gt;5.8594&lt;/td&gt;
&lt;td&gt;18658 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Result_Pattern_ValidOnly&lt;/td&gt;
&lt;td&gt;100 items&lt;/td&gt;
&lt;td&gt;3.792 us&lt;/td&gt;
&lt;td&gt;0.0690 us&lt;/td&gt;
&lt;td&gt;0.0645 us&lt;/td&gt;
&lt;td&gt;0.3281&lt;/td&gt;
&lt;td&gt;1022 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Exception_Throw_ValidOnly&lt;/td&gt;
&lt;td&gt;100 items&lt;/td&gt;
&lt;td&gt;3.318 us&lt;/td&gt;
&lt;td&gt;0.0511 us&lt;/td&gt;
&lt;td&gt;0.0546 us&lt;/td&gt;
&lt;td&gt;0.2747&lt;/td&gt;
&lt;td&gt;903 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Result_Pattern&lt;/td&gt;
&lt;td&gt;250 items&lt;/td&gt;
&lt;td&gt;1.347 us&lt;/td&gt;
&lt;td&gt;0.0141 us&lt;/td&gt;
&lt;td&gt;0.0131 us&lt;/td&gt;
&lt;td&gt;0.2251&lt;/td&gt;
&lt;td&gt;704 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Exception_Throw&lt;/td&gt;
&lt;td&gt;250 items&lt;/td&gt;
&lt;td&gt;58.265 us&lt;/td&gt;
&lt;td&gt;0.4117 us&lt;/td&gt;
&lt;td&gt;0.3851 us&lt;/td&gt;
&lt;td&gt;5.8594&lt;/td&gt;
&lt;td&gt;18661 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Result_Pattern_ValidOnly&lt;/td&gt;
&lt;td&gt;250 items&lt;/td&gt;
&lt;td&gt;6.500 us&lt;/td&gt;
&lt;td&gt;0.2048 us&lt;/td&gt;
&lt;td&gt;0.5875 us&lt;/td&gt;
&lt;td&gt;0.2441&lt;/td&gt;
&lt;td&gt;1017 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Exception_Throw_ValidOnly&lt;/td&gt;
&lt;td&gt;250 items&lt;/td&gt;
&lt;td&gt;6.659 us&lt;/td&gt;
&lt;td&gt;0.1314 us&lt;/td&gt;
&lt;td&gt;0.2336 us&lt;/td&gt;
&lt;td&gt;0.2823&lt;/td&gt;
&lt;td&gt;892 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Result_Pattern&lt;/td&gt;
&lt;td&gt;500 items&lt;/td&gt;
&lt;td&gt;1.371 us&lt;/td&gt;
&lt;td&gt;0.0137 us&lt;/td&gt;
&lt;td&gt;0.0129 us&lt;/td&gt;
&lt;td&gt;0.2251&lt;/td&gt;
&lt;td&gt;704 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Exception_Throw&lt;/td&gt;
&lt;td&gt;500 items&lt;/td&gt;
&lt;td&gt;57.530 us&lt;/td&gt;
&lt;td&gt;0.8766 us&lt;/td&gt;
&lt;td&gt;0.8200 us&lt;/td&gt;
&lt;td&gt;5.8594&lt;/td&gt;
&lt;td&gt;18629 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Result_Pattern_ValidOnly&lt;/td&gt;
&lt;td&gt;500 items&lt;/td&gt;
&lt;td&gt;7.139 us&lt;/td&gt;
&lt;td&gt;0.1873 us&lt;/td&gt;
&lt;td&gt;0.5524 us&lt;/td&gt;
&lt;td&gt;0.3204&lt;/td&gt;
&lt;td&gt;1013 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Exception_Throw_ValidOnly&lt;/td&gt;
&lt;td&gt;500 items&lt;/td&gt;
&lt;td&gt;6.468 us&lt;/td&gt;
&lt;td&gt;0.1283 us&lt;/td&gt;
&lt;td&gt;0.1798 us&lt;/td&gt;
&lt;td&gt;0.2823&lt;/td&gt;
&lt;td&gt;885 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Result_Pattern&lt;/td&gt;
&lt;td&gt;750 items&lt;/td&gt;
&lt;td&gt;1.317 us&lt;/td&gt;
&lt;td&gt;0.0124 us&lt;/td&gt;
&lt;td&gt;0.0103 us&lt;/td&gt;
&lt;td&gt;0.2251&lt;/td&gt;
&lt;td&gt;704 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Exception_Throw&lt;/td&gt;
&lt;td&gt;750 items&lt;/td&gt;
&lt;td&gt;58.142 us&lt;/td&gt;
&lt;td&gt;0.5948 us&lt;/td&gt;
&lt;td&gt;0.5564 us&lt;/td&gt;
&lt;td&gt;5.8594&lt;/td&gt;
&lt;td&gt;18665 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Result_Pattern_ValidOnly&lt;/td&gt;
&lt;td&gt;750 items&lt;/td&gt;
&lt;td&gt;8.023 us&lt;/td&gt;
&lt;td&gt;0.1575 us&lt;/td&gt;
&lt;td&gt;0.2675 us&lt;/td&gt;
&lt;td&gt;0.3204&lt;/td&gt;
&lt;td&gt;1011 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Exception_Throw_ValidOnly&lt;/td&gt;
&lt;td&gt;750 items&lt;/td&gt;
&lt;td&gt;10.361 us&lt;/td&gt;
&lt;td&gt;0.2963 us&lt;/td&gt;
&lt;td&gt;0.8738 us&lt;/td&gt;
&lt;td&gt;0.2441&lt;/td&gt;
&lt;td&gt;883 B&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Performance Analysis
&lt;/h3&gt;

&lt;h4&gt;
  
  
  The Failure Path (Error Handling Overhead)
&lt;/h4&gt;

&lt;p&gt;The benchmark shows a clear performance gap when errors occur: the &lt;strong&gt;Result Pattern is approximately 44 times faster&lt;/strong&gt; (~1.3 μs vs. ~58 μs). While a few microseconds might seem small, these costs add up quickly in applications that process thousands of requests per second.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why is the Exception approach slower? (~58 μs)&lt;/strong&gt;&lt;br&gt;
When you &lt;code&gt;throw&lt;/code&gt; an exception, the .NET runtime has to do some heavy lifting:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Stack Walking:&lt;/strong&gt; The runtime must traverse the entire call stack to identify the appropriate &lt;code&gt;catch&lt;/code&gt; block. This requires aggregating metadata for every active method in the current execution thread, which is a CPU-bound process.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Internal Bookkeeping:&lt;/strong&gt; Even if the &lt;code&gt;StackTrace&lt;/code&gt; is never explicitly accessed, the runtime must still capture and store the raw frame data during the throw. This ensures the diagnostic information is available if needed, but it creates immediate overhead.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Memory Allocation:&lt;/strong&gt; Exceptions are heavyweight heap objects. These results show an allocation of &lt;strong&gt;~18.6 KB&lt;/strong&gt; per error. Excessive throwing increases memory pressure and triggers more frequent Garbage Collection (GC) cycles, which can lead to application stutters.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why is the Result Pattern faster? (~1.3 μs)&lt;/strong&gt;&lt;br&gt;
It treats an error like a regular piece of data:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No call-stack traversal is required:&lt;/strong&gt; It doesn't care about the history of method calls; it just returns a simple object.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lightweight:&lt;/strong&gt; It consumes significantly less memory (&lt;strong&gt;~704 B&lt;/strong&gt;). Because &lt;code&gt;Result&amp;lt;T&amp;gt;&lt;/code&gt; is a small value type, it typically avoids the large heap allocations associated with exception objects, though it may still be copied or stored within async state machines depending on usage.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  The Success Path (Normal Execution)
&lt;/h4&gt;

&lt;p&gt;When everything goes right (the &lt;code&gt;ValidOnly&lt;/code&gt; benchmarks), the performance gap disappears. In fact, &lt;strong&gt;the exception approach is slightly faster&lt;/strong&gt; in some cases.&lt;/p&gt;

&lt;p&gt;This is because .NET uses "zero-cost" exceptions. If an error is &lt;strong&gt;never thrown&lt;/strong&gt;, the &lt;code&gt;try/catch&lt;/code&gt; block doesn't slow down your code at all. On the other hand, the Result Pattern has a tiny "success tax" as you always pay a few nanoseconds because the code must wrap the successful answer inside a &lt;code&gt;Result&lt;/code&gt; object. &lt;/p&gt;

&lt;p&gt;The memory usage on the success path is also nearly identical (around 1 KB), showing that during normal operation, the choice of error handling doesn't really impact your app's performance.&lt;/p&gt;

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

&lt;p&gt;The benchmark results clearly demonstrate that the Result Pattern is &lt;strong&gt;substantially faster&lt;/strong&gt; and consumes &lt;strong&gt;significantly less memory&lt;/strong&gt; than exception-based error handling in the &lt;strong&gt;failure path&lt;/strong&gt;. In most scenarios, the Result Pattern is the superior choice for managing expected failures. While it requires more repetitive "failure checks", it provides &lt;strong&gt;predictable behavior and undeniable performance benefits&lt;/strong&gt;. Exact performance characteristics will vary based on how the &lt;code&gt;Result&amp;lt;T&amp;gt;&lt;/code&gt; object is implemented.&lt;/p&gt;

&lt;p&gt;While the exception-based approach often results in &lt;strong&gt;cleaner-looking code&lt;/strong&gt;, it &lt;strong&gt;demands strict discipline&lt;/strong&gt;. Developers must catch only specific exceptions and handle general failures at a central level. Without this rigor, exception-based control flow can quickly devolve into a confusing web of dependencies, forcing developers to dig through lower-level logic just to understand what might go wrong.&lt;/p&gt;

&lt;p&gt;In summary, when failures occur frequently and are a normal part of the business logic, the Result Pattern is the clear winner. However, &lt;strong&gt;exceptions remain the correct tool for truly exceptional, unexpected&lt;/strong&gt; conditions, especially when the diagnostic value of a full stack trace outweighs the performance cost. The &lt;strong&gt;ideal architecture likely uses both&lt;/strong&gt;, choosing the right tool based on failure frequency, performance needs, and domain clarity.&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>benchmark</category>
      <category>csharp</category>
      <category>performance</category>
    </item>
  </channel>
</rss>
