{"authors":[{"name":"Martin Stühmer","url":"https://daily-devops.net/authors/martin/"},{"name":"Jendrik Brack","url":"https://daily-devops.net/authors/jendrik/"}],"description":"Recent content in Code Quality and Clean Code Principles on Daily DevOps \u0026 .NET","favicon":"https://daily-devops.net/images/logo_hu_6465d873dfa490cf.png","feed_url":"https://daily-devops.net/tags/codequality/feed.json","home_page_url":"https://daily-devops.net/tags/codequality/","icon":"https://daily-devops.net/images/logo_hu_5926de77762241ba.png","items":[{"authors":[{"name":"Martin Stühmer","url":"https://daily-devops.net/authors/martin/"}],"content_html":"\u003cp\u003eTwo articles into this series, I\u0026rsquo;ve spent a lot of words describing Past Self, the engineer who left the evidence file, who optimized for the wrong horizon, who handed Future Self the rough work without the context to do it properly.\u003c/p\u003e\n\u003cp\u003eWhat I\u0026rsquo;ve been carefully avoiding is the obvious conclusion.\u003c/p\u003e\n\u003cp\u003eI am Past Self. Right now. Today. The \u003ccode\u003e// TODO\u003c/code\u003e I wrote last Tuesday is already starting to decay. The verbal commitment I made in last week\u0026rsquo;s planning session: \u0026ldquo;we\u0026rsquo;ll revisit that architecture after the next release\u0026rdquo;. It has already begun its quiet journey toward never. The test coverage gap I noted and deprioritized is waiting to become an incident.\u003c/p\u003e\n\u003cp\u003eI know this because I\u0026rsquo;ve read the code Past Self wrote, and I recognize the voice.\u003c/p\u003e\n\u003cp\u003eIt sounds exactly like mine.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"the-promises-ive-made\"\u003e\u003ca href=\"/posts/code-as-legacy-empty-promises/#the-promises-ive-made\" title=\"The Promises I\u0026rsquo;ve Made\"\u003eThe Promises I\u0026rsquo;ve Made\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eI\u0026rsquo;m not going to pretend these are abstract patterns. They\u0026rsquo;re mine.\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003e// TODO: implement proper tiered discount logic\u003c/code\u003e. I wrote that. Three years ago. The \u0026ldquo;proper\u0026rdquo; logic was never defined, never ticketed, never implemented. The method has run in production millions of times with the simplified version. I told myself it was a placeholder. It became the implementation.\u003c/p\u003e\n\u003cp\u003e\u0026ldquo;We\u0026rsquo;ll add observability to this once the service stabilizes\u0026rdquo;: I said that in a meeting in 2023. The service stabilized. The observability never materialized. Six months later we had an incident where the first question was \u0026ldquo;what is this service actually doing right now\u0026rdquo; and the answer was silence. I remembered the promise the moment someone asked the question. I did not say anything in the post-mortem about having made it.\u003c/p\u003e\n\u003cp\u003e\u0026ldquo;I\u0026rsquo;ll write the integration tests for this edge case next sprint\u0026rdquo;. The edge case was in a payment calculation, the kind of thing where being wrong has a number attached to it. Next sprint arrived with different priorities. The sprint after that as well. The test was never written. The bug in the edge case was found by a customer, not by us.\u003c/p\u003e\n\u003cp\u003eThese aren\u0026rsquo;t cautionary tales about other engineers. They\u0026rsquo;re mine. The damage was real, the promises were mine, and the fact that I meant them at the time doesn\u0026rsquo;t change what Future Self found when he arrived.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"what-meaning-it-is-worth\"\u003e\u003ca href=\"/posts/code-as-legacy-empty-promises/#what-meaning-it-is-worth\" title=\"What \u0026ldquo;Meaning It\u0026rdquo; Is Worth\"\u003eWhat \u0026ldquo;Meaning It\u0026rdquo; Is Worth\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eThis is the part that took me the longest to accept: intent is not load-bearing.\u003c/p\u003e\n\u003cp\u003eWhen I wrote \u003ccode\u003e// TODO: fix this properly\u003c/code\u003e, I genuinely intended to come back to it. When I said \u0026ldquo;we\u0026rsquo;ll refactor after the release,\u0026rdquo; I believed, in that moment, that we would. I wasn\u0026rsquo;t lying. I was optimistic, or under pressure, or operating with a timeline I thought was realistic.\u003c/p\u003e\n\u003cp\u003eBut Future Self doesn\u0026rsquo;t inherit my intentions. He inherits the code.\u003c/p\u003e\n\u003cp\u003eHe doesn\u0026rsquo;t know that I meant it. He doesn\u0026rsquo;t know that the promise was sincere. He finds a \u003ccode\u003e// TODO\u003c/code\u003e comment with no ticket, no context, no owner, and no indication of how dangerous the thing it describes actually is. He finds a service with no observability and has to make decisions in the dark:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003ecatch\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eException\u003c/span\u003e \u003cspan class=\"n\"\u003eex\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e// TODO: proper logging\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eConsole\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eWriteLine\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eex\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eMessage\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"kc\"\u003enull\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThat \u003ccode\u003ereturn null\u003c/code\u003e is now someone else\u0026rsquo;s NullReferenceException three call frames up, with no stack trace connecting it back here, and no log entry that tells Future Self what the original exception was. He finds a payment calculation with an untested edge case and either notices the gap (in which case he has to stop what he\u0026rsquo;s doing and fix it) or doesn\u0026rsquo;t notice, in which case the customer finds it.\u003c/p\u003e\n\u003cp\u003eMy intentions are invisible to Future Self. What I left behind is not.\u003c/p\u003e\n\u003cp\u003eThat asymmetry is the thing I couldn\u0026rsquo;t keep ignoring.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"the-decision\"\u003e\u003ca href=\"/posts/code-as-legacy-empty-promises/#the-decision\" title=\"The Decision\"\u003eThe Decision\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eI\u0026rsquo;m done with empty promises.\u003c/p\u003e\n\u003cp\u003eNot in the sense of \u0026ldquo;I will now be perfect and never defer anything again\u0026rdquo;. That\u0026rsquo;s just a different kind of empty promise. I mean something more specific: I\u0026rsquo;m done using \u003ccode\u003e// TODO\u003c/code\u003e as a substitute for a decision, and I\u0026rsquo;m done making verbal commitments about future work that has no owner, no trigger, and no cost attached to not delivering.\u003c/p\u003e\n\u003cp\u003eThe shift is smaller than it sounds, and it took me longer than I\u0026rsquo;d like to admit to make it.\u003c/p\u003e\n\u003cp\u003eA \u003ccode\u003e// TODO\u003c/code\u003e without a tracked issue is not a note: it\u0026rsquo;s a lie I\u0026rsquo;m telling Future Self about my intentions. If I can\u0026rsquo;t take sixty seconds to open a ticket, I don\u0026rsquo;t actually believe this is worth doing. So either I create the ticket and reference it, or I delete the comment and accept that this is the implementation. Not both.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// Before: a promise to no one, tracked nowhere\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// TODO: implement proper tiered discount logic\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003eorder\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eTotal\u003c/span\u003e \u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"m\"\u003e1000\u003c/span\u003e \u003cspan class=\"p\"\u003e?\u003c/span\u003e \u003cspan class=\"n\"\u003eorder\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eTotal\u003c/span\u003e \u003cspan class=\"p\"\u003e*\u003c/span\u003e \u003cspan class=\"m\"\u003e0.1\u003c/span\u003e\u003cspan class=\"n\"\u003em\u003c/span\u003e \u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"m\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// After: a decision, documented\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// Simplified discount (full tiered logic tracked in #847)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003eorder\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eTotal\u003c/span\u003e \u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"m\"\u003e1000\u003c/span\u003e \u003cspan class=\"p\"\u003e?\u003c/span\u003e \u003cspan class=\"n\"\u003eorder\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eTotal\u003c/span\u003e \u003cspan class=\"p\"\u003e*\u003c/span\u003e \u003cspan class=\"m\"\u003e0.1\u003c/span\u003e\u003cspan class=\"n\"\u003em\u003c/span\u003e \u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"m\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe code is identical. The difference is honesty. Issue #847 exists, has context, can be prioritized or closed as \u0026ldquo;won\u0026rsquo;t fix.\u0026rdquo; The \u003ccode\u003e// TODO\u003c/code\u003e was a gesture. The issue reference is a commitment that can be held.\u003c/p\u003e\n\u003cp\u003e\u0026ldquo;We\u0026rsquo;ll refactor after the release\u0026rdquo; needs a condition that actually fires, not a timeline that slides. \u0026ldquo;We revisit this when we add the second tenant\u0026rdquo; fires when it fires or it doesn\u0026rsquo;t. If the second tenant never comes, the decision was right. \u0026ldquo;Next sprint\u0026rdquo; never arrives. Conditions arrive or they don\u0026rsquo;t. That\u0026rsquo;s the difference between a trigger and a wish.\u003c/p\u003e\n\u003cp\u003eAnd missing tests aren\u0026rsquo;t a detail I\u0026rsquo;ll get to later. If the test is worth writing, the feature isn\u0026rsquo;t done. That\u0026rsquo;s a discipline question, not a time question. Pretending it\u0026rsquo;s a time question is how the payment edge case goes untested for two years. What I do instead is leave the skeleton visible:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"na\"\u003e[Fact(Skip = \u0026#34;Edge case: negative discount on refunded orders, see #912\u0026#34;)]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"n\"\u003eTask\u003c/span\u003e \u003cspan class=\"n\"\u003eApplyDiscount_OnRefundedOrder_ShouldNotProduceNegativeTotal\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003ethrow\u003c/span\u003e \u003cspan class=\"k\"\u003enew\u003c/span\u003e \u003cspan class=\"n\"\u003eNotImplementedException\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis test doesn\u0026rsquo;t pass. It doesn\u0026rsquo;t even run. But it exists, it has a ticket reference, and it fails loudly if someone removes the \u003ccode\u003eSkip\u003c/code\u003e before the implementation is done. The gap is visible, not implicit.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"what-this-actually-costs\"\u003e\u003ca href=\"/posts/code-as-legacy-empty-promises/#what-this-actually-costs\" title=\"What This Actually Costs\"\u003eWhat This Actually Costs\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eI want to be honest about something: this decision is not free.\u003c/p\u003e\n\u003cp\u003eMaking it real has friction. Creating a ticket instead of writing a comment takes longer in the moment, not much, but enough to feel it when you\u0026rsquo;re under pressure and someone is waiting for you to ship. Saying \u0026ldquo;I\u0026rsquo;m not going to commit to that refactor without a trigger condition\u0026rdquo; in a planning meeting is harder than saying \u0026ldquo;we\u0026rsquo;ll handle that in Q3.\u0026rdquo; Treating missing tests as a blocker on the definition of done means occasionally shipping later than a version that cuts corners.\u003c/p\u003e\n\u003cp\u003eThe friction is real. What I\u0026rsquo;ve had to accept is that the friction now is cheaper than the silence later.\u003c/p\u003e\n\u003cp\u003eBecause the alternative isn\u0026rsquo;t \u0026ldquo;no friction.\u0026rdquo; The alternative is the post-mortem where nobody mentions the promise that wasn\u0026rsquo;t kept. It\u0026rsquo;s the \u003ccode\u003e// TODO\u003c/code\u003e comment that becomes a fossil, referenced by code that depends on the thing it was promising to fix, until Future Self doesn\u0026rsquo;t know if he can touch it without breaking something he can\u0026rsquo;t see. It\u0026rsquo;s the incident that happens because the edge case was on someone\u0026rsquo;s list.\u003c/p\u003e\n\u003cp\u003eThat friction compounds. The friction of honesty now is roughly constant. The friction of deferred promises grows every month they age.\u003c/p\u003e\n\u003cp\u003eThere\u0026rsquo;s also something harder to quantify: what it does to the people around you. A team that\u0026rsquo;s learned to discount verbal commitments, because they\u0026rsquo;ve seen enough \u0026ldquo;we\u0026rsquo;ll fix that after the release\u0026rdquo; promises expire, stops trusting the ones you mean. You lose the ability to say \u0026ldquo;this will get done\u0026rdquo; and have it land. Past Self made enough empty promises that Future Self, and the people who work with me, have to spend some effort evaluating which commitments are real.\u003c/p\u003e\n\u003cp\u003eThat\u0026rsquo;s a cost I inflicted by being careless with my word. Rebuilding it takes longer than the individual tickets I didn\u0026rsquo;t create.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"what-future-self-deserves\"\u003e\u003ca href=\"/posts/code-as-legacy-empty-promises/#what-future-self-deserves\" title=\"What Future Self Deserves\"\u003eWhat Future Self Deserves\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eIn \u003ca href=\"/posts/code-as-legacy-past-self/\"\u003epart two\u003c/a\u003e of this series, I described Future Self as the person who inherits whatever I ship. I know who he is. I know what his days look like. I know what it feels like to find a codebase full of \u003ccode\u003e// TODO\u003c/code\u003e comments with no context, verbal promises that evaporated, coverage gaps that became incidents.\u003c/p\u003e\n\u003cp\u003eI know because I am him, regularly, looking at code Past Self wrote.\u003c/p\u003e\n\u003cp\u003eHe\u0026rsquo;ll show up at 11 PM because something is broken in production, and the first thing he\u0026rsquo;ll hit is a method that has been quietly wrong for two years because the test that would have caught it was on someone\u0026rsquo;s list. He\u0026rsquo;ll have thirty minutes to understand a decision Past Self made under pressure, with no comment, no ticket, no trail. Just a magic constant and a hunch that something here used to make sense:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003eprivate\u003c/span\u003e \u003cspan class=\"kd\"\u003estatic\u003c/span\u003e \u003cspan class=\"k\"\u003ereadonly\u003c/span\u003e \u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"n\"\u003e_timeout\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"m\"\u003e30000\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThirty seconds. Why thirty? Was it measured? Is it a client SLA? Is it a guess? Is it still right? Future Self has no way to know. He can change it and hope, or leave it and wonder. Past Self knew the answer once. He just didn\u0026rsquo;t write it down. He\u0026rsquo;ll look at the \u003ccode\u003e// TODO\u003c/code\u003e in the error-handling path and wonder, correctly, whether this is load-bearing neglect or just noise.\u003c/p\u003e\n\u003cp\u003eHe deserves better than my good intentions. Not because he\u0026rsquo;s fragile. He isn\u0026rsquo;t. But because every hour he spends excavating my reasoning is an hour he isn\u0026rsquo;t spending building something. Every incident that traces back to a promise I didn\u0026rsquo;t keep is a cost he didn\u0026rsquo;t ask to carry.\u003c/p\u003e\n\u003cp\u003eHe deserves decisions that were documented well enough to be evaluated against the current situation and changed if needed. He deserves to know, when he finds a \u003ccode\u003e// TODO\u003c/code\u003e, whether that represents genuine deferred work tracked somewhere or just a comment Past Self left as a gesture of good faith that nobody else can redeem. He deserves code that doesn\u0026rsquo;t require trust in a person who\u0026rsquo;s no longer present.\u003c/p\u003e\n\u003cp\u003eThat\u0026rsquo;s not heroism. It\u0026rsquo;s just honesty about what a promise is.\u003c/p\u003e\n\u003cp\u003eA promise you make without infrastructure to keep it isn\u0026rsquo;t a promise. It\u0026rsquo;s a note to yourself that you\u0026rsquo;re leaving someone else\u0026rsquo;s problem for later. I\u0026rsquo;ve left enough of those. Future Self has been cleaning them up for years, and he\u0026rsquo;ll inherit a few more before I get this right.\u003c/p\u003e\n\u003cp\u003eBut I\u0026rsquo;m done adding to the pile deliberately. The accidental ones are unavoidable. You can\u0026rsquo;t know what you don\u0026rsquo;t know yet. The deliberate ones, the \u003ccode\u003e// TODO\u003c/code\u003e you write because it\u0026rsquo;s faster, the commitment you make because it\u0026rsquo;s easier than having the harder conversation right now: those are the ones I\u0026rsquo;m done with.\u003c/p\u003e\n\u003cp\u003eFuture Self is going to inherit my code either way. The question is what kind of Past Self I\u0026rsquo;m choosing to be for him.\u003c/p\u003e\n\u003cp\u003e\u003cem\u003eThis is part three of the \u003ca href=\"/posts/code-as-legacy/\"\u003eCode as Legacy\u003c/a\u003e series. \u003ca href=\"/posts/code-as-legacy/\"\u003ePart one\u003c/a\u003e covers what \u0026ldquo;building carefully\u0026rdquo; actually means in practice. \u003ca href=\"/posts/code-as-legacy-past-self/\"\u003ePart two\u003c/a\u003e is about Past Self, the person who made the mess.\u003c/em\u003e\u003c/p\u003e\n","date_modified":"2026-05-28T17:06:53+02:00","date_published":"2026-05-28T17:00:00+02:00","id":"https://daily-devops.net/posts/code-as-legacy-empty-promises/","language":"en","summary":"// TODO: fix this properly. We'll refactor after the release. Tests when the API stabilizes. I've made every one of these promises. I'm done.\n","tags":["softwareengineering","codequality","technicaldebt","architecture","dotnet","csharp","bestpractices"],"title":"I'm Done Making Empty Promises\n","url":"https://daily-devops.net/posts/code-as-legacy-empty-promises/"},{"authors":[{"name":"Martin Stühmer","url":"https://daily-devops.net/authors/martin/"}],"content_html":"\u003cp\u003eThere\u0026rsquo;s an engineer I\u0026rsquo;ve worked with for nearly twenty years. He\u0026rsquo;s technically skilled, reasonably intelligent, often under pressure, and thoroughly convinced that Future Self will clean up whatever he leaves behind.\u003c/p\u003e\n\u003cp\u003eHis name is Past Self. He\u0026rsquo;s my arch enemy. And he writes all my oldest code.\u003c/p\u003e\n\u003cp\u003eThis is the second part of the \u003ca href=\"/posts/code-as-legacy/\"\u003e\u003cem\u003eCode as Legacy\u003c/em\u003e\u003c/a\u003e series. In part one, I made the case that code is a legacy (something you leave behind), and that the difference between a gift and a burden is almost entirely determined by how carefully it was built. This part is about what happens when you weren\u0026rsquo;t careful. About the person responsible. And about the uncomfortable realization that Past Self and Future Self are the same person, separated by time and context and the slow erosion of memory.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"past-self-characterized\"\u003e\u003ca href=\"/posts/code-as-legacy-past-self/#past-self-characterized\" title=\"Past Self, Characterized\"\u003ePast Self, Characterized\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003ePast Self is not a villain. That\u0026rsquo;s the first thing to understand, and the most annoying one.\u003c/p\u003e\n\u003cp\u003eHe was usually working under real constraints: a deadline that wasn\u0026rsquo;t negotiable, a requirement that kept changing, a codebase he inherited and didn\u0026rsquo;t fully understand. He made the trade-offs that made sense at the time, with the information he had. I know this because I was there. I remember the Jira ticket. I remember the conversation that ended with \u0026ldquo;just get it working for now.\u0026rdquo;\u003c/p\u003e\n\u003cp\u003eWhat Past Self lacked wasn\u0026rsquo;t intelligence or intent. He lacked two things: imagination and humility.\u003c/p\u003e\n\u003cp\u003eHe couldn\u0026rsquo;t imagine that the code would still be running three years later in a context he\u0026rsquo;d never anticipated. And he wasn\u0026rsquo;t humble enough to admit, at the moment of the shortcut, that he was making a permanent decision while pretending it was temporary.\u003c/p\u003e\n\u003cp\u003eI know this because I still catch myself doing it.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"the-evidence-file\"\u003e\u003ca href=\"/posts/code-as-legacy-past-self/#the-evidence-file\" title=\"The Evidence File\"\u003eThe Evidence File\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eEvery codebase I\u0026rsquo;ve worked on long enough has what I mentally call an evidence file: a collection of decisions Past Self made that Future Self is currently paying for. Here are a few entries from mine.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eThe connection string that became a foundation.\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eEarly in a project, there was a SQL connection string in \u003ccode\u003eappsettings.json\u003c/code\u003e. Direct, clear, no abstraction. It worked. Nobody moved it when the project grew. Then it got referenced in six places. Then someone built a multi-tenancy feature that assumed a single database. Then we needed to support read replicas. By the time Future Self arrived at this problem, the connection string wasn\u0026rsquo;t a configuration value anymore. It was structural. Changing it meant touching half the service layer.\u003c/p\u003e\n\u003cp\u003ePast Self had forty seconds to introduce an abstraction. He didn\u0026rsquo;t, because \u0026ldquo;we\u0026rsquo;ll refactor when we need to.\u0026rdquo; Future Self needed two sprints.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eThe \u003ccode\u003ebool\u003c/code\u003e parameter that grew up.\u003c/strong\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// Past Self, six years ago\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kd\"\u003easync\u003c/span\u003e \u003cspan class=\"n\"\u003eTask\u003c/span\u003e \u003cspan class=\"n\"\u003eSendNotificationAsync\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"n\"\u003euserId\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"kt\"\u003ebool\u003c/span\u003e \u003cspan class=\"n\"\u003eisUrgent\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eReasonable at the time. Two states, clear semantics. Then came \u0026ldquo;also high priority but not urgent,\u0026rdquo; then \u0026ldquo;urgent but silent,\u0026rdquo; then \u0026ldquo;urgent and high-priority and batched.\u0026rdquo; The method signature became:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// Future Self, inheriting the mess\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kd\"\u003easync\u003c/span\u003e \u003cspan class=\"n\"\u003eTask\u003c/span\u003e \u003cspan class=\"n\"\u003eSendNotificationAsync\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"n\"\u003euserId\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kt\"\u003ebool\u003c/span\u003e \u003cspan class=\"n\"\u003eisUrgent\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kt\"\u003ebool\u003c/span\u003e \u003cspan class=\"n\"\u003eisHighPriority\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kt\"\u003ebool\u003c/span\u003e \u003cspan class=\"n\"\u003eisSilent\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kt\"\u003ebool\u003c/span\u003e \u003cspan class=\"n\"\u003eisBatched\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eFive booleans. All positional. All looking identical at every call site. All impossible to read without hovering over the method signature. Past Self\u0026rsquo;s \u003ccode\u003ebool\u003c/code\u003e was the reasonable starting point. The problem was that nobody stopped to redesign when it started multiplying:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// What Future Self eventually had to write anyway\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kd\"\u003easync\u003c/span\u003e \u003cspan class=\"n\"\u003eTask\u003c/span\u003e \u003cspan class=\"n\"\u003eSendNotificationAsync\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"n\"\u003euserId\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eNotificationOptions\u003c/span\u003e \u003cspan class=\"n\"\u003eoptions\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kd\"\u003esealed\u003c/span\u003e \u003cspan class=\"k\"\u003erecord\u003c/span\u003e \u003cspan class=\"nc\"\u003eNotificationOptions\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eNotificationPriority\u003c/span\u003e \u003cspan class=\"n\"\u003ePriority\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eNotificationPriority\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eNormal\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kt\"\u003ebool\u003c/span\u003e \u003cspan class=\"n\"\u003eSilent\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"kc\"\u003efalse\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kt\"\u003ebool\u003c/span\u003e \u003cspan class=\"n\"\u003eBatched\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"kc\"\u003efalse\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kd\"\u003eenum\u003c/span\u003e \u003cspan class=\"n\"\u003eNotificationPriority\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"n\"\u003eNormal\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eHigh\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eUrgent\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis was always the right shape. Past Self just didn\u0026rsquo;t know it yet, and neither did I, when I was him.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eThe log statement that ate the disk.\u003c/strong\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003e_logger\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eLogInformation\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;Processing order {OrderId}: {@Order}\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eorderId\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eorder\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003ccode\u003e{@Order}\u003c/code\u003e serializes the entire object. Including the \u003ccode\u003eCustomer\u003c/code\u003e navigation property. Including the \u003ccode\u003eCustomer.Orders\u003c/code\u003e collection. Including each of those orders\u0026rsquo; \u003ccode\u003eCustomer\u003c/code\u003e navigation properties. On a Tuesday morning with normal traffic: fine. On Black Friday, with order volume at 40× normal: the logging pipeline wrote 800 MB of JSON per minute, filled the disk, and took down the service.\u003c/p\u003e\n\u003cp\u003ePast Self was debugging something. He wanted to see the full order object. He committed the log line and forgot it was there.\u003c/p\u003e\n\u003cp\u003eFuture Self found it during a post-mortem at 3 AM.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"why-you-cant-fire-past-self\"\u003e\u003ca href=\"/posts/code-as-legacy-past-self/#why-you-cant-fire-past-self\" title=\"Why You Can\u0026rsquo;t Fire Past Self\"\u003eWhy You Can\u0026rsquo;t Fire Past Self\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eThe obvious response to all of this is: why didn\u0026rsquo;t you fix it at the time? Why didn\u0026rsquo;t you write it correctly from the start?\u003c/p\u003e\n\u003cp\u003eSometimes the answer is genuine negligence, and I won\u0026rsquo;t pretend otherwise. But more often, Past Self was operating under a set of conditions that made the decision locally rational even if it was globally wrong:\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHe didn\u0026rsquo;t have the full picture.\u003c/strong\u003e The connection string was in \u003ccode\u003eappsettings.json\u003c/code\u003e because nobody had decided on a multi-tenancy strategy yet. The \u003ccode\u003ebool\u003c/code\u003e was \u003ccode\u003ebool\u003c/code\u003e because the requirements only described two states. Decisions that look obviously wrong in retrospect were made before the retrospect existed.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHe was optimizing for the wrong horizon.\u003c/strong\u003e Software development has strong incentives to ship now and a much weaker feedback loop for the cost of what you shipped. Past Self felt the deadline. He did not feel the two-sprint refactor that happened three years after he\u0026rsquo;d moved on to a different feature.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHe told himself it was temporary.\u003c/strong\u003e This is the one I find hardest to forgive, because it\u0026rsquo;s the most deliberate self-deception. \u0026ldquo;We\u0026rsquo;ll clean this up\u0026rdquo; is a phrase Past Self used as a get-out-of-jail card, knowing full well who would be holding the bill.\u003c/p\u003e\n\u003cp\u003eThat person is me. Future Self is not some abstract successor or a colleague who joins the team later. Future Self is me, roughly twelve months from now, with no memory of what I was thinking today, inheriting whatever I ship this week. He doesn\u0026rsquo;t get a briefing. He gets a diff.\u003c/p\u003e\n\u003cp\u003eYou can\u0026rsquo;t fire Past Self because he\u0026rsquo;s already gone. All you can do is clean up after him, try not to become him, and (this is the part that matters) think carefully about what you\u0026rsquo;re about to hand yourself.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"the-asymmetry-problem\"\u003e\u003ca href=\"/posts/code-as-legacy-past-self/#the-asymmetry-problem\" title=\"The Asymmetry Problem\"\u003eThe Asymmetry Problem\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eHere\u0026rsquo;s what makes Past Self so dangerous: the cost of his decisions is borne entirely by Future Self.\u003c/p\u003e\n\u003cp\u003eThis asymmetry is not unique to software. It shows up everywhere that consequences are deferred: environmental policy, infrastructure maintenance, pension systems. The person who makes the decision and the person who lives with it are not the same person. This creates a systematic bias toward decisions that look good now and cost later.\u003c/p\u003e\n\u003cp\u003eIn software, the version of this that I see most often is what I\u0026rsquo;d call \u003cstrong\u003ethe invisible tax\u003c/strong\u003e. Past Self doesn\u0026rsquo;t add a line item to the budget for his shortcuts. He doesn\u0026rsquo;t log the future cost anywhere. Future Self just finds, gradually, that everything is harder than it should be. Features take longer. Bugs are more frequent. Changes in one place break things in unexpected places. Nobody points at a specific decision and calls it out, because Past Self\u0026rsquo;s decisions are distributed across thousands of lines of code, each one small and deniable, each one contributing to a codebase that resists change at every turn.\u003c/p\u003e\n\u003cp\u003eThe tax is real. It\u0026rsquo;s just invisible until you try to spend.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"what-future-self-deserves\"\u003e\u003ca href=\"/posts/code-as-legacy-past-self/#what-future-self-deserves\" title=\"What Future Self Deserves\"\u003eWhat Future Self Deserves\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eThis is the part Past Self consistently gets wrong: Future Self isn\u0026rsquo;t an abstraction. He\u0026rsquo;s me, a year from now, with no memory of what I was thinking today. He inherits my shortcuts the same way I inherited Past Self\u0026rsquo;s, not as a debt somebody else took on, but as his problem to solve with whatever time and energy he has left after dealing with everything else.\u003c/p\u003e\n\u003cp\u003eHe\u0026rsquo;ll find the code in the middle of something else. He\u0026rsquo;ll have thirty minutes to understand what I wrote and why, fix whatever broke, and get out without making it worse. He won\u0026rsquo;t have my context. He won\u0026rsquo;t have the Slack thread. He won\u0026rsquo;t have the meeting where I decided the timeout should be 30 seconds because the legacy service was slow and the client couldn\u0026rsquo;t wait for a proper fix.\u003c/p\u003e\n\u003cp\u003eWhat he deserves is code that doesn\u0026rsquo;t require archaeology to understand.\u003c/p\u003e\n\u003cp\u003eThis doesn\u0026rsquo;t mean over-documentation. It doesn\u0026rsquo;t mean exhaustive comments. It means code that makes its assumptions visible, surfaces its constraints, and fails clearly when something goes wrong. It means the difference between:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// Past Self\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003etimeout\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"m\"\u003e30000\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eand:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// Future Self can understand this without a Slack thread\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// Matches the SLA of the legacy ReportService endpoint (see ADR-042)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003eprivate\u003c/span\u003e \u003cspan class=\"kd\"\u003estatic\u003c/span\u003e \u003cspan class=\"k\"\u003ereadonly\u003c/span\u003e \u003cspan class=\"n\"\u003eTimeSpan\u003c/span\u003e \u003cspan class=\"n\"\u003eReportGenerationTimeout\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eTimeSpan\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eFromSeconds\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"m\"\u003e30\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eOne is a magic number with no explanation. The other is a decision with enough context that Future Self can evaluate whether the constraint still applies, and change it if it doesn\u0026rsquo;t.\u003c/p\u003e\n\u003cp\u003eThe comment here is justified precisely because it encodes \u003cem\u003ewhy\u003c/em\u003e, not \u003cem\u003ewhat\u003c/em\u003e. The what is obvious. The why was in someone\u0026rsquo;s head, and now it isn\u0026rsquo;t.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"the-uncomfortable-continuity\"\u003e\u003ca href=\"/posts/code-as-legacy-past-self/#the-uncomfortable-continuity\" title=\"The Uncomfortable Continuity\"\u003eThe Uncomfortable Continuity\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eI\u0026rsquo;ve been writing about Past Self as if he\u0026rsquo;s a separate person. He isn\u0026rsquo;t.\u003c/p\u003e\n\u003cp\u003eEvery piece of code I write today becomes part of Past Self\u0026rsquo;s legacy within the year. The shortcut I take this afternoon because the sprint ends on Friday will be Future Self\u0026rsquo;s archaeology project sometime in 2027. The \u003ccode\u003e// TODO: handle this properly\u003c/code\u003e I leave in because I\u0026rsquo;m tired becomes the thing that nobody ever comes back to fix.\u003c/p\u003e\n\u003cp\u003eThe uncomfortable truth is that Past Self is not a character from my past. He\u0026rsquo;s a character I\u0026rsquo;m actively writing right now: every time I ship something I know isn\u0026rsquo;t quite right, every time I leave a decision implicit that should be explicit, every time I tell myself Future Self will deal with it.\u003c/p\u003e\n\u003cp\u003eHe won\u0026rsquo;t deal with it. He\u0026rsquo;ll be too busy dealing with something else Past Self left behind.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"making-peace-without-excusing\"\u003e\u003ca href=\"/posts/code-as-legacy-past-self/#making-peace-without-excusing\" title=\"Making Peace Without Excusing\"\u003eMaking Peace Without Excusing\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eI\u0026rsquo;ve made peace with Past Self, more or less. Not because he didn\u0026rsquo;t cause damage. He did, measurably, in sprints and in incident hours and in engineers who got frustrated and left. But because the alternative to making peace is a kind of paralysis that doesn\u0026rsquo;t help anyone.\u003c/p\u003e\n\u003cp\u003eWhat I haven\u0026rsquo;t done is excuse him.\u003c/p\u003e\n\u003cp\u003eMaking peace means: I understand why you made those decisions. I understand the constraints, the pressure, the incomplete picture. I know you weren\u0026rsquo;t trying to create problems.\u003c/p\u003e\n\u003cp\u003eNot excusing means: you still should have known better on some of this. The magic numbers. The deferred decisions you knew were permanent. The \u003ccode\u003e// TODO\u003c/code\u003e comments you never intended to come back to. Those weren\u0026rsquo;t forced on you by constraints. Those were choices.\u003c/p\u003e\n\u003cp\u003eThe difference matters because excusing everything Past Self did means never learning anything from him. And making peace means I can look at the evidence file without anger, figure out what\u0026rsquo;s worth fixing and what isn\u0026rsquo;t, and move forward.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"what-im-handing-future-self\"\u003e\u003ca href=\"/posts/code-as-legacy-past-self/#what-im-handing-future-self\" title=\"What I\u0026rsquo;m Handing Future Self\"\u003eWhat I\u0026rsquo;m Handing Future Self\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eHere\u0026rsquo;s where I get to confess: this article is partly an accountability document.\u003c/p\u003e\n\u003cp\u003eI maintain systems that have Past Self\u0026rsquo;s fingerprints all over them. Some of it I\u0026rsquo;ve fixed. Some of it I\u0026rsquo;ve accepted as the cost of the original decisions. Some of it I\u0026rsquo;m actively making worse right now, probably, in ways I can\u0026rsquo;t see yet.\u003c/p\u003e\n\u003cp\u003eWhat I\u0026rsquo;m trying to do differently (and what I\u0026rsquo;d argue is the only practical response to the Past Self problem) is to make the implicit explicit, every time, even when it\u0026rsquo;s inconvenient. Not to write more code, but to write code that explains itself. To make the assumptions visible, the constraints documented, the failure modes clear.\u003c/p\u003e\n\u003cp\u003eFuture Self will still find things Past Self left behind. That\u0026rsquo;s inevitable. What I can control is whether Future Self finds them with enough context to understand what he\u0026rsquo;s looking at, or whether he has to figure it out from first principles at 3 AM while something is broken in production.\u003c/p\u003e\n\u003cp\u003eThe code I write today is a letter to myself: to someone who will have no idea what I was thinking, who will be under pressure, who will need to understand this quickly and get out cleanly. I know who Future Self is. I know what his days look like, because they look like mine.\u003c/p\u003e\n\u003cp\u003eI\u0026rsquo;m trying to write him clearer letters.\u003c/p\u003e\n\u003cp\u003e\u003cem\u003eThis is part two of the \u003ca href=\"/posts/code-as-legacy/\"\u003eCode as Legacy\u003c/a\u003e series. Part one covers what \u0026ldquo;building carefully\u0026rdquo; actually means in practice.\u003c/em\u003e\u003c/p\u003e\n","date_modified":"2026-05-26T17:06:12+02:00","date_published":"2026-05-26T17:00:00+02:00","id":"https://daily-devops.net/posts/code-as-legacy-past-self/","language":"en","summary":"Past Self is the most dangerous engineer on your team: skilled, well-intentioned, and gone when the bill comes due. This is about the code he left behind.\n","tags":["softwareengineering","codequality","technicaldebt","architecture","dotnet","csharp","bestpractices"],"title":"My Biggest Enemy Writes My Code\n","url":"https://daily-devops.net/posts/code-as-legacy-past-self/"},{"authors":[{"name":"Martin Stühmer","url":"https://daily-devops.net/authors/martin/"}],"content_html":"\u003cp\u003eMy author bio ends with a sentence I\u0026rsquo;ve been carrying for years:\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e\u003cem\u003eThe code you create is a valuable legacy, so it\u0026rsquo;s important to build it carefully.\u003c/em\u003e\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003eIt sounds like something you\u0026rsquo;d frame and hang above a whiteboard. It isn\u0026rsquo;t. It\u0026rsquo;s the distilled result of watching systems survive their authors, outlive their requirements, and eventually become someone else\u0026rsquo;s problem — sometimes that someone else being me, years later, at 2 AM.\u003c/p\u003e\n\u003cp\u003eThis article is the story behind that sentence.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"what-legacy-actually-means\"\u003e\u003ca href=\"/posts/code-as-legacy/#what-legacy-actually-means\" title=\"What \u0026ldquo;Legacy\u0026rdquo; Actually Means\"\u003eWhat \u0026ldquo;Legacy\u0026rdquo; Actually Means\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eThe word legacy in software has been colonized by negativity. \u0026ldquo;Legacy system\u0026rdquo; means old, unmaintainable, the thing you inherited and wish you hadn\u0026rsquo;t. People say it like an apology.\u003c/p\u003e\n\u003cp\u003eThat\u0026rsquo;s not how I use it.\u003c/p\u003e\n\u003cp\u003eA legacy is what you leave behind. It can be a gift or a burden — and the difference is almost entirely determined by how carefully it was built. The Colosseum is a legacy. So is every \u003ccode\u003estatic readonly Dictionary\u0026lt;string, object\u0026gt;\u003c/code\u003e that someone thread-unsafe-cached against a singleton in 2014 and then shipped to production without tests.\u003c/p\u003e\n\u003cp\u003eBoth will outlast their creators. Only one will be admired.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"the-compounding-cost-of-carelessness\"\u003e\u003ca href=\"/posts/code-as-legacy/#the-compounding-cost-of-carelessness\" title=\"The Compounding Cost of Carelessness\"\u003eThe Compounding Cost of Carelessness\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eIn nearly twenty years of .NET systems, the most expensive decisions I\u0026rsquo;ve witnessed weren\u0026rsquo;t made by incompetent people. They were made by skilled engineers in a hurry, under pressure, with incomplete context, who told themselves: \u003cem\u003e\u0026ldquo;We\u0026rsquo;ll clean this up later.\u0026rdquo;\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003eLater never comes. Or rather, it comes in the form of an incident.\u003c/p\u003e\n\u003cp\u003eConsider what \u0026ldquo;building carefully\u0026rdquo; actually costs at the moment of creation:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eEnabling \u003ca href=\"https://learn.microsoft.com/en-us/dotnet/csharp/nullable-references\" target=\"_blank\" rel=\"noopener external noreferrer\"\u003enullable reference types\u003c/a\u003e in a new project: \u003cstrong\u003eminutes\u003c/strong\u003e\u003c/li\u003e\n\u003cli\u003eEnabling them three years later across 200,000 lines: \u003cstrong\u003emonths\u003c/strong\u003e\u003c/li\u003e\n\u003cli\u003eAdding an \u003ccode\u003e.editorconfig\u003c/code\u003e with analyzer rules at project start: \u003cstrong\u003eone afternoon\u003c/strong\u003e\u003c/li\u003e\n\u003cli\u003eEnforcing consistency across an organic codebase after four teams touched it: \u003cstrong\u003ea quarter\u003c/strong\u003e\u003c/li\u003e\n\u003cli\u003eWriting a proper \u003ccode\u003eCancellationToken\u003c/code\u003e propagation pattern from the start: \u003cstrong\u003etrivial\u003c/strong\u003e\u003c/li\u003e\n\u003cli\u003eRetrofitting cancellation into an async call tree that never anticipated it: \u003cstrong\u003esurgical, risky, and slow\u003c/strong\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\n\n\n\n\u003ch3 id=\"the-cancellation-token-you-should-have-added\"\u003e\u003ca href=\"/posts/code-as-legacy/#the-cancellation-token-you-should-have-added\" title=\"The Cancellation Token You Should Have Added\"\u003eThe Cancellation Token You Should Have Added\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eThe CancellationToken case is worth pausing on, because it\u0026rsquo;s so easy to defer and so expensive when you do. A call tree without cancellation looks harmless:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kd\"\u003easync\u003c/span\u003e \u003cspan class=\"n\"\u003eTask\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eReport\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003eGenerateReportAsync\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"n\"\u003ecustomerId\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003eorders\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003e_orderRepo\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eGetOrdersAsync\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ecustomerId\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003einvoices\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003e_invoiceRepo\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eGetInvoicesAsync\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ecustomerId\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003epdf\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003e_pdfService\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eRenderAsync\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eorders\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003einvoices\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"k\"\u003enew\u003c/span\u003e \u003cspan class=\"n\"\u003eReport\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003epdf\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eA year later, HTTP timeouts fire while the PDF renderer keeps allocating and the database queries keep running — because there\u0026rsquo;s nothing to stop them. Retrofitting cancellation now means touching every signature in the chain, every interface, every test, every caller. Versus what \u0026ldquo;careful at creation time\u0026rdquo; looked like:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kd\"\u003easync\u003c/span\u003e \u003cspan class=\"n\"\u003eTask\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eReport\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003eGenerateReportAsync\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"n\"\u003ecustomerId\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eCancellationToken\u003c/span\u003e \u003cspan class=\"n\"\u003ect\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003edefault\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003eorders\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003e_orderRepo\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eGetOrdersAsync\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ecustomerId\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003ect\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003einvoices\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003e_invoiceRepo\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eGetInvoicesAsync\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ecustomerId\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003ect\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003epdf\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003e_pdfService\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eRenderAsync\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eorders\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003einvoices\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003ect\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"k\"\u003enew\u003c/span\u003e \u003cspan class=\"n\"\u003eReport\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003epdf\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eOne parameter. Thirty seconds. That\u0026rsquo;s the decision that was \u0026ldquo;not needed yet.\u0026rdquo;\u003c/p\u003e\n\u003cp\u003eThis is not a coincidence. It is compounding interest on technical debt, and the interest rate is not linear. The further the decision recedes into the past, the more the code has grown around it, the harder it is to reach, and the more things break when you try.\u003c/p\u003e\n\u003cp\u003eCareful building is cheap. Careless building is cheap too — until it isn\u0026rsquo;t. And it always stops being cheap at the worst possible moment.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"what-carefully-does-not-mean\"\u003e\u003ca href=\"/posts/code-as-legacy/#what-carefully-does-not-mean\" title=\"What \u0026ldquo;Carefully\u0026rdquo; Does Not Mean\"\u003eWhat \u0026ldquo;Carefully\u0026rdquo; Does Not Mean\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eI\u0026rsquo;ve made a mistake I see others repeat: confusing \u0026ldquo;carefully\u0026rdquo; with \u0026ldquo;perfectly.\u0026rdquo;\u003c/p\u003e\n\u003cp\u003ePerfectly is a trap. It produces over-engineered systems that look impeccable in architecture diagrams and are misery to extend. I have taken over projects from consultants who preached Clean Code and delivered something that could not change without collapsing. Everything was carefully named, carefully layered, carefully documented — and completely rigid.\u003c/p\u003e\n\u003cp\u003eThat\u0026rsquo;s not careful. That\u0026rsquo;s fearful.\u003c/p\u003e\n\u003cp\u003eCareful means four things — none of them perfectionism.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eUnderstanding the operating costs of what you write.\u003c/strong\u003e A \u003ccode\u003eDictionary\u003c/code\u003e is not thread-safe. An \u003ccode\u003easync void\u003c/code\u003e swallows exceptions silently. A \u003ccode\u003eGuid.NewGuid()\u003c/code\u003e primary key fragments your index with every insert. Not obscure knowledge — basic operating costs that change the failure mode of code that otherwise compiles and ships fine. \u003ccode\u003easync void\u003c/code\u003e is the instructive one: exceptions escape unobserved, hit the thread pool, and crash the process with no stack trace pointing back to the source:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// async void: exception becomes unobservable noise\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003eprivate\u003c/span\u003e \u003cspan class=\"kd\"\u003easync\u003c/span\u003e \u003cspan class=\"k\"\u003evoid\u003c/span\u003e \u003cspan class=\"n\"\u003eOnMessageReceived\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003eobject\u003c/span\u003e \u003cspan class=\"n\"\u003esender\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eMessageEventArgs\u003c/span\u003e \u003cspan class=\"n\"\u003ee\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003eProcessMessageAsync\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ee\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eMessage\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// async Task: caller can catch, log, and handle\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003eprivate\u003c/span\u003e \u003cspan class=\"kd\"\u003easync\u003c/span\u003e \u003cspan class=\"n\"\u003eTask\u003c/span\u003e \u003cspan class=\"n\"\u003eOnMessageReceivedAsync\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003eobject\u003c/span\u003e \u003cspan class=\"n\"\u003esender\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eMessageEventArgs\u003c/span\u003e \u003cspan class=\"n\"\u003ee\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003eProcessMessageAsync\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ee\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eMessage\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe \u003ccode\u003eGuid\u003c/code\u003e case is slower-burning. Both versions below ship on day one. The difference shows up in production monitoring three months later, when you notice your index is 60% fragmented and inserts are taking four times longer than they should:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"n\"\u003eGuid\u003c/span\u003e \u003cspan class=\"n\"\u003eId\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"k\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003eset\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eGuid\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eNewGuid\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e        \u003cspan class=\"c1\"\u003e// random, causes page splits\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"n\"\u003eGuid\u003c/span\u003e \u003cspan class=\"n\"\u003eId\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"k\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003eset\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eGuid\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eCreateVersion7\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e \u003cspan class=\"c1\"\u003e// monotonically increasing, .NET 9+ (see: https://learn.microsoft.com/en-us/dotnet/api/system.guid.createversion7)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\n\n\n\u003ch3 id=\"writing-for-the-next-reader\"\u003e\u003ca href=\"/posts/code-as-legacy/#writing-for-the-next-reader\" title=\"Writing For The Next Reader\"\u003eWriting For The Next Reader\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003e\u003cstrong\u003eOptimizing for the reader, not the writer.\u003c/strong\u003e The next person to read this code is often you, six months from now, with no memory of what you were thinking. Deliberate code — code that makes its assumptions visible — is not slower to write. It\u0026rsquo;s more expensive to start and cheaper to maintain forever after.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eKnowing when good enough actually is good enough.\u003c/strong\u003e Careful is not exhaustive. Configuration loaded once at startup does not need nanosecond optimization. A nightly batch job does not need payment-processor reliability. Misapplied care creates its own form of debt — rigidity dressed up as quality.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"making-assumptions-visible-to-the-compiler\"\u003e\u003ca href=\"/posts/code-as-legacy/#making-assumptions-visible-to-the-compiler\" title=\"Making Assumptions Visible To The Compiler\"\u003eMaking Assumptions Visible To The Compiler\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003e\u003cstrong\u003eMaking the implicit explicit.\u003c/strong\u003e The most dangerous code in any system isn\u0026rsquo;t complex code — it\u0026rsquo;s code where critical assumptions live in someone\u0026rsquo;s head instead of in the type system or the tests. The two implementations below are functionally equivalent on a happy path. Only one survives a new developer joining the team:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// assumptions in the developer\u0026#39;s head\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eInvoiceService\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003eprivate\u003c/span\u003e \u003cspan class=\"n\"\u003eDictionary\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"kt\"\u003edecimal\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003e_taxRates\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kt\"\u003estring\u003c/span\u003e \u003cspan class=\"n\"\u003eFormatAmount\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003edecimal\u003c/span\u003e \u003cspan class=\"n\"\u003eamount\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"n\"\u003eregionId\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"s\"\u003e$\u0026#34;{amount * _taxRates[regionId]:C}\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// assumptions in the compiler\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kd\"\u003esealed\u003c/span\u003e \u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eInvoiceService\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003eprivate\u003c/span\u003e \u003cspan class=\"k\"\u003ereadonly\u003c/span\u003e \u003cspan class=\"n\"\u003eIReadOnlyDictionary\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"kt\"\u003edecimal\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003e_taxRates\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"n\"\u003eInvoiceService\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eIReadOnlyDictionary\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"kt\"\u003edecimal\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003etaxRates\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003e_taxRates\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003etaxRates\u003c/span\u003e \u003cspan class=\"p\"\u003e??\u003c/span\u003e \u003cspan class=\"k\"\u003ethrow\u003c/span\u003e \u003cspan class=\"k\"\u003enew\u003c/span\u003e \u003cspan class=\"n\"\u003eArgumentNullException\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003enameof\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003etaxRates\u003c/span\u003e\u003cspan class=\"p\"\u003e));\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kt\"\u003estring\u003c/span\u003e \u003cspan class=\"n\"\u003eFormatAmount\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003edecimal\u003c/span\u003e \u003cspan class=\"n\"\u003eamount\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"n\"\u003eregionId\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(!\u003c/span\u003e\u003cspan class=\"n\"\u003e_taxRates\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eTryGetValue\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eregionId\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"k\"\u003eout\u003c/span\u003e \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003erate\u003c/span\u003e\u003cspan class=\"p\"\u003e))\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"k\"\u003ethrow\u003c/span\u003e \u003cspan class=\"k\"\u003enew\u003c/span\u003e \u003cspan class=\"n\"\u003eKeyNotFoundException\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e$\u0026#34;No tax rate configured for region {regionId}.\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"s\"\u003e$\u0026#34;{amount * rate:C}\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe second version is longer because it encodes what was previously undocumented: tax rates are required, null is not acceptable, and an unknown region is a programming error — not a silent zero that produces a wrong invoice.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"code-outlives-context\"\u003e\u003ca href=\"/posts/code-as-legacy/#code-outlives-context\" title=\"Code Outlives Context\"\u003eCode Outlives Context\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eHere is the thing that took me the longest to internalize:\u003c/p\u003e\n\u003cp\u003eThe context in which you wrote the code will not survive. The business requirement that made the trade-off obvious will be forgotten. The pressure that justified the shortcut will be invisible. The Slack thread explaining why the timeout is hardcoded to 30 seconds will scroll into history. The team that understood the design will disperse.\u003c/p\u003e\n\u003cp\u003eWhat remains is the code.\u003c/p\u003e\n\u003cp\u003eAnd someone will have to work with it without your context, your justifications, or your intentions. They will read what you wrote and form conclusions. They will extend it, debug it, and curse it — or understand it and be grateful.\u003c/p\u003e\n\u003cp\u003eThat is the legacy.\u003c/p\u003e\n\u003cp\u003eI have been both recipients. I\u0026rsquo;ve inherited systems where everything was explained by what the code did — where reading a class told you not just how it worked but why, what it was protecting against, and where the landmines were. I\u0026rsquo;ve also inherited systems that required six months of archaeology before I trusted any change I made.\u003c/p\u003e\n\u003cp\u003eThe engineers who wrote both kinds were equally intelligent. The difference was care.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"the-relationship-between-care-and-speed\"\u003e\u003ca href=\"/posts/code-as-legacy/#the-relationship-between-care-and-speed\" title=\"The Relationship Between Care and Speed\"\u003eThe Relationship Between Care and Speed\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eTeams that haven\u0026rsquo;t experienced this tension believe that careful code is slower to produce than careless code. They\u0026rsquo;re right in the short term. A quick hack ships faster than a considered design — once.\u003c/p\u003e\n\u003cp\u003eWhat they miss is the asymmetry in the other direction.\u003c/p\u003e\n\u003cp\u003eCareless code is expensive to extend, expensive to debug, expensive to test, expensive to hand off, and expensive to explain. Every future interaction with that code costs more than it needed to. The total cost of ownership grows with the number of future interactions, and production code has a lot of future interactions.\u003c/p\u003e\n\u003cp\u003eCareful code costs more upfront and less every time after.\u003c/p\u003e\n\u003cp\u003eThis is not an abstract economic argument. I can point to specific decisions in systems I maintain where five minutes of thinking at creation time would have saved months of debugging over the lifetime of the feature. I can also point to the opposite: careful designs that held up under four years of changing requirements without needing to be rewritten.\u003c/p\u003e\n\u003cp\u003eThe careful code was not slower to develop. It was \u003cstrong\u003eslower to start and faster to finish\u003c/strong\u003e — across the entire lifecycle of the feature.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"what-i-actually-do-differently\"\u003e\u003ca href=\"/posts/code-as-legacy/#what-i-actually-do-differently\" title=\"What I Actually Do Differently\"\u003eWhat I Actually Do Differently\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eAfter nearly twenty years, \u0026ldquo;build carefully\u0026rdquo; has specific practices attached to it. These are not aspirational principles. They are the concrete things I do, or insist my teams do, because I\u0026rsquo;ve felt the cost of not doing them.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eEnable \u003ca href=\"https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/overview\" target=\"_blank\" rel=\"noopener external noreferrer\"\u003eRoslyn analyzers\u003c/a\u003e from day zero.\u003c/strong\u003e Not as a code review substitute — as a safety net that operates at compilation time. I configure them in \u003ccode\u003e.editorconfig\u003c/code\u003e at project creation, severity-as-error for the things that matter, and when they produce noise I fix the noise rather than silence the rule. The six rules I never start a project without:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ini\" data-lang=\"ini\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003e[*.cs]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"na\"\u003edotnet_diagnostic.CA2007.severity\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s\"\u003eerror   # ConfigureAwait missing\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"na\"\u003edotnet_diagnostic.CA1031.severity\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s\"\u003ewarning # catch Exception (too broad)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"na\"\u003edotnet_diagnostic.CA1051.severity\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s\"\u003eerror   # public instance fields\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"na\"\u003edotnet_diagnostic.CA1825.severity\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s\"\u003eerror   # unnecessary array allocation\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"na\"\u003edotnet_diagnostic.CS8600.severity\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s\"\u003eerror   # nullable dereference\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"na\"\u003edotnet_diagnostic.CS8602.severity\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s\"\u003eerror   # possible null reference\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThese rules catch bugs that appear in incident reports, not in code review — which is exactly the point.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"forcing-yourself-to-articulate-intent\"\u003e\u003ca href=\"/posts/code-as-legacy/#forcing-yourself-to-articulate-intent\" title=\"Forcing Yourself To Articulate Intent\"\u003eForcing Yourself To Articulate Intent\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003e\u003cstrong\u003eWrite the summary before the method.\u003c/strong\u003e Not a docstring — a sentence in my head: \u003cem\u003e\u0026ldquo;This method does X and assumes Y.\u0026rdquo;\u003c/em\u003e If I can\u0026rsquo;t complete that sentence clearly, I don\u0026rsquo;t understand my own code well enough to ship it. This sounds trivial. It isn\u0026rsquo;t. It catches underspecified designs before they become permanent.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eTreat \u003ccode\u003eTODO\u003c/code\u003e comments as deferred decisions, not reminders.\u003c/strong\u003e Every \u003ccode\u003e// TODO: fix this properly\u003c/code\u003e is a piece of context that will expire. Either I fix it now, create a tracked issue with enough context that a stranger could complete it, or I accept that it will never be fixed and stop pretending otherwise. The lie that \u0026ldquo;we\u0026rsquo;ll come back to this\u0026rdquo; is one of the most expensive fictions in software.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"the-pre-commit-diff-review-habit\"\u003e\u003ca href=\"/posts/code-as-legacy/#the-pre-commit-diff-review-habit\" title=\"The Pre-commit Diff Review Habit\"\u003eThe Pre-commit Diff Review Habit\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003e\u003cstrong\u003eRead the diff before every commit.\u003c/strong\u003e Not to catch typos — to notice surprises. If I see code I don\u0026rsquo;t remember writing or can\u0026rsquo;t explain, that\u0026rsquo;s the signal. Familiar code that suddenly looks strange is often code that shouldn\u0026rsquo;t be committed yet.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eName things for what they are, not what they do.\u003c/strong\u003e \u003ccode\u003eCustomerRepository\u003c/code\u003e tells you the mechanism. \u003ccode\u003eCustomerAccess\u003c/code\u003e is vague. \u003ccode\u003eActiveCustomersByRegionQuery\u003c/code\u003e tells you what you\u0026rsquo;re getting and why. The noun matters. The qualifier matters more.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"the-longer-arc\"\u003e\u003ca href=\"/posts/code-as-legacy/#the-longer-arc\" title=\"The Longer Arc\"\u003eThe Longer Arc\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eI carry that motto in my bio because it is the most honest thing I can say about why I write the way I write and build the way I build.\u003c/p\u003e\n\u003cp\u003eIt isn\u0026rsquo;t about perfectionism. It isn\u0026rsquo;t about impressing code reviewers or following the fashionable methodology of the moment. It\u0026rsquo;s about the relationship between present decisions and future consequences — and taking that relationship seriously enough to slow down slightly, every single time, and ask: \u003cem\u003e\u0026ldquo;Is this how I would want to find this?\u0026rdquo;\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003eMost of the time, the answer is no. That\u0026rsquo;s fine. That\u0026rsquo;s the question working.\u003c/p\u003e\n\u003cp\u003eThe code you write today will be maintained by someone who doesn\u0026rsquo;t know what you were thinking. It might be a colleague. It might be a future version of yourself. It might be someone you\u0026rsquo;ll never meet, building on a library you published and forgot about.\u003c/p\u003e\n\u003cp\u003eDo them the courtesy of building it carefully.\u003c/p\u003e\n","date_modified":"2026-05-26T10:22:03+02:00","date_published":"2026-05-19T17:00:00+02:00","id":"https://daily-devops.net/posts/code-as-legacy/","language":"en","summary":"Code is not just something you write—it is something you leave behind. After nearly two decades in production, here is what treating code as legacy means.\n","tags":["softwareengineering","codequality","bestpractices","technicaldebt","architecture","dotnet","csharp"],"title":"The Code You Write Today Is Someone's Problem Tomorrow\n","url":"https://daily-devops.net/posts/code-as-legacy/"},{"authors":[{"name":"Martin Stühmer","url":"https://daily-devops.net/authors/martin/"}],"content_html":"\u003cp\u003eGitHub Copilot code review is available in pull requests. Claude can review a diff. Cursor highlights issues as you type. Every major AI coding assistant now offers some form of review, and teams are using these tools to supplement (or in some cases replace) asynchronous human review on pull requests.\u003c/p\u003e\n\u003cp\u003eThis is not necessarily wrong. AI code review is genuinely useful. But there is a pattern to what it misses, and understanding that pattern matters more than debating whether to use these tools at all.\u003c/p\u003e\n\u003cp\u003eIn my experience, AI code reviewers behave like sycophants. They are good at finding small problems with how you built something. They are almost incapable of questioning whether you should have built it at all.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"what-ai-code-review-is-good-at\"\u003e\u003ca href=\"/posts/ai-code-review-is-a-sycophant/#what-ai-code-review-is-good-at\" title=\"What AI Code Review Is Good At\"\u003eWhat AI Code Review Is Good At\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eTo be clear: these tools are useful. Worth adding to your PR workflow.\u003c/p\u003e\n\u003cp\u003eAI reviews reliably catch:\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eObvious bugs in isolation.\u003c/strong\u003e Null dereferences, off-by-one errors, incorrect operator precedence, missing \u003ccode\u003eawait\u003c/code\u003e, unchecked return values from methods that can fail. These are the bugs human reviewers also catch, and they slip through when reviewers are tired, rushed, or staring at a 500-line diff.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eCommon anti-patterns.\u003c/strong\u003e \u003ccode\u003easync void\u003c/code\u003e, catching \u003ccode\u003eException\u003c/code\u003e without rethrowing, \u003ccode\u003eDateTime.Now\u003c/code\u003e instead of \u003ccode\u003eDateTime.UtcNow\u003c/code\u003e, string concatenation in loops, \u003ccode\u003eConfigureAwait(false)\u003c/code\u003e missing in library code. Pattern matching against known bad patterns is exactly what Large Language Models (LLMs) do well.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eTrivial security issues.\u003c/strong\u003e SQL injection via string concatenation, hardcoded credentials, insecure random number generation. These appear in training data thousands of times.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eStyle consistency.\u003c/strong\u003e Naming inconsistencies, missing XML documentation, inconsistent error handling patterns relative to the rest of the file.\u003c/p\u003e\n\u003cp\u003eThese categories represent real value. A review pass that catches these before human review means human reviewers can spend their time on harder problems.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"what-ai-code-review-systematically-misses\"\u003e\u003ca href=\"/posts/ai-code-review-is-a-sycophant/#what-ai-code-review-systematically-misses\" title=\"What AI Code Review Systematically Misses\"\u003eWhat AI Code Review Systematically Misses\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eThis is where the sycophancy shows up.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eWrong abstraction.\u003c/strong\u003e AI reviewers evaluate the code you wrote against its own internal logic. They rarely notice that the abstraction itself is wrong: that the \u003ccode\u003eOrderProcessor\u003c/code\u003e class is doing three different things and probably should not exist as a single class, that the interface design couples callers to implementation details, that the naming reveals a confused mental model of the domain. Recognizing a wrong abstraction requires understanding the system it lives in and the cost of fixing it later. AI reviewers do not have that context.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e\u0026ldquo;This should be deleted.\u0026rdquo;\u003c/strong\u003e The correct review comment for a surprising fraction of pull requests is something like: \u0026ldquo;This feature was not the right call, let\u0026rsquo;s talk before merging.\u0026rdquo; AI reviewers will not write that comment. They review code on its own terms. A well-implemented feature that solves the wrong problem gets a positive AI review, and that feedback loop, repeated over time, shapes how a team thinks about what quality means.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eSystemic patterns across the codebase.\u003c/strong\u003e AI reviewers see the diff. They do not know that the same abstraction appeared in three other places and was wrong each time. They do not know that this exact approach was tried and reverted eight months ago, and that the revert commit explains why. Reviewers with codebase history catch this. AI reviewers cannot.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eBusiness logic correctness.\u003c/strong\u003e Is this the right formula for calculating the surcharge? Does this authorization check correctly represent the access control model? Is this state machine transition valid given how the domain actually works? AI reviewers can tell you the code is internally consistent. They cannot tell you it is correct relative to what the software is supposed to do. This is not a minor gap. Business logic bugs are often the costliest bugs, and they are invisible to a reviewer that does not understand the business.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003ePerformance under real load.\u003c/strong\u003e AI reviewers flag obvious O(n²) algorithms and missing database indexes in toy examples. They rarely have visibility into the data distribution, the access patterns, or the production load profile that determines whether the code will hold up at scale. The performance review that matters happens in load testing and production, not in the diff view.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"the-sycophancy-problem\"\u003e\u003ca href=\"/posts/ai-code-review-is-a-sycophant/#the-sycophancy-problem\" title=\"The Sycophancy Problem\"\u003eThe Sycophancy Problem\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eThe specific failure mode of AI code review is not that it misses things. Every review process misses things. The problem is the pattern of what it misses.\u003c/p\u003e\n\u003cp\u003eAI reviewers tend to approve the overall approach and find issues in the details. When a team leans heavily on AI review, there is a subtle risk: reviewers get better and better at fixing the details an AI flags, while the bigger structural questions get less attention over time. I have seen this happen, and it is not anyone\u0026rsquo;s fault. It is a natural response to the feedback signal you are getting.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"why-the-approval-bias-is-structural\"\u003e\u003ca href=\"/posts/ai-code-review-is-a-sycophant/#why-the-approval-bias-is-structural\" title=\"Why The Approval Bias Is Structural\"\u003eWhy The Approval Bias Is Structural\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eThe approval bias is structural. AI reviewers are trained on review data where most code in a diff is acceptable. The kind of feedback that says \u0026ldquo;the entire approach here is wrong, close this PR and start over\u0026rdquo; is rare in training data and produces outcomes that make the tool seem less useful. So the model optimizes away from it.\u003c/p\u003e\n\u003cp\u003eThe result: AI reviewers are systematically biased toward approving what you built and suggesting small improvements. They are not calibrated to recognize when the correct response is rejection.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"the-confidence-effect-on-developers\"\u003e\u003ca href=\"/posts/ai-code-review-is-a-sycophant/#the-confidence-effect-on-developers\" title=\"The Confidence Effect On Developers\"\u003eThe Confidence Effect On Developers\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eThere is also a confidence effect worth naming. A developer who ships a PR with zero AI findings tends to feel more confident that the code is solid. That confidence is not entirely wrong (the mechanical issues are likely clean), but it can crowd out the instinct to ask for a second human opinion. Over time, \u0026ldquo;the AI found nothing\u0026rdquo; starts to function as a substitute for \u0026ldquo;this is good code\u0026rdquo;, and that is a different claim entirely.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"what-ai-review-should-change-about-human-review\"\u003e\u003ca href=\"/posts/ai-code-review-is-a-sycophant/#what-ai-review-should-change-about-human-review\" title=\"What AI Review Should Change About Human Review\"\u003eWhat AI Review Should Change About Human Review\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eIf AI review is in your pipeline, it should shift what human reviewers focus on, not replace them.\u003c/p\u003e\n\u003cp\u003eAI reviewers handle the mechanical layer well: obvious bugs, pattern violations, style issues. That creates an opportunity for human reviewers to focus on what AI cannot do:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eIs this the right design?\u003c/li\u003e\n\u003cli\u003eDoes this code belong here at all?\u003c/li\u003e\n\u003cli\u003eDoes the naming suggest the author has a clear mental model of the domain?\u003c/li\u003e\n\u003cli\u003eIs this consistent with decisions made elsewhere in the system?\u003c/li\u003e\n\u003cli\u003eWhat will maintaining this cost in six months?\u003c/li\u003e\n\u003c/ul\u003e\n\n\n\n\n\u003ch3 id=\"where-human-review-time-belongs\"\u003e\u003ca href=\"/posts/ai-code-review-is-a-sycophant/#where-human-review-time-belongs\" title=\"Where Human Review Time Belongs\"\u003eWhere Human Review Time Belongs\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eHuman review time is finite. If a human reviewer spends twenty minutes on a PR that an AI already reviewed and only surfaces style issues, something has gone wrong with how review time is being used. The value of human review is judgment, context, and the willingness to say \u0026ldquo;not yet.\u0026rdquo;\u003c/p\u003e\n\u003cp\u003eA team that uses AI review to reduce the need for human judgment does not end up with less review. It ends up with coverage that feels high but catches less of what actually matters.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"the-diff-problem\"\u003e\u003ca href=\"/posts/ai-code-review-is-a-sycophant/#the-diff-problem\" title=\"The Diff Problem\"\u003eThe Diff Problem\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eBoth AI and human review share a structural limitation: they evaluate changes, not outcomes.\u003c/p\u003e\n\u003cp\u003eA large refactor that genuinely improves a design looks messy as a diff: deletions everywhere, moved code, renamed concepts. A small change that introduces a subtle bug can look perfectly clean. Both human and AI reviewers are influenced by the shape of the change, not just its effect on the codebase.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"why-ai-cannot-step-outside-the-diff\"\u003e\u003ca href=\"/posts/ai-code-review-is-a-sycophant/#why-ai-cannot-step-outside-the-diff\" title=\"Why AI Cannot Step Outside The Diff\"\u003eWhy AI Cannot Step Outside The Diff\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eAI reviewers are more constrained here because they have no option to go beyond the diff. A human reviewer can pull the branch, run it, read the surrounding code, check git history. AI reviewers are limited to what is presented to them.\u003c/p\u003e\n\u003cp\u003eThis means AI review is structurally better suited to focused, contained changes, and less suited to catching problems that only become visible when you look at the broader context.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"a-concrete-library-migration-example\"\u003e\u003ca href=\"/posts/ai-code-review-is-a-sycophant/#a-concrete-library-migration-example\" title=\"A Concrete Library Migration Example\"\u003eA Concrete Library Migration Example\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eA concrete example: a PR that migrates a service to use a new internal library might look straightforward in the diff. The imports change, a few method calls are updated, tests pass. An AI reviewer sees nothing alarming. But a human who knows that the new library has different error propagation semantics, or that the migration breaks an assumption made elsewhere in the codebase, can catch that. The diff does not surface it. Context does.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"using-ai-review-without-becoming-dependent-on-it\"\u003e\u003ca href=\"/posts/ai-code-review-is-a-sycophant/#using-ai-review-without-becoming-dependent-on-it\" title=\"Using AI Review Without Becoming Dependent on It\"\u003eUsing AI Review Without Becoming Dependent on It\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eA few practices that have worked well in my experience:\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eUse AI review as a pre-filter, not a gatekeeper.\u003c/strong\u003e Let it catch mechanical issues before human review. Humans then review for judgment, not syntax. An AI approval should not substitute for human review on anything that carries real risk.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eTreat AI approval as a weak signal.\u003c/strong\u003e An AI saying \u0026ldquo;looks good\u0026rdquo; means it did not find a pattern match for common issues. That is useful information, but it is not an endorsement of the design.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eRead what the AI flagged, and what it did not.\u003c/strong\u003e If it found nothing interesting, that is not evidence the code is good. It may mean the problems are exactly the kind the AI cannot see.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eKeep humans in the design conversation.\u003c/strong\u003e Architecture decisions, new abstractions, changes to domain models: these all need human review from someone with context. No AI reviewer carries your system\u0026rsquo;s history, your domain knowledge, or the judgment to tell you a design direction is off before you build it out.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eWatch for approval drift.\u003c/strong\u003e If PRs consistently get AI approval and human reviewers gradually stop questioning design decisions, that is a signal worth paying attention to. The human review may have been quietly degraded, not supplemented.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"the-honest-summary\"\u003e\u003ca href=\"/posts/ai-code-review-is-a-sycophant/#the-honest-summary\" title=\"The Honest Summary\"\u003eThe Honest Summary\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eAI code review tools are useful. Add them to your pipeline. Let them handle the mechanical layer.\u003c/p\u003e\n\u003cp\u003eBut they are not reviewers in the sense that actually matters. They do not have judgment. They do not know your system. They cannot tell you that you built the wrong thing. They are pattern matchers with a structural bias toward approving what you wrote.\u003c/p\u003e\n\u003cp\u003eThe risk is not that these tools make developers worse (most developers using AI review are thoughtful professionals who also get human review). The risk is subtler: over time, optimizing for what AI review catches can quietly shift attention away from the questions it cannot ask. Staying aware of that dynamic is enough to avoid it.\u003c/p\u003e\n\u003cp\u003eAI review is a useful tool. Keep it in that category.\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eAn AI reviewer that says \u0026ldquo;looks good\u0026rdquo; is not telling you the code is good. It is telling you it did not find a match.\u003c/p\u003e\n\u003c/blockquote\u003e\n","date_modified":"2026-05-25T22:27:03+02:00","date_published":"2026-05-12T17:00:00+02:00","id":"https://daily-devops.net/posts/ai-code-review-is-a-sycophant/","language":"en","summary":"Copilot and Claude find real bugs, but miss wrong abstractions and bad designs. Understanding that gap matters more than debating the tools.","tags":["ai","softwareengineering","codequality","bestpractices","devops"],"title":"AI Code Review Is a Sycophant: Why It Always Approves","url":"https://daily-devops.net/posts/ai-code-review-is-a-sycophant/"},{"authors":[{"name":"Martin Stühmer","url":"https://daily-devops.net/authors/martin/"}],"content_html":"\u003cp\u003eEvery quarter, your compliance team gathers around conference tables reviewing spreadsheets that claim your organization processes personal data lawfully. Meanwhile, your production databases collect new PII fields nobody documented, consent records expire without notification, and deletion requests sit in ticketing systems with no automated verification they were honored. You\u0026rsquo;re not non-compliant because you don\u0026rsquo;t care. You\u0026rsquo;re non-compliant because manual privacy audits cannot keep pace with continuous deployment.\u003c/p\u003e\n\u003cp\u003eHere\u0026rsquo;s the uncomfortable truth: privacy compliance is a continuous state, not a quarterly checkpoint. Every code deployment potentially introduces new PII processing. Every schema migration might create compliance gaps. Every API endpoint modification could violate documented privacy purposes. Manual audits examine yesterday\u0026rsquo;s system while today\u0026rsquo;s changes accumulate unreviewed. By the time the next audit rolls around, you\u0026rsquo;ve already shipped three months of undocumented data collection.\u003c/p\u003e\n\u003cp\u003eThis article demonstrates building .NET CLI tools that automate privacy audit workflows. We\u0026rsquo;ll examine why manual approaches fail, then construct automated solutions using reflection for PII discovery, Entity Framework metadata for database schema analysis, and GitHub Actions for continuous compliance monitoring.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"the-fatal-manual-privacy-audit-pattern\"\u003e\u003ca href=\"/posts/privacy-audit-automation-dotnet-cli/#the-fatal-manual-privacy-audit-pattern\" title=\"The Fatal Manual Privacy Audit Pattern\"\u003eThe Fatal Manual Privacy Audit Pattern\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eLet\u0026rsquo;s examine what \u0026ldquo;compliance\u0026rdquo; looks like in organizations that rely on quarterly manual reviews:\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"schema-drift-the-silent-compliance-killer\"\u003e\u003ca href=\"/posts/privacy-audit-automation-dotnet-cli/#schema-drift-the-silent-compliance-killer\" title=\"Schema Drift: The Silent Compliance Killer\"\u003eSchema Drift: The Silent Compliance Killer\u003c/a\u003e\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eUserProfile\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"n\"\u003eGuid\u003c/span\u003e \u003cspan class=\"n\"\u003eId\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"k\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003eset\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kt\"\u003estring\u003c/span\u003e \u003cspan class=\"n\"\u003eEmail\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"k\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003eset\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kt\"\u003estring\u003c/span\u003e \u003cspan class=\"n\"\u003eFullName\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"k\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003eset\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e// Added in sprint 47 - privacy docs? What privacy docs?\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kt\"\u003estring\u003c/span\u003e \u003cspan class=\"n\"\u003eSocialSecurityNumber\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"k\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003eset\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kt\"\u003estring\u003c/span\u003e \u003cspan class=\"n\"\u003eBiometricHash\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"k\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003eset\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eSensitive PII hits production while compliance reviews migration files from three months ago. By the next audit, you\u0026rsquo;ve collected biometric data for an entire quarter without documented legal basis. The spreadsheet says you\u0026rsquo;re compliant. Reality disagrees.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"the-consent-expiration-time-bomb\"\u003e\u003ca href=\"/posts/privacy-audit-automation-dotnet-cli/#the-consent-expiration-time-bomb\" title=\"The Consent Expiration Time Bomb\"\u003eThe Consent Expiration Time Bomb\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eYour consent records have expiration dates. Your tracking spreadsheet has a quarterly reminder to \u0026ldquo;check expired consents.\u0026rdquo; Meanwhile, 14,000 expired consent records remain marked active, and marketing emails continue flowing to users who withdrew consent months ago.\u003c/p\u003e\n\u003cp\u003eGDPR requires making consent withdrawal as easy as giving it. If your expiration tracking relies on someone remembering to run a database query every few months, you\u0026rsquo;re systematically violating this requirement. A daily automated check would catch this in hours, not quarters.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"deletion-requests-hope-is-not-a-strategy\"\u003e\u003ca href=\"/posts/privacy-audit-automation-dotnet-cli/#deletion-requests-hope-is-not-a-strategy\" title=\"Deletion Requests: Hope Is Not a Strategy\"\u003eDeletion Requests: Hope Is Not a Strategy\u003c/a\u003e\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kd\"\u003easync\u003c/span\u003e \u003cspan class=\"n\"\u003eTask\u003c/span\u003e \u003cspan class=\"n\"\u003eDeleteUserAccount\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eGuid\u003c/span\u003e \u003cspan class=\"n\"\u003euserId\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e// Deletes from Users table\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003e_dbContext\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eUsers\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eWhere\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eu\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003eu\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eId\u003c/span\u003e \u003cspan class=\"p\"\u003e==\u003c/span\u003e \u003cspan class=\"n\"\u003euserId\u003c/span\u003e\u003cspan class=\"p\"\u003e).\u003c/span\u003e\u003cspan class=\"n\"\u003eExecuteDeleteAsync\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e// But PII remains in: OrderHistory, AuditLogs, EmailCampaigns, \u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e// BackupSnapshots, and that analytics database nobody remembers exists\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eUser requests deletion. Support marks ticket resolved. Compliance assumes it happened. Meanwhile, email addresses live on in five different tables across three systems nobody checked. Without automated scanning, deletion requests become best-effort guesses.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"documentation-drift-accurate-records-of-a-system-that-no-longer-exists\"\u003e\u003ca href=\"/posts/privacy-audit-automation-dotnet-cli/#documentation-drift-accurate-records-of-a-system-that-no-longer-exists\" title=\"Documentation Drift: Accurate Records of a System That No Longer Exists\"\u003eDocumentation Drift: Accurate Records of a System That No Longer Exists\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eYour processing records say you collect \u0026ldquo;email addresses, names, and purchase history\u0026rdquo; with 36-month retention. Your actual system now processes health information, geolocation data, and biometric logs (none documented) with 60-month retention. The documentation was accurate. Eighteen months ago. Now it\u0026rsquo;s compliant fiction, and auditors are reviewing a system that no longer exists.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"building-the-fix-net-cli-tools-for-privacy-automation\"\u003e\u003ca href=\"/posts/privacy-audit-automation-dotnet-cli/#building-the-fix-net-cli-tools-for-privacy-automation\" title=\"Building the Fix: .NET CLI Tools for Privacy Automation\"\u003eBuilding the Fix: .NET CLI Tools for Privacy Automation\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eEnough problems. Let\u0026rsquo;s build solutions. The goal: make privacy compliance a build-time fact rather than a quarterly hope. These CLI tools integrate into your CI/CD pipeline, failing builds when privacy violations exist.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"pii-discovery-find-everything-you-forgot-to-document\"\u003e\u003ca href=\"/posts/privacy-audit-automation-dotnet-cli/#pii-discovery-find-everything-you-forgot-to-document\" title=\"PII Discovery: Find Everything You Forgot to Document\"\u003ePII Discovery: Find Everything You Forgot to Document\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eFirst, a CLI tool that scans your codebase and database schema for PII attributes:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eDiscoverPiiCommand\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kd\"\u003easync\u003c/span\u003e \u003cspan class=\"n\"\u003eTask\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003ePrivacyAuditReport\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003eExecuteAsync\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003estring\u003c/span\u003e \u003cspan class=\"n\"\u003eassemblyPath\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"kt\"\u003estring\u003c/span\u003e \u003cspan class=\"n\"\u003econnectionString\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003ereport\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003enew\u003c/span\u003e \u003cspan class=\"n\"\u003ePrivacyAuditReport\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003eassembly\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eAssembly\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eLoadFrom\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eassemblyPath\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"c1\"\u003e// Find all properties marked with [PersonalData]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003epiiProperties\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eassembly\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eGetTypes\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eSelectMany\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003etype\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003etype\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eGetProperties\u003c/span\u003e\u003cspan class=\"p\"\u003e())\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eWhere\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eprop\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003eprop\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eGetCustomAttribute\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003ePersonalDataAttribute\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;()\u003c/span\u003e \u003cspan class=\"p\"\u003e!=\u003c/span\u003e \u003cspan class=\"kc\"\u003enull\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eSelect\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eprop\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"k\"\u003enew\u003c/span\u003e \u003cspan class=\"n\"\u003ePiiProperty\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"n\"\u003eTypeName\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eprop\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eDeclaringType\u003c/span\u003e\u003cspan class=\"p\"\u003e?.\u003c/span\u003e\u003cspan class=\"n\"\u003eFullName\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"n\"\u003ePropertyName\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eprop\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eName\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"n\"\u003eLegalBasis\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eprop\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eGetCustomAttribute\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eLegalBasisAttribute\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;()?.\u003c/span\u003e\u003cspan class=\"n\"\u003eBasis\u003c/span\u003e \u003cspan class=\"p\"\u003e??\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;NOT_DOCUMENTED\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"p\"\u003e});\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003ereport\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eDiscoveredPiiProperties\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003epiiProperties\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eToList\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"c1\"\u003e// Scan EF metadata for database columns\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"k\"\u003eusing\u003c/span\u003e \u003cspan class=\"nn\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003edbContext\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003enew\u003c/span\u003e \u003cspan class=\"n\"\u003eApplicationDbContext\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003econnectionString\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003eforeach\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003eentityType\u003c/span\u003e \u003cspan class=\"k\"\u003ein\u003c/span\u003e \u003cspan class=\"n\"\u003edbContext\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eModel\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eGetEntityTypes\u003c/span\u003e\u003cspan class=\"p\"\u003e())\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003epiiColumns\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eentityType\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eGetProperties\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eWhere\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ep\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003ep\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eFindAnnotation\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;Privacy:IsPII\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)?.\u003c/span\u003e\u003cspan class=\"n\"\u003eValue\u003c/span\u003e \u003cspan class=\"k\"\u003eas\u003c/span\u003e \u003cspan class=\"kt\"\u003ebool?\u003c/span\u003e \u003cspan class=\"p\"\u003e==\u003c/span\u003e \u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"n\"\u003ereport\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eDatabasePiiColumns\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAddRange\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003epiiColumns\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eSelect\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ep\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"k\"\u003enew\u003c/span\u003e \u003cspan class=\"n\"\u003eDatabasePiiColumn\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"n\"\u003eTableName\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eentityType\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eGetTableName\u003c/span\u003e\u003cspan class=\"p\"\u003e(),\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"n\"\u003eColumnName\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003ep\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eGetColumnName\u003c/span\u003e\u003cspan class=\"p\"\u003e(),\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"n\"\u003eLegalBasis\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003ep\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eFindAnnotation\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;Privacy:LegalBasis\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)?.\u003c/span\u003e\u003cspan class=\"n\"\u003eValue\u003c/span\u003e \u003cspan class=\"k\"\u003eas\u003c/span\u003e \u003cspan class=\"kt\"\u003estring\u003c/span\u003e \u003cspan class=\"p\"\u003e??\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;UNDOCUMENTED\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"p\"\u003e}));\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"c1\"\u003e// Cross-reference against processing records - anything missing is a compliance gap\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003edocumented\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003eLoadProcessingRecordsAsync\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003ereport\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eUndocumentedPii\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003ereport\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eDatabasePiiColumns\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eWhere\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ecol\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"p\"\u003e!\u003c/span\u003e\u003cspan class=\"n\"\u003edocumented\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAny\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003erec\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003erec\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eCoversColumn\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ecol\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eTableName\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003ecol\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eColumnName\u003c/span\u003e\u003cspan class=\"p\"\u003e)))\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eToList\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003ereport\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe tool scans code via reflection and database schema via EF metadata, cross-referencing against your processing records. Anything not documented shows up as a compliance gap. Run it in CI/CD and you\u0026rsquo;ll know within minutes when someone adds undocumented PII.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"consent-monitoring-catch-expirations-before-regulators-do\"\u003e\u003ca href=\"/posts/privacy-audit-automation-dotnet-cli/#consent-monitoring-catch-expirations-before-regulators-do\" title=\"Consent Monitoring: Catch Expirations Before Regulators Do\"\u003eConsent Monitoring: Catch Expirations Before Regulators Do\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eA CLI tool that runs daily to catch consent violations:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eConsentAuditCommand\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kd\"\u003easync\u003c/span\u003e \u003cspan class=\"n\"\u003eTask\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eConsentAuditReport\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003eExecuteAsync\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003etoday\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eDateTime\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eUtcNow\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003ereport\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003enew\u003c/span\u003e \u003cspan class=\"n\"\u003eConsentAuditReport\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"n\"\u003eAuditDate\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003etoday\u003c/span\u003e \u003cspan class=\"p\"\u003e};\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"c1\"\u003e// Find expired consents still marked active\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003ereport\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eExpiredActiveConsents\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003e_dbContext\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eMarketingConsents\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eWhere\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ec\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003ec\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eIsActive\u003c/span\u003e \u003cspan class=\"p\"\u003e\u0026amp;\u0026amp;\u003c/span\u003e \u003cspan class=\"n\"\u003ec\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eExpirationDate\u003c/span\u003e \u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e \u003cspan class=\"n\"\u003etoday\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eSelect\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ec\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"k\"\u003enew\u003c/span\u003e \u003cspan class=\"n\"\u003eExpiredConsent\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"n\"\u003eUserId\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003ec\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eUserId\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"n\"\u003ePurpose\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003ec\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ePurpose\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"n\"\u003eDaysOverdue\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e\u003cspan class=\"p\"\u003e)(\u003c/span\u003e\u003cspan class=\"n\"\u003etoday\u003c/span\u003e \u003cspan class=\"p\"\u003e-\u003c/span\u003e \u003cspan class=\"n\"\u003ec\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eExpirationDate\u003c/span\u003e\u003cspan class=\"p\"\u003e).\u003c/span\u003e\u003cspan class=\"n\"\u003eTotalDays\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"p\"\u003e})\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eToListAsync\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"c1\"\u003e// Verify \u0026#34;completed\u0026#34; deletion requests actually deleted everything\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003edeletionRequests\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003e_dbContext\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eGdprDeletionRequests\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eWhere\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003er\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003er\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eStatus\u003c/span\u003e \u003cspan class=\"p\"\u003e==\u003c/span\u003e \u003cspan class=\"n\"\u003eDeletionStatus\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eCompleted\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eSelect\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003er\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003er\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eUserId\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eToListAsync\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003eforeach\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003euserId\u003c/span\u003e \u003cspan class=\"k\"\u003ein\u003c/span\u003e \u003cspan class=\"n\"\u003edeletionRequests\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003eremainingData\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003eScanAllTablesForUser\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003euserId\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eremainingData\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAny\u003c/span\u003e\u003cspan class=\"p\"\u003e())\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"n\"\u003ereport\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eIncompleteDeletions\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAdd\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"k\"\u003enew\u003c/span\u003e \u003cspan class=\"n\"\u003eIncompleteDeletion\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"n\"\u003eUserId\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003euserId\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eLocations\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eremainingData\u003c/span\u003e \u003cspan class=\"p\"\u003e});\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003ereport\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eRun this daily. When it finds 14,000 expired consents still active, you\u0026rsquo;ll know about it the same day, not three months later during the quarterly audit.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"change-detection-know-when-privacy-impact-assessments-need-updates\"\u003e\u003ca href=\"/posts/privacy-audit-automation-dotnet-cli/#change-detection-know-when-privacy-impact-assessments-need-updates\" title=\"Change Detection: Know When Privacy Impact Assessments Need Updates\"\u003eChange Detection: Know When Privacy Impact Assessments Need Updates\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eGit integration catches privacy-impacting changes automatically:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eDpiaChangeDetectionCommand\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kd\"\u003easync\u003c/span\u003e \u003cspan class=\"n\"\u003eTask\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eDpiaChangeReport\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003eExecuteAsync\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003estring\u003c/span\u003e \u003cspan class=\"n\"\u003ecurrentCommit\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"kt\"\u003estring\u003c/span\u003e \u003cspan class=\"n\"\u003epreviousCommit\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003ereport\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003enew\u003c/span\u003e \u003cspan class=\"n\"\u003eDpiaChangeReport\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"n\"\u003eCurrentCommit\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003ecurrentCommit\u003c/span\u003e \u003cspan class=\"p\"\u003e};\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003egitDiff\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003eGetGitDiffAsync\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003epreviousCommit\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003ecurrentCommit\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003eforeach\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003echangedFile\u003c/span\u003e \u003cspan class=\"k\"\u003ein\u003c/span\u003e \u003cspan class=\"n\"\u003egitDiff\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eChangedFiles\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eWhere\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ef\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003ef\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ePath\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eEndsWith\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;.cs\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)))\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003ecurrentTree\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eCSharpSyntaxTree\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eParseText\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003eFile\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eReadAllTextAsync\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003echangedFile\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ePath\u003c/span\u003e\u003cspan class=\"p\"\u003e));\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003epreviousTree\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eCSharpSyntaxTree\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eParseText\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003eGetFileContentAtCommitAsync\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003echangedFile\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ePath\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003epreviousCommit\u003c/span\u003e\u003cspan class=\"p\"\u003e));\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"c1\"\u003e// Detect new [PersonalData] properties\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003enewPiiFields\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eGetPiiProperties\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ecurrentTree\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eGetRoot\u003c/span\u003e\u003cspan class=\"p\"\u003e())\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eExcept\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eGetPiiProperties\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003epreviousTree\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eGetRoot\u003c/span\u003e\u003cspan class=\"p\"\u003e()))\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eToList\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003enewPiiFields\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAny\u003c/span\u003e\u003cspan class=\"p\"\u003e())\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"n\"\u003ereport\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eFilesWithPrivacyChanges\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAdd\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"k\"\u003enew\u003c/span\u003e \u003cspan class=\"n\"\u003ePrivacyChange\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                    \u003cspan class=\"n\"\u003eFilePath\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003echangedFile\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ePath\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                    \u003cspan class=\"n\"\u003eNewPiiFields\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003enewPiiFields\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                    \u003cspan class=\"n\"\u003eRequiresDpiaUpdate\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"p\"\u003e});\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003ereport\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNew PII field added in a commit? The tool flags it. External data flow changed? Flagged. Now your Data Protection Impact Assessment updates happen as part of code review, not eighteen months later when an auditor notices the mismatch.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"deletion-verification-test-that-it-actually-works\"\u003e\u003ca href=\"/posts/privacy-audit-automation-dotnet-cli/#deletion-verification-test-that-it-actually-works\" title=\"Deletion Verification: Test That It Actually Works\"\u003eDeletion Verification: Test That It Actually Works\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eDon\u0026rsquo;t trust that deletion requests were honored. Verify:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eDeletionVerificationCommand\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kd\"\u003easync\u003c/span\u003e \u003cspan class=\"n\"\u003eTask\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eDeletionReport\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003eExecuteAsync\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"c1\"\u003e// Find all tables containing user PII\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003epiiTables\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003e_dbContext\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eModel\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eGetEntityTypes\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eWhere\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ee\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003ee\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eGetProperties\u003c/span\u003e\u003cspan class=\"p\"\u003e().\u003c/span\u003e\u003cspan class=\"n\"\u003eAny\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ep\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003ep\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eFindAnnotation\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;Privacy:IsPII\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)?.\u003c/span\u003e\u003cspan class=\"n\"\u003eValue\u003c/span\u003e \u003cspan class=\"k\"\u003eas\u003c/span\u003e \u003cspan class=\"kt\"\u003ebool?\u003c/span\u003e \u003cspan class=\"p\"\u003e==\u003c/span\u003e \u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\u003cspan class=\"p\"\u003e))\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eSelect\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ee\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003ee\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eGetTableName\u003c/span\u003e\u003cspan class=\"p\"\u003e())\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eToList\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"c1\"\u003e// Create synthetic test user with data in all PII tables\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003etestUserId\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003eCreateSyntheticUserWithPii\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003e_deletionService\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eDeleteUserAccount\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003etestUserId\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"c1\"\u003e// Verify deletion actually worked\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003ereport\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003enew\u003c/span\u003e \u003cspan class=\"n\"\u003eDeletionReport\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003eforeach\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003etable\u003c/span\u003e \u003cspan class=\"k\"\u003ein\u003c/span\u003e \u003cspan class=\"n\"\u003epiiTables\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003eremainingRecords\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003eCountRecordsForUser\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003etable\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003etestUserId\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eremainingRecords\u003c/span\u003e \u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"m\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"n\"\u003ereport\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eIssues\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAdd\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e$\u0026#34;Table {table} still contains {remainingRecords} records after deletion\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003ereport\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis creates a synthetic user, exercises deletion, then verifies every PII table is actually empty. No more assuming deletion worked. Prove it.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"processing-records-from-code-not-spreadsheets\"\u003e\u003ca href=\"/posts/privacy-audit-automation-dotnet-cli/#processing-records-from-code-not-spreadsheets\" title=\"Processing Records From Code, Not Spreadsheets\"\u003eProcessing Records From Code, Not Spreadsheets\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eGenerate compliance documentation from your actual system state:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eGenerateProcessingRecordCommand\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kd\"\u003easync\u003c/span\u003e \u003cspan class=\"n\"\u003eTask\u003c/span\u003e \u003cspan class=\"n\"\u003eExecuteAsync\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"k\"\u003erecord\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003enew\u003c/span\u003e \u003cspan class=\"n\"\u003eProcessingRecord\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"n\"\u003eGeneratedDate\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eDateTime\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eUtcNow\u003c/span\u003e \u003cspan class=\"p\"\u003e};\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003eforeach\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003eentityType\u003c/span\u003e \u003cspan class=\"k\"\u003ein\u003c/span\u003e \u003cspan class=\"n\"\u003e_dbContext\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eModel\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eGetEntityTypes\u003c/span\u003e\u003cspan class=\"p\"\u003e())\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003epiiProperties\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eentityType\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eGetProperties\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eWhere\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ep\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003ep\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eFindAnnotation\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;Privacy:IsPII\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)?.\u003c/span\u003e\u003cspan class=\"n\"\u003eValue\u003c/span\u003e \u003cspan class=\"k\"\u003eas\u003c/span\u003e \u003cspan class=\"kt\"\u003ebool?\u003c/span\u003e \u003cspan class=\"p\"\u003e==\u003c/span\u003e \u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eToList\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(!\u003c/span\u003e\u003cspan class=\"n\"\u003epiiProperties\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAny\u003c/span\u003e\u003cspan class=\"p\"\u003e())\u003c/span\u003e \u003cspan class=\"k\"\u003econtinue\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"n\"\u003erecord\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eActivities\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAdd\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"k\"\u003enew\u003c/span\u003e \u003cspan class=\"n\"\u003eProcessingActivity\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"n\"\u003eName\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eentityType\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eClrType\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eName\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"n\"\u003eDataCategories\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003epiiProperties\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eSelect\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ep\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003ep\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eFindAnnotation\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;Privacy:Category\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)?.\u003c/span\u003e\u003cspan class=\"n\"\u003eValue\u003c/span\u003e \u003cspan class=\"k\"\u003eas\u003c/span\u003e \u003cspan class=\"kt\"\u003estring\u003c/span\u003e\u003cspan class=\"p\"\u003e).\u003c/span\u003e\u003cspan class=\"n\"\u003eToList\u003c/span\u003e\u003cspan class=\"p\"\u003e(),\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"n\"\u003eLegalBasis\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eentityType\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eFindAnnotation\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;Privacy:LegalBasis\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)?.\u003c/span\u003e\u003cspan class=\"n\"\u003eValue\u003c/span\u003e \u003cspan class=\"k\"\u003eas\u003c/span\u003e \u003cspan class=\"kt\"\u003estring\u003c/span\u003e \u003cspan class=\"p\"\u003e??\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;NOT_DOCUMENTED\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"n\"\u003ePurpose\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eentityType\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eFindAnnotation\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;Privacy:Purpose\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)?.\u003c/span\u003e\u003cspan class=\"n\"\u003eValue\u003c/span\u003e \u003cspan class=\"k\"\u003eas\u003c/span\u003e \u003cspan class=\"kt\"\u003estring\u003c/span\u003e \u003cspan class=\"p\"\u003e??\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;NOT_DOCUMENTED\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"p\"\u003e});\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003eFile\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eWriteAllTextAsync\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;ProcessingRecord.json\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eJsonSerializer\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eSerialize\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003erecord\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"k\"\u003enew\u003c/span\u003e \u003cspan class=\"n\"\u003eJsonSerializerOptions\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"n\"\u003eWriteIndented\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"kc\"\u003etrue\u003c/span\u003e \u003cspan class=\"p\"\u003e}));\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eDocumentation generated from code annotations is always current. When someone adds a new PII field and forgets to annotate it, the tool flags it as undocumented. The processing record reflects reality, not eighteen-month-old assumptions.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"github-actions-integration\"\u003e\u003ca href=\"/posts/privacy-audit-automation-dotnet-cli/#github-actions-integration\" title=\"GitHub Actions Integration\"\u003eGitHub Actions Integration\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eWire everything into CI/CD:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-yaml\" data-lang=\"yaml\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003ePrivacy Compliance Audit\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003eon\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"nt\"\u003epush\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003ebranches\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"l\"\u003emain, develop]\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"nt\"\u003epull_request\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"nt\"\u003eschedule\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e- \u003cspan class=\"nt\"\u003ecron\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;0 2 * * *\u0026#39;\u003c/span\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"c\"\u003e# Daily at 2 AM\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003ejobs\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"nt\"\u003eprivacy-audit\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003eruns-on\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eubuntu-latest\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003esteps\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e- \u003cspan class=\"nt\"\u003euses\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eactions/checkout@v4\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"nt\"\u003ewith\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e          \u003c/span\u003e\u003cspan class=\"nt\"\u003efetch-depth\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"m\"\u003e2\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e- \u003cspan class=\"nt\"\u003euses\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eactions/setup-dotnet@v4\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"nt\"\u003ewith\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e          \u003c/span\u003e\u003cspan class=\"nt\"\u003edotnet-version\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;9.0.x\u0026#39;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e- \u003cspan class=\"nt\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eDiscover Undocumented PII\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"nt\"\u003erun\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003edotnet run --project PrivacyAudit.Cli -- discover-pii --output pii-report.json\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e- \u003cspan class=\"nt\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eAudit Consent Records\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"nt\"\u003erun\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003edotnet run --project PrivacyAudit.Cli -- audit-consent --output consent-report.json\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e- \u003cspan class=\"nt\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eDetect Privacy-Impacting Changes\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"nt\"\u003erun\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003edotnet run --project PrivacyAudit.Cli -- detect-changes --output changes-report.json\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e- \u003cspan class=\"nt\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eVerify Deletion Completeness\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"nt\"\u003erun\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003edotnet run --project PrivacyAudit.Cli -- verify-deletion --output deletion-report.json\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e- \u003cspan class=\"nt\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eFail on Violations\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"nt\"\u003erun\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003edotnet run --project PrivacyAudit.Cli -- check-compliance --fail-on-violations true\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e- \u003cspan class=\"nt\"\u003euses\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eactions/upload-artifact@v4\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"nt\"\u003eif\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003ealways()\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"nt\"\u003ewith\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e          \u003c/span\u003e\u003cspan class=\"nt\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eprivacy-audit-reports\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e          \u003c/span\u003e\u003cspan class=\"nt\"\u003epath\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;*-report.json\u0026#39;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eUndocumented PII? Build fails. Expired consents? Build fails. Incomplete deletions? Build fails. Compliance becomes a technical gate, not a quarterly prayer meeting.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"what-these-tools-actually-prove\"\u003e\u003ca href=\"/posts/privacy-audit-automation-dotnet-cli/#what-these-tools-actually-prove\" title=\"What These Tools Actually Prove\"\u003eWhat These Tools Actually Prove\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eLet\u0026rsquo;s map this back to what regulators care about:\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eLegal basis documentation\u003c/strong\u003e: The PII discovery tool scans everything and flags undocumented processing. No more hoping developers remembered to update the spreadsheet.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eConsent management\u003c/strong\u003e: Daily automated checks catch expired consents within 24 hours, not 90 days. When someone asks \u0026ldquo;how do you ensure consent is current?\u0026rdquo;, you have timestamped reports.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eDeletion verification\u003c/strong\u003e: Synthetic user tests prove deletion works across all systems. When someone asks \u0026ldquo;how do you verify erasure requests?\u0026rdquo;, you have test results, not assurances.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eChange detection\u003c/strong\u003e: Git-integrated analysis flags privacy-impacting code changes at PR time. Impact assessments update when code changes, not whenever someone remembers.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eProcessing records\u003c/strong\u003e: Documentation generated from code annotations reflects current reality. The record you show auditors matches the system they\u0026rsquo;re auditing.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"getting-started-without-boiling-the-ocean\"\u003e\u003ca href=\"/posts/privacy-audit-automation-dotnet-cli/#getting-started-without-boiling-the-ocean\" title=\"Getting Started Without Boiling the Ocean\"\u003eGetting Started Without Boiling the Ocean\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eDon\u0026rsquo;t try to implement everything at once:\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eWeek 1-2\u003c/strong\u003e: Deploy PII discovery. Baseline your current state. You\u0026rsquo;ll find undocumented fields in 40-60% of your tables. That\u0026rsquo;s normal. Start documenting.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eWeek 3-4\u003c/strong\u003e: Add consent monitoring. Run it daily. When you discover 14,000 expired consents nobody knew about, you\u0026rsquo;ll understand why quarterly audits don\u0026rsquo;t work.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eWeek 5-6\u003c/strong\u003e: Integrate change detection into CI/CD. Make privacy compliance a build requirement. New undocumented PII stops reaching production.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eWeek 7-8\u003c/strong\u003e: Deploy deletion verification. Create synthetic test users. Prove erasure actually works.\u003c/p\u003e\n\u003cp\u003eStart with non-production environments to tune false positive rates. Privacy audit automation reveals uncomfortable truths. Expect resistance from teams who prefer checkbox compliance to technical accountability.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"the-uncomfortable-truth\"\u003e\u003ca href=\"/posts/privacy-audit-automation-dotnet-cli/#the-uncomfortable-truth\" title=\"The Uncomfortable Truth\"\u003eThe Uncomfortable Truth\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eManual privacy audits are security theater. Quarterly spreadsheet reviews cannot detect PII added last Tuesday, consent that expired this morning, or deletion requests honored in some tables but not others.\u003c/p\u003e\n\u003cp\u003ePrivacy compliance is a continuous technical state, not a point-in-time assessment. Your compliance documentation is either generated from your actual system state, or it\u0026rsquo;s fiction. There\u0026rsquo;s no middle ground.\u003c/p\u003e\n\u003cp\u003eBuild .NET CLI tools that make compliance a build-time fact. Use reflection to discover all PII. Use Entity Framework metadata to scan schemas. Use Git integration to catch privacy-impacting changes. Fail builds when violations exist.\u003c/p\u003e\n\u003cp\u003eThe tools in this article are a starting point. Real implementations need customization for your specific architecture and regulatory requirements. But the fundamental principle stands: automate privacy audits or accept that your compliance documentation is wishful thinking.\u003c/p\u003e\n\u003cp\u003eStop reviewing spreadsheets. Start building proof.\u003c/p\u003e\n","date_modified":"2026-05-26T10:22:03+02:00","date_published":"2026-04-30T17:00:00+02:00","id":"https://daily-devops.net/posts/privacy-audit-automation-dotnet-cli/","language":"en","summary":"Quarterly audits can't catch PII added last Tuesday. Build .NET CLI tools that make compliance a build-time fact, not a spreadsheet fantasy.\n","tags":["iso-standards","privacy","cli","dotnet","automation","gdpr","compliance","security","codequality","devops"],"title":"Your Privacy Docs Are Fiction: Let's Fix That with .NET CLI Tools\n","url":"https://daily-devops.net/posts/privacy-audit-automation-dotnet-cli/"},{"authors":[{"name":"Martin Stühmer","url":"https://daily-devops.net/authors/martin/"}],"content_html":"\u003cp\u003eI\u0026rsquo;ve watched this trainwreck unfold a dozen times: Organization gets certified, consultants cash their checks, comprehensive documentation gets filed somewhere, and then\u0026hellip; compliance becomes a Word document ritual. \u0026ldquo;Just check the Azure portal\u0026rdquo; before each release. Screenshot some settings. Sign the checklist. Ship it.\u003c/p\u003e\n\u003cp\u003eThree months later, an audit exposes configuration drift, hardcoded secrets in production, and vulnerable dependencies nobody noticed. The compliance team swears everything was checked. The forensic evidence disagrees.\u003c/p\u003e\n\u003cp\u003eHere\u0026rsquo;s what nobody wants to admit: Manual compliance verification is fundamentally broken. Not \u0026ldquo;could be improved\u0026rdquo; broken. Broken in ways that would make your auditor weep if they understood software. Screenshots aren\u0026rsquo;t documentation. Different people finding different issues isn\u0026rsquo;t \u0026ldquo;thorough review,\u0026rdquo; it\u0026rsquo;s randomness. And \u0026ldquo;I checked it last Tuesday\u0026rdquo; isn\u0026rsquo;t an evidence trail.\u003c/p\u003e\n\u003cp\u003eThe fix isn\u0026rsquo;t more checklists or stricter sign-off procedures. It\u0026rsquo;s treating compliance as what it actually is: an engineering problem. One that demands automated tooling in your CI/CD pipeline, not a rotating sacrifice of whoever drew the short straw this sprint.\u003c/p\u003e\n\u003cp\u003e.NET gives you everything needed: \u003ccode\u003eSystem.CommandLine\u003c/code\u003e for CLI interfaces, Roslyn for code inspection, Azure SDK for infrastructure validation. Let me show you what real compliance automation looks like.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"the-checklist-ceremony-a-horror-story\"\u003e\u003ca href=\"/posts/compliance-verification-dotnet-cli/#the-checklist-ceremony-a-horror-story\" title=\"The Checklist Ceremony (A Horror Story)\"\u003eThe Checklist Ceremony (A Horror Story)\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eLet\u0026rsquo;s talk about what I see in organizations that claim to be \u0026ldquo;compliant.\u0026rdquo;\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"the-sacred-document\"\u003e\u003ca href=\"/posts/compliance-verification-dotnet-cli/#the-sacred-document\" title=\"The Sacred Document\"\u003eThe Sacred Document\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eA Word document titled \u003ccode\u003eSecurity_Compliance_Checklist_v3_FINAL_v2_REALLY_FINAL.docx\u003c/code\u003e gets circulated before each release. Someone (usually whoever lost the argument about who has to do it this time) manually verifies items:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-text\" data-lang=\"text\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e☐ Check code for hardcoded secrets (search GitHub for \u0026#34;password\u0026#34;, \u0026#34;apikey\u0026#34;)\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e☐ Verify all API endpoints require authentication (manually test 10 endpoints)\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e☐ Confirm Azure Key Vault configured correctly (log into portal, screenshot settings)\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e☐ Validate database encryption enabled (run SQL query, save results)\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e☐ Check dependency versions for known vulnerabilities (visit NuGet.org, manually search)\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e☐ Confirm HTTPS enforced on all services (curl a few endpoints)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eEach check gets marked complete. Someone signs off. The deployment proceeds. Everyone feels very professional.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"what-actually-happens-spoiler-nothing-good\"\u003e\u003ca href=\"/posts/compliance-verification-dotnet-cli/#what-actually-happens-spoiler-nothing-good\" title=\"What Actually Happens (Spoiler: Nothing Good)\"\u003eWhat Actually Happens (Spoiler: Nothing Good)\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003e\u003cstrong\u003eInconsistent execution.\u003c/strong\u003e Different team members interpret \u0026ldquo;check for secrets\u0026rdquo; differently. One searches for \u0026ldquo;password\u0026rdquo; in the codebase. Another skips config files because \u0026ldquo;those don\u0026rsquo;t count, right?\u0026rdquo; A third forgets entirely because they\u0026rsquo;re three days behind on a feature that should have shipped last week.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eNo evidence trail.\u003c/strong\u003e The checklist says \u0026ldquo;completed\u0026rdquo; but provides zero forensic evidence. An auditor asks, \u0026ldquo;Can you prove API endpoints required authentication on March 15th?\u0026rdquo; The answer is a signature and a shrug. Maybe a vague memory of clicking around in the portal.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eConfiguration drift goes unnoticed.\u003c/strong\u003e The Azure portal shows correct settings \u003cem\u003etoday\u003c/em\u003e. Last week when someone disabled encryption for \u0026ldquo;quick troubleshooting\u0026rdquo; and forgot to re-enable it? That shipped to production. Nobody knows.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eVulnerable dependencies everywhere.\u003c/strong\u003e Manually checking NuGet packages takes hours and misses transitive dependencies entirely. By the time someone googles the top-level packages, code with known CVEs is already running in prod.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eEnvironments? What environments?\u003c/strong\u003e The checklist gets run in production. Maybe. Does staging match? Dev? Nobody\u0026rsquo;s manually checking every environment every deployment. That would be insane. (It would also be the actual requirement.)\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"why-auditors-should-be-furious\"\u003e\u003ca href=\"/posts/compliance-verification-dotnet-cli/#why-auditors-should-be-furious\" title=\"Why Auditors Should Be Furious\"\u003eWhy Auditors Should Be Furious\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eThe standard requires \u0026ldquo;regular\u0026rdquo; compliance reviews. Regular means every deployment, every config change, every commit. Not \u0026ldquo;quarterly when someone remembers.\u0026rdquo; Not \u0026ldquo;weekly if we\u0026rsquo;re feeling disciplined.\u0026rdquo;\u003c/p\u003e\n\u003cp\u003eIt also requires documented procedures. Screenshots in Word documents aren\u0026rsquo;t procedures. They\u0026rsquo;re artifacts from a specific moment that became lies the instant someone changed something.\u003c/p\u003e\n\u003cp\u003eManual compliance theater satisfies auditors who don\u0026rsquo;t understand software. It doesn\u0026rsquo;t satisfy the actual requirements. And increasingly, auditors \u003cem\u003edo\u003c/em\u003e understand software.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"the-fix-make-the-computer-do-it\"\u003e\u003ca href=\"/posts/compliance-verification-dotnet-cli/#the-fix-make-the-computer-do-it\" title=\"The Fix: Make the Computer Do It\"\u003eThe Fix: Make the Computer Do It\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eCompliance verification needs to be automated, repeatable, verifiable, fast, and comprehensive. Humans are bad at all of these things. Computers are great at them.\u003c/p\u003e\n\u003cp\u003e.NET CLI tools deliver exactly this. Here\u0026rsquo;s how to build them.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"building-your-compliance-scanner\"\u003e\u003ca href=\"/posts/compliance-verification-dotnet-cli/#building-your-compliance-scanner\" title=\"Building Your Compliance Scanner\"\u003eBuilding Your Compliance Scanner\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eWe\u0026rsquo;re creating a .NET Global Tool that scans code and infrastructure, then fails the pipeline when it finds problems. Simple concept, surprisingly effective.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003edotnet new tool -n ComplianceScanner\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003ecd\u003c/span\u003e ComplianceScanner\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003edotnet add package System.CommandLine --version 2.0.2\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003edotnet add package Microsoft.CodeAnalysis.CSharp --version 5.0.0\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003edotnet add package Azure.Identity --version 1.17.1\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003edotnet add package Azure.ResourceManager --version 1.13.2\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe core CLI structure is straightforward:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eusing\u003c/span\u003e \u003cspan class=\"nn\"\u003eSystem.CommandLine\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003erootCommand\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003enew\u003c/span\u003e \u003cspan class=\"n\"\u003eRootCommand\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;Compliance verification tool\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003escanCommand\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003enew\u003c/span\u003e \u003cspan class=\"n\"\u003eCommand\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;scan\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;Scan codebase for violations\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003epathOption\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003enew\u003c/span\u003e \u003cspan class=\"n\"\u003eOption\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"kt\"\u003estring\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;--path\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"p\"\u003e()\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003eDirectory\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eGetCurrentDirectory\u003c/span\u003e\u003cspan class=\"p\"\u003e());\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003escanCommand\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAddOption\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003epathOption\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003escanCommand\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eSetHandler\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kd\"\u003easync\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003epath\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003eviolations\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003enew\u003c/span\u003e \u003cspan class=\"n\"\u003eList\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eComplianceViolation\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eviolations\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAddRange\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003eSecretScanner\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eScanAsync\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003epath\u003c/span\u003e\u003cspan class=\"p\"\u003e));\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eviolations\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAddRange\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003eAuthScanner\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eScanAsync\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003epath\u003c/span\u003e\u003cspan class=\"p\"\u003e));\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eviolations\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAny\u003c/span\u003e\u003cspan class=\"p\"\u003e())\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003eforeach\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003ev\u003c/span\u003e \u003cspan class=\"k\"\u003ein\u003c/span\u003e \u003cspan class=\"n\"\u003eviolations\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"n\"\u003eConsole\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eWriteLine\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e$\u0026#34;[{v.Severity}] {v.Rule}: {v.Message}\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"m\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"c1\"\u003e// Fails the pipeline\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"m\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e},\u003c/span\u003e \u003cspan class=\"n\"\u003epathOption\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003erootCommand\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAddCommand\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003escanCommand\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003erootCommand\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eInvokeAsync\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eargs\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe key insight: return non-zero exit codes. That\u0026rsquo;s what makes pipelines fail. Everything else is details.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"finding-secrets-the-ones-your-team-swears-arent-there\"\u003e\u003ca href=\"/posts/compliance-verification-dotnet-cli/#finding-secrets-the-ones-your-team-swears-arent-there\" title=\"Finding Secrets (The Ones Your Team Swears Aren\u0026rsquo;t There)\"\u003eFinding Secrets (The Ones Your Team Swears Aren\u0026rsquo;t There)\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eRoslyn makes this embarrassingly simple:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eSecretScanner\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003eprivate\u003c/span\u003e \u003cspan class=\"kd\"\u003estatic\u003c/span\u003e \u003cspan class=\"k\"\u003ereadonly\u003c/span\u003e \u003cspan class=\"n\"\u003eRegex\u003c/span\u003e\u003cspan class=\"p\"\u003e[]\u003c/span\u003e \u003cspan class=\"n\"\u003ePatterns\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003enew\u003c/span\u003e\u003cspan class=\"p\"\u003e[]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003enew\u003c/span\u003e \u003cspan class=\"n\"\u003eRegex\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e@\u0026#34;(?i)(password|pwd)\\s*=\\s*[\u0026#34;\u0026#34;\u0026#39;][^\u0026#34;\u0026#34;\u0026#39;]{8,}[\u0026#34;\u0026#34;\u0026#39;]\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e),\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003enew\u003c/span\u003e \u003cspan class=\"n\"\u003eRegex\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e@\u0026#34;(?i)(api[_-]?key)\\s*=\\s*[\u0026#34;\u0026#34;\u0026#39;][^\u0026#34;\u0026#34;\u0026#39;]{20,}[\u0026#34;\u0026#34;\u0026#39;]\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e),\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003enew\u003c/span\u003e \u003cspan class=\"n\"\u003eRegex\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e@\u0026#34;AKIA[0-9A-Z]{16}\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e),\u003c/span\u003e \u003cspan class=\"c1\"\u003e// AWS keys - always fun to find these\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e};\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kd\"\u003estatic\u003c/span\u003e \u003cspan class=\"kd\"\u003easync\u003c/span\u003e \u003cspan class=\"n\"\u003eTask\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eList\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eViolation\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003eScanAsync\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003estring\u003c/span\u003e \u003cspan class=\"n\"\u003epath\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003eviolations\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003enew\u003c/span\u003e \u003cspan class=\"n\"\u003eList\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eViolation\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003eforeach\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003efile\u003c/span\u003e \u003cspan class=\"k\"\u003ein\u003c/span\u003e \u003cspan class=\"n\"\u003eDirectory\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eGetFiles\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003epath\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;*.cs\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eSearchOption\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAllDirectories\u003c/span\u003e\u003cspan class=\"p\"\u003e))\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003etree\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eCSharpSyntaxTree\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eParseText\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003eFile\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eReadAllTextAsync\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003efile\u003c/span\u003e\u003cspan class=\"p\"\u003e));\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003eliterals\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003etree\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eGetRootAsync\u003c/span\u003e\u003cspan class=\"p\"\u003e()).\u003c/span\u003e\u003cspan class=\"n\"\u003eDescendantNodes\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eOfType\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eLiteralExpressionSyntax\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eWhere\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003el\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003el\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eIsKind\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eSyntaxKind\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eStringLiteralExpression\u003c/span\u003e\u003cspan class=\"p\"\u003e));\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"k\"\u003eforeach\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003eliteral\u003c/span\u003e \u003cspan class=\"k\"\u003ein\u003c/span\u003e \u003cspan class=\"n\"\u003eliterals\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ePatterns\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAny\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ep\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003ep\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eIsMatch\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eliteral\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eToken\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eValueText\u003c/span\u003e\u003cspan class=\"p\"\u003e)))\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                    \u003cspan class=\"n\"\u003eviolations\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAdd\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"k\"\u003enew\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;SECRET\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;CRITICAL\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003efile\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;Hardcoded secret\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e));\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003eviolations\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eYou\u0026rsquo;d be amazed how many \u0026ldquo;we definitely don\u0026rsquo;t have hardcoded secrets\u0026rdquo; codebases light up like Christmas trees when you run this.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"finding-naked-endpoints\"\u003e\u003ca href=\"/posts/compliance-verification-dotnet-cli/#finding-naked-endpoints\" title=\"Finding Naked Endpoints\"\u003eFinding Naked Endpoints\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eEvery ASP.NET Core controller endpoint should either have \u003ccode\u003e[Authorize]\u003c/code\u003e or an explicit \u003ccode\u003e[AllowAnonymous]\u003c/code\u003e with a documented reason. \u0026ldquo;We forgot\u0026rdquo; is not a documented reason.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eAuthScanner\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kd\"\u003estatic\u003c/span\u003e \u003cspan class=\"kd\"\u003easync\u003c/span\u003e \u003cspan class=\"n\"\u003eTask\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eList\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eViolation\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003eScanAsync\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003estring\u003c/span\u003e \u003cspan class=\"n\"\u003epath\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003eviolations\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003enew\u003c/span\u003e \u003cspan class=\"n\"\u003eList\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eViolation\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003eforeach\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003efile\u003c/span\u003e \u003cspan class=\"k\"\u003ein\u003c/span\u003e \u003cspan class=\"n\"\u003eDirectory\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eGetFiles\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003epath\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;*.cs\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eSearchOption\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAllDirectories\u003c/span\u003e\u003cspan class=\"p\"\u003e))\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003eroot\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003eCSharpSyntaxTree\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eParseText\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003eFile\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eReadAllTextAsync\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003efile\u003c/span\u003e\u003cspan class=\"p\"\u003e)).\u003c/span\u003e\u003cspan class=\"n\"\u003eGetRootAsync\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"k\"\u003eforeach\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003econtroller\u003c/span\u003e \u003cspan class=\"k\"\u003ein\u003c/span\u003e \u003cspan class=\"n\"\u003eroot\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eDescendantNodes\u003c/span\u003e\u003cspan class=\"p\"\u003e().\u003c/span\u003e\u003cspan class=\"n\"\u003eOfType\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eClassDeclarationSyntax\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eWhere\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ec\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003ec\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eIdentifier\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eText\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eEndsWith\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;Controller\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)))\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003ehasClassAuth\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003econtroller\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAttributeLists\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                    \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eSelectMany\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ea\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003ea\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAttributes\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                    \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAny\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ea\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003ea\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eName\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eToString\u003c/span\u003e\u003cspan class=\"p\"\u003e().\u003c/span\u003e\u003cspan class=\"n\"\u003eContains\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;Authorize\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e));\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"k\"\u003eforeach\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003emethod\u003c/span\u003e \u003cspan class=\"k\"\u003ein\u003c/span\u003e \u003cspan class=\"n\"\u003econtroller\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eDescendantNodes\u003c/span\u003e\u003cspan class=\"p\"\u003e().\u003c/span\u003e\u003cspan class=\"n\"\u003eOfType\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eMethodDeclarationSyntax\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;())\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                    \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003eattrs\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003emethod\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAttributeLists\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eSelectMany\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ea\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003ea\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAttributes\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                    \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003eisEndpoint\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eattrs\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAny\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ea\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003ea\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eName\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eToString\u003c/span\u003e\u003cspan class=\"p\"\u003e().\u003c/span\u003e\u003cspan class=\"n\"\u003eStartsWith\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;Http\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e));\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                    \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003ehasAuth\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eattrs\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAny\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ea\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003ea\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eName\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eToString\u003c/span\u003e\u003cspan class=\"p\"\u003e().\u003c/span\u003e\u003cspan class=\"n\"\u003eContains\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;Authorize\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                                               \u003cspan class=\"p\"\u003e||\u003c/span\u003e \u003cspan class=\"n\"\u003ea\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eName\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eToString\u003c/span\u003e\u003cspan class=\"p\"\u003e().\u003c/span\u003e\u003cspan class=\"n\"\u003eContains\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;AllowAnonymous\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e));\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                    \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eisEndpoint\u003c/span\u003e \u003cspan class=\"p\"\u003e\u0026amp;\u0026amp;\u003c/span\u003e \u003cspan class=\"p\"\u003e!\u003c/span\u003e\u003cspan class=\"n\"\u003ehasClassAuth\u003c/span\u003e \u003cspan class=\"p\"\u003e\u0026amp;\u0026amp;\u003c/span\u003e \u003cspan class=\"p\"\u003e!\u003c/span\u003e\u003cspan class=\"n\"\u003ehasAuth\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                        \u003cspan class=\"n\"\u003eviolations\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAdd\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"k\"\u003enew\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;AUTH\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;HIGH\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003efile\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                            \u003cspan class=\"s\"\u003e$\u0026#34;Endpoint \u0026#39;{method.Identifier}\u0026#39; has no auth attribute\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e));\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003eviolations\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis catches the \u0026ldquo;I\u0026rsquo;ll add authentication later\u0026rdquo; endpoints that somehow made it to production three years ago.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"infrastructure-reality-checks\"\u003e\u003ca href=\"/posts/compliance-verification-dotnet-cli/#infrastructure-reality-checks\" title=\"Infrastructure Reality Checks\"\u003eInfrastructure Reality Checks\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eAzure SDK lets you verify that what\u0026rsquo;s \u003cem\u003eactually\u003c/em\u003e deployed matches what everyone \u003cem\u003ethinks\u003c/em\u003e is deployed. Spoiler: it often doesn\u0026rsquo;t.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eInfraValidator\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003eprivate\u003c/span\u003e \u003cspan class=\"k\"\u003ereadonly\u003c/span\u003e \u003cspan class=\"n\"\u003eArmClient\u003c/span\u003e \u003cspan class=\"n\"\u003e_client\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003enew\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"k\"\u003enew\u003c/span\u003e \u003cspan class=\"n\"\u003eDefaultAzureCredential\u003c/span\u003e\u003cspan class=\"p\"\u003e());\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kd\"\u003easync\u003c/span\u003e \u003cspan class=\"n\"\u003eTask\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eList\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eIssue\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003eValidateAsync\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003eissues\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003enew\u003c/span\u003e \u003cspan class=\"n\"\u003eList\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eIssue\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"k\"\u003eforeach\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003esub\u003c/span\u003e \u003cspan class=\"k\"\u003ein\u003c/span\u003e \u003cspan class=\"n\"\u003e_client\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eGetSubscriptions\u003c/span\u003e\u003cspan class=\"p\"\u003e())\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"c1\"\u003e// Storage: HTTPS only? Encryption on?\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"k\"\u003eforeach\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003estorage\u003c/span\u003e \u003cspan class=\"k\"\u003ein\u003c/span\u003e \u003cspan class=\"n\"\u003esub\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eGetStorageAccountsAsync\u003c/span\u003e\u003cspan class=\"p\"\u003e())\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(!\u003c/span\u003e\u003cspan class=\"n\"\u003estorage\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eData\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eEnableHttpsTrafficOnly\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eGetValueOrDefault\u003c/span\u003e\u003cspan class=\"p\"\u003e())\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                    \u003cspan class=\"n\"\u003eissues\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAdd\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"k\"\u003enew\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003estorage\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eData\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eName\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;Storage allows HTTP\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e));\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003estorage\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eData\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eEncryption\u003c/span\u003e\u003cspan class=\"p\"\u003e?.\u003c/span\u003e\u003cspan class=\"n\"\u003eServices\u003c/span\u003e\u003cspan class=\"p\"\u003e?.\u003c/span\u003e\u003cspan class=\"n\"\u003eBlob\u003c/span\u003e\u003cspan class=\"p\"\u003e?.\u003c/span\u003e\u003cspan class=\"n\"\u003eEnabled\u003c/span\u003e \u003cspan class=\"p\"\u003e!=\u003c/span\u003e \u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                    \u003cspan class=\"n\"\u003eissues\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAdd\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"k\"\u003enew\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003estorage\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eData\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eName\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;Blob encryption disabled\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e));\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"c1\"\u003e// Key Vault: Soft delete? Purge protection?\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"k\"\u003eforeach\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003evault\u003c/span\u003e \u003cspan class=\"k\"\u003ein\u003c/span\u003e \u003cspan class=\"n\"\u003esub\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eGetKeyVaultsAsync\u003c/span\u003e\u003cspan class=\"p\"\u003e())\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(!\u003c/span\u003e\u003cspan class=\"n\"\u003evault\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eData\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eProperties\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eEnableSoftDelete\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eGetValueOrDefault\u003c/span\u003e\u003cspan class=\"p\"\u003e())\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                    \u003cspan class=\"n\"\u003eissues\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAdd\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"k\"\u003enew\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003evault\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eData\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eName\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;No soft delete - secrets can vanish\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e));\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003eissues\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eRun this the first time and prepare for uncomfortable conversations about \u0026ldquo;temporary\u0026rdquo; configuration changes from 2019.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"dependency-nightmares\"\u003e\u003ca href=\"/posts/compliance-verification-dotnet-cli/#dependency-nightmares\" title=\"Dependency Nightmares\"\u003eDependency Nightmares\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eGood news: .NET already has this built in. Bad news: nobody\u0026rsquo;s running it.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kd\"\u003estatic\u003c/span\u003e \u003cspan class=\"kd\"\u003easync\u003c/span\u003e \u003cspan class=\"n\"\u003eTask\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eList\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eViolation\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003eScanVulnerabilitiesAsync\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003estring\u003c/span\u003e \u003cspan class=\"n\"\u003epath\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003eprocess\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eProcess\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eStart\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"k\"\u003enew\u003c/span\u003e \u003cspan class=\"n\"\u003eProcessStartInfo\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003eFileName\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;dotnet\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003eArguments\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;list package --vulnerable --include-transitive --format json\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003eWorkingDirectory\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003epath\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003eRedirectStandardOutput\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003eUseShellExecute\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"kc\"\u003efalse\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e});\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003eoutput\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003eprocess\u003c/span\u003e\u003cspan class=\"p\"\u003e!.\u003c/span\u003e\u003cspan class=\"n\"\u003eStandardOutput\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eReadToEndAsync\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003eprocess\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eWaitForExitAsync\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e// Parse JSON, extract vulnerabilities, return violations\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e// The JSON structure is well-documented and straightforward\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe \u003ccode\u003e--include-transitive\u003c/code\u003e flag is crucial. That\u0026rsquo;s where the real horrors live: three levels deep in dependencies nobody knew existed.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"wiring-it-into-your-pipeline\"\u003e\u003ca href=\"/posts/compliance-verification-dotnet-cli/#wiring-it-into-your-pipeline\" title=\"Wiring It Into Your Pipeline\"\u003eWiring It Into Your Pipeline\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eHere\u0026rsquo;s the GitHub Actions workflow that makes compliance a gate, not a suggestion:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-yaml\" data-lang=\"yaml\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eCompliance Gate\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003eon\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"l\"\u003epull_request, push]\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003ejobs\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"nt\"\u003ecompliance\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003eruns-on\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eubuntu-latest\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003esteps\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e- \u003cspan class=\"nt\"\u003euses\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eactions/checkout@v6\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e- \u003cspan class=\"nt\"\u003euses\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eactions/setup-dotnet@v5\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"nt\"\u003ewith\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e          \u003c/span\u003e\u003cspan class=\"nt\"\u003edotnet-version\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;10.0.x\u0026#39;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e- \u003cspan class=\"nt\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eInstall Scanner\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"nt\"\u003erun\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003edotnet tool install --global ComplianceScanner\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e- \u003cspan class=\"nt\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eScan Code\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"nt\"\u003erun\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003ecompliance-scanner scan --path .\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e- \u003cspan class=\"nt\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eCheck Vulnerabilities  \u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"nt\"\u003erun\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003edotnet list package --vulnerable --include-transitive\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e- \u003cspan class=\"nt\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eValidate Infrastructure\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"nt\"\u003erun\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003ecompliance-scanner validate-infra\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"nt\"\u003eenv\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e          \u003c/span\u003e\u003cspan class=\"nt\"\u003eAZURE_CLIENT_ID\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003e${{ secrets.AZURE_CLIENT_ID }}\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e          \u003c/span\u003e\u003cspan class=\"nt\"\u003eAZURE_TENANT_ID\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003e${{ secrets.AZURE_TENANT_ID }}\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eMake this a required status check. No exceptions. \u0026ldquo;But we need to ship!\u0026rdquo; is exactly when you need it most.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"reports-for-the-auditors\"\u003e\u003ca href=\"/posts/compliance-verification-dotnet-cli/#reports-for-the-auditors\" title=\"Reports for the Auditors\"\u003eReports for the Auditors\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eGenerate JSON, HTML, or Markdown reports from your scan results. Auditors love artifacts they can file. More importantly, \u003cem\u003eyou\u003c/em\u003e love having evidence when someone asks \u0026ldquo;but did you actually check?\u0026rdquo;\u003c/p\u003e\n\u003cp\u003eThe report generator is straightforward: serialize your violations to the format of choice, timestamp it, and archive it. The structure matters less than the fact that it exists and is generated automatically.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"for-the-auditors-in-the-room\"\u003e\u003ca href=\"/posts/compliance-verification-dotnet-cli/#for-the-auditors-in-the-room\" title=\"For the Auditors in the Room\"\u003eFor the Auditors in the Room\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eYes, this satisfies the standard. Specifically:\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eA.5.36 (Regular compliance review):\u003c/strong\u003e Running on every PR and deployment is \u0026ldquo;regular.\u0026rdquo; Quarterly manual checks are not.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eA.8.8 (Vulnerability management):\u003c/strong\u003e Automated scanning that blocks deployment beats \u0026ldquo;we\u0026rsquo;ll check NuGet.org when we remember.\u0026rdquo;\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eA.5.37 (Documented procedures):\u003c/strong\u003e The code \u003cem\u003eis\u003c/em\u003e the documentation. It runs the same way every time. Unlike Bob\u0026rsquo;s interpretation of the checklist.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eA.8.24 (Cryptography):\u003c/strong\u003e Automated validation that encryption is actually enabled, not just assumed to be.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"rolling-this-out-without-a-mutiny\"\u003e\u003ca href=\"/posts/compliance-verification-dotnet-cli/#rolling-this-out-without-a-mutiny\" title=\"Rolling This Out (Without a Mutiny)\"\u003eRolling This Out (Without a Mutiny)\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eDon\u0026rsquo;t try to implement everything at once. That\u0026rsquo;s how you get ignored.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eWeek 1: Secret scanning.\u003c/strong\u003e Start with warnings. Let the team see what\u0026rsquo;s been lurking. Move to failures once the initial panic subsides.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eWeek 2: Vulnerability checks.\u003c/strong\u003e Add \u003ccode\u003edotnet list package --vulnerable\u003c/code\u003e to the pipeline. Fail on CRITICAL. Watch the dependency update PRs roll in.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eWeek 3-4: Auth verification.\u003c/strong\u003e Audit existing endpoints first. You\u0026rsquo;ll find things. Fix them before enforcing.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eWeek 5-6: Infrastructure validation.\u003c/strong\u003e Run manually first. Discover the \u0026ldquo;temporary\u0026rdquo; configs. Remediate. Then automate.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eWeek 7: Reporting.\u003c/strong\u003e Generate artifacts. Distribute to stakeholders. Watch compliance become boring (which is exactly what you want).\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"the-bottom-line\"\u003e\u003ca href=\"/posts/compliance-verification-dotnet-cli/#the-bottom-line\" title=\"The Bottom Line\"\u003eThe Bottom Line\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eManual compliance is a lie everyone agrees to tell. \u0026ldquo;We checked\u0026rdquo; means \u0026ldquo;someone signed something.\u0026rdquo; It doesn\u0026rsquo;t mean the systems are actually secure.\u003c/p\u003e\n\u003cp\u003eAutomated CLI tools change the equation. They run every time. They check the same things. They generate evidence. They fail the build when something\u0026rsquo;s wrong.\u003c/p\u003e\n\u003cp\u003e.NET gives you \u003ccode\u003eSystem.CommandLine\u003c/code\u003e (finally at GA!), Roslyn, and the Azure SDK. The tooling exists. The patterns are straightforward. The only thing standing between your organization and actual compliance is the decision to stop pretending the Word doc is enough.\u003c/p\u003e\n\u003cp\u003eBuild the scanner. Wire it into the pipeline. Make it a required check.\u003c/p\u003e\n\u003cp\u003eOr keep signing the checklist. Your call.\u003c/p\u003e\n","date_modified":"2026-05-26T10:22:03+02:00","date_published":"2026-04-23T17:00:00+02:00","id":"https://daily-devops.net/posts/compliance-verification-dotnet-cli/","language":"en","summary":"Consultants paid. Docs filed. Then compliance becomes a Word doc ritual until an audit exposes the drift. CLI tools fix what checklists never could.","tags":["iso-standards","cli","dotnet","automation","compliance","security","devops","cicd","azure","codequality"],"title":"Certified, Filed, Forgotten: The Compliance Trainwreck","url":"https://daily-devops.net/posts/compliance-verification-dotnet-cli/"},{"authors":[{"name":"Martin Stühmer","url":"https://daily-devops.net/authors/martin/"}],"content_html":"\u003cp\u003eYou discover on a Tuesday morning that an authentication anomaly in your production API has been happening since last Thursday. Five days of potential unauthorized access. Your team learns about it from a customer complaint, not from your monitoring. Your \u0026ldquo;incident response plan\u0026rdquo; is a Word document last updated 18 months ago. The on-call engineer is unavailable. Nobody remembers where the rollback runbooks are stored.\u003c/p\u003e\n\u003cp\u003eThis isn\u0026rsquo;t a hypothetical nightmare scenario—this is what happens when incident response is a manual process instead of an automated system.\u003c/p\u003e\n\u003cp\u003eISO/IEC 27001 Control A.16 (Information security incident management) and A.17.1 (Information security continuity) don\u0026rsquo;t merely suggest having incident response procedures. They mandate documented, tested, and continuously improved processes for detecting, responding to, and learning from security incidents. The standard recognizes a fundamental truth: incidents aren\u0026rsquo;t just data breaches or server compromises. They include failed deployments that expose sensitive data, configuration drift that opens attack vectors, dependency vulnerabilities that attackers exploit, and authentication anomalies that signal reconnaissance attempts.\u003c/p\u003e\n\u003cp\u003eThe challenge isn\u0026rsquo;t writing an incident response plan. Every organization has one gathering digital dust. The challenge is making incident response actually work when the production environment is on fire, the security team is in different time zones, and every minute of undetected compromise expands the blast radius.\u003c/p\u003e\n\u003cp\u003eGitHub Actions transforms incident response from a theoretical document into an executable workflow. When properly configured, it detects security events in real-time, automatically creates structured incident tickets with severity classification, executes rollback procedures without human intervention, notifies on-call rotations through the channels they actually monitor, and generates immutable audit logs that satisfy both ISO 27001 evidence requirements and post-incident analysis.\u003c/p\u003e\n\u003cp\u003eThis isn\u0026rsquo;t about replacing security teams with automation. It\u0026rsquo;s about ensuring that when incidents occur—and they will occur—the initial detection, triage, and containment happen in seconds instead of days.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"the-fatal-pattern-when-incident-response-is-a-manual-afterthought\"\u003e\u003ca href=\"/posts/incident-response-github-actions/#the-fatal-pattern-when-incident-response-is-a-manual-afterthought\" title=\"The Fatal Pattern: When Incident Response Is a Manual Afterthought\"\u003eThe Fatal Pattern: When Incident Response Is a Manual Afterthought\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eLet\u0026rsquo;s examine what \u0026ldquo;incident response\u0026rdquo; looks like in organizations that haven\u0026rsquo;t automated their processes. This is the baseline against which ISO 27001 auditors measure your security maturity.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"the-manual-incident-response-anti-pattern\"\u003e\u003ca href=\"/posts/incident-response-github-actions/#the-manual-incident-response-anti-pattern\" title=\"The Manual Incident Response Anti-Pattern\"\u003eThe Manual Incident Response Anti-Pattern\u003c/a\u003e\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-yaml\" data-lang=\"yaml\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c\"\u003e# ❌ Manual Incident Response Reality\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c\"\u003e# Detection: Quarterly audits (if someone remembers)\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c\"\u003e# Notification: Email to security-team@company.com (4 recipients left months ago)\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c\"\u003e# Response: Find someone with prod access. Hope they\u0026#39;re awake. Pray.\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c\"\u003e# Patching: \u0026#34;When someone has time\u0026#34; (never)\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c\"\u003e# Postmortem: Meeting notes nobody reads\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c\"\u003e# Results:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c\"\u003e# Time to Detection: Days to weeks\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c\"\u003e# Time to Response: Hours to days\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c\"\u003e# Recurrence Rate: High\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis isn\u0026rsquo;t an exaggeration. This is the documented reality in organizations where incident response remains a manual process. The incident response plan exists as a compliance artifact, not as an operational system. When actual incidents occur, teams improvise under pressure, miss critical steps, and create gaps that both attackers and auditors exploit.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"the-real-cost-hidden-until-it-isnt\"\u003e\u003ca href=\"/posts/incident-response-github-actions/#the-real-cost-hidden-until-it-isnt\" title=\"The Real Cost: Hidden Until It Isn\u0026rsquo;t\"\u003eThe Real Cost: Hidden Until It Isn\u0026rsquo;t\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eThe fatal flaw of manual incident response isn\u0026rsquo;t that it never works—it\u0026rsquo;s that it fails unpredictably and catastrophically:\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eDiscovery Latency\u003c/strong\u003e: Security incidents discovered manually weeks after occurrence give attackers extended dwell time to escalate privileges, exfiltrate data, and establish persistence. The 2023 Verizon Data Breach Investigations Report found that 68% of breaches took months to discover. Manual processes don\u0026rsquo;t just delay response—they extend the window of vulnerability.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eKnowledge Silos\u003c/strong\u003e: When incident response procedures live in people\u0026rsquo;s heads instead of executable workflows, your response capability depends on who\u0026rsquo;s available when the incident occurs. The engineer who knows the rollback procedure is on vacation. The security analyst who understands the authentication system left the company three months ago. Tribal knowledge doesn\u0026rsquo;t scale, doesn\u0026rsquo;t survive turnover, and creates single points of failure.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eAlert Fatigue and Desensitization\u003c/strong\u003e: Email distribution lists for security alerts create a tragedy of the commons. When everyone is responsible, nobody is responsible. Critical alerts drown in noise. Teams develop learned helplessness—\u0026ldquo;the alerts are always firing, they\u0026rsquo;re probably false positives\u0026rdquo;—until the one real incident gets ignored alongside the noise.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eCompliance Theater vs. Operational Reality\u003c/strong\u003e: The incident response plan that satisfies auditors during annual reviews bears no resemblance to what actually happens during incidents. Teams improvise, skip documented steps that don\u0026rsquo;t work in practice, and create shadow processes that aren\u0026rsquo;t captured in compliance documentation. This gap creates audit risk and operational brittleness.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eCompounding Delays\u003c/strong\u003e: Manual processes create cascading delays. Detection takes days. Notification takes hours. Triage takes hours more. Finding the right person with the right access takes more time. Each handoff introduces latency, miscommunication, and opportunities for mistakes. By the time response actions execute, the incident has evolved into a crisis.\u003c/p\u003e\n\u003cp\u003eISO 27001 A.16.1 requires \u0026ldquo;management responsibilities and procedures shall be established to ensure a quick, effective and orderly response to information security incidents.\u0026rdquo; The standard doesn\u0026rsquo;t specify \u003cem\u003ehow\u003c/em\u003e to achieve this, but it does mandate measurable effectiveness. Manual processes can\u0026rsquo;t deliver the speed, consistency, or evidence trail that both operational excellence and compliance require.\u003c/p\u003e\n\u003cp\u003eThe alternative isn\u0026rsquo;t theoretical. It\u0026rsquo;s automatable, testable, and already implemented in organizations that treat incident response as a system engineering problem rather than a documentation exercise.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"the-solution-automated-incident-response-with-github-actions\"\u003e\u003ca href=\"/posts/incident-response-github-actions/#the-solution-automated-incident-response-with-github-actions\" title=\"The Solution: Automated Incident Response with GitHub Actions\"\u003eThe Solution: Automated Incident Response with GitHub Actions\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eEffective incident response requires four capabilities: rapid detection, structured notification, automated containment, and systematic learning. GitHub Actions provides the automation substrate to deliver all four with auditability that satisfies ISO 27001 evidence requirements.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"architecture-event-driven-incident-detection\"\u003e\u003ca href=\"/posts/incident-response-github-actions/#architecture-event-driven-incident-detection\" title=\"Architecture: Event-Driven Incident Detection\"\u003eArchitecture: Event-Driven Incident Detection\u003c/a\u003e\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-yaml\" data-lang=\"yaml\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c\"\u003e# ✅ Automated incident detection and response\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eSecurity Incident Detection and Response\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003eon\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"nt\"\u003erepository_vulnerability_alert\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003etypes\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"l\"\u003ecreate]\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"nt\"\u003eworkflow_run\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003eworkflows\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;Production Deploy\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003etypes\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"l\"\u003ecompleted]\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"nt\"\u003eschedule\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e- \u003cspan class=\"nt\"\u003ecron\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;0 */6 * * *\u0026#39;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"nt\"\u003eworkflow_dispatch\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003einputs\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e\u003cspan class=\"nt\"\u003eincident_type\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"nt\"\u003etype\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003echoice\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"nt\"\u003eoptions\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"l\"\u003eauthentication_anomaly, failed_deployment, dependency_vulnerability]\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003ejobs\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"nt\"\u003edetect-auth-anomalies\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003eruns-on\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eubuntu-latest\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003esteps\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e- \u003cspan class=\"nt\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eQuery Application Insights\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"nt\"\u003eid\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003equery\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"nt\"\u003euses\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eazure/CLI@v2\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"nt\"\u003ewith\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e          \u003c/span\u003e\u003cspan class=\"nt\"\u003einlineScript\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e|\u003c/span\u003e\u003cspan class=\"sd\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"sd\"\u003e            QUERY=\u0026#39;requests \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"sd\"\u003e            | where timestamp \u0026gt; ago(5m) and name contains \u0026#34;auth\u0026#34; and success == false\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"sd\"\u003e            | summarize FailureCount=count() by client_IP\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"sd\"\u003e            | where FailureCount \u0026gt; 100\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"sd\"\u003e            \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"sd\"\u003e            RESULT=$(az monitor app-insights query --app ${{ secrets.APPINSIGHTS_ID }} --analytics-query \u0026#34;$QUERY\u0026#34; -o json)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"sd\"\u003e            echo \u0026#34;detected=$([ $(echo $RESULT | jq \u0026#39;.tables[0].rows | length\u0026#39;) -gt 0 ] \u0026amp;\u0026amp; echo true || echo false)\u0026#34; \u0026gt;\u0026gt; $GITHUB_OUTPUT\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e- \u003cspan class=\"nt\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eCreate Incident Issue\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"nt\"\u003eif\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003esteps.query.outputs.detected == \u0026#39;true\u0026#39;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"nt\"\u003euses\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eactions/github-script@v7\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"nt\"\u003ewith\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e          \u003c/span\u003e\u003cspan class=\"nt\"\u003escript\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e|\u003c/span\u003e\u003cspan class=\"sd\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"sd\"\u003e            await github.rest.issues.create({\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"sd\"\u003e              owner: context.repo.owner,\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"sd\"\u003e              repo: context.repo.repo,\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"sd\"\u003e              title: `[CRITICAL] Auth Anomaly - ${new Date().toISOString()}`,\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"sd\"\u003e              body: `## Security Incident\\n\\n**ISO 27001 Control**: A.16.1\\n\\n### Actions\\n- [ ] Investigate IPs\\n- [ ] Apply rate limiting`,\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"sd\"\u003e              labels: [\u0026#39;security-incident\u0026#39;, \u0026#39;critical\u0026#39;]\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"sd\"\u003e            });\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"nt\"\u003eauto-rollback\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003eif\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003egithub.event.workflow_run.conclusion == \u0026#39;failure\u0026#39;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003eruns-on\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eubuntu-latest\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003esteps\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e- \u003cspan class=\"nt\"\u003euses\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eactions/checkout@v4\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"nt\"\u003ewith\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e          \u003c/span\u003e\u003cspan class=\"nt\"\u003efetch-depth\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"m\"\u003e0\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e- \u003cspan class=\"nt\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eRollback to Last Good Commit\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"nt\"\u003erun\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e|\u003c/span\u003e\u003cspan class=\"sd\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"sd\"\u003e          LAST_GOOD=$(git log --format=\u0026#34;%H\u0026#34; --grep=\u0026#34;deploy: success\u0026#34; -1)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"sd\"\u003e          git revert --no-commit $LAST_GOOD..${{ github.sha }}\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"sd\"\u003e          git commit -m \u0026#34;chore: auto-rollback [skip ci]\u0026#34;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"sd\"\u003e          git push origin HEAD:main\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e- \u003cspan class=\"nt\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eCreate Incident Issue\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"nt\"\u003euses\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eactions/github-script@v7\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"nt\"\u003ewith\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e          \u003c/span\u003e\u003cspan class=\"nt\"\u003escript\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e|\u003c/span\u003e\u003cspan class=\"sd\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"sd\"\u003e            await github.rest.issues.create({\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"sd\"\u003e              owner: context.repo.owner,\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"sd\"\u003e              repo: context.repo.repo,\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"sd\"\u003e              title: `[HIGH] Auto-Rollback - ${new Date().toISOString()}`,\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"sd\"\u003e              body: `## Rollback Executed\\n\\n**Failed**: \\`${{ github.sha }}\\`\\n**ISO 27001**: A.17.1\\n\\n### Postmortem\\n- [ ] Root cause?\\n- [ ] Prevention?`,\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"sd\"\u003e              labels: [\u0026#39;security-incident\u0026#39;, \u0026#39;rollback\u0026#39;]\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"sd\"\u003e            });\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis workflow architecture delivers several capabilities that manual processes cannot:\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eReal-Time Detection\u003c/strong\u003e: Security events trigger workflows immediately, not when someone checks a dashboard. Authentication anomalies, deployment failures, and dependency vulnerabilities generate incidents within seconds of occurrence. Detection latency measured in seconds, not days.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eStructured Incident Classification\u003c/strong\u003e: Every incident automatically receives severity classification, ISO 27001 control mapping, unique incident ID, and consistent metadata. No more ambiguous \u0026ldquo;urgent\u0026rdquo; emails that might mean anything from minor configuration drift to active breach.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eAutomated Containment\u003c/strong\u003e: Critical incidents trigger automated response actions—rollbacks execute without human intervention, security patches apply automatically for high-severity CVEs, rate limiting engages for authentication anomalies. Containment happens in minutes, not hours.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eImmutable Audit Trail\u003c/strong\u003e: Every incident generates a GitHub Issue with complete timeline, automated actions taken, manual actions required, and ISO 27001 control references. Auditors can trace detection→response→resolution for every incident with Git-backed evidence.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"integration-with-monitoring-application-insights-as-security-sensor\"\u003e\u003ca href=\"/posts/incident-response-github-actions/#integration-with-monitoring-application-insights-as-security-sensor\" title=\"Integration with Monitoring: Application Insights as Security Sensor\"\u003eIntegration with Monitoring: Application Insights as Security Sensor\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eThe authentication anomaly detection demonstrates a pattern applicable to any monitoring platform. Application Insights becomes a security sensor, not just a performance dashboard:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Query pattern for authentication anomalies\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003erequests \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e|\u003c/span\u003e where timestamp \u0026gt; ago\u003cspan class=\"o\"\u003e(\u003c/span\u003e5m\u003cspan class=\"o\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e|\u003c/span\u003e where name contains \u003cspan class=\"s2\"\u003e\u0026#34;login\u0026#34;\u003c/span\u003e or name contains \u003cspan class=\"s2\"\u003e\u0026#34;auth\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e|\u003c/span\u003e where \u003cspan class=\"nv\"\u003esuccess\u003c/span\u003e \u003cspan class=\"o\"\u003e==\u003c/span\u003e \u003cspan class=\"nb\"\u003efalse\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e|\u003c/span\u003e summarize \u003cspan class=\"nv\"\u003eFailureCount\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003ecount\u003cspan class=\"o\"\u003e()\u003c/span\u003e by client_IP, user_Id\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e|\u003c/span\u003e where FailureCount \u0026gt; \u003cspan class=\"m\"\u003e100\u003c/span\u003e or \u003cspan class=\"o\"\u003e(\u003c/span\u003eisnotempty\u003cspan class=\"o\"\u003e(\u003c/span\u003euser_Id\u003cspan class=\"o\"\u003e)\u003c/span\u003e and FailureCount \u0026gt; 10\u003cspan class=\"o\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis query identifies two distinct attack patterns: credential stuffing (many failed attempts from single IP) and targeted account compromise (many failed attempts for single user). The workflow executes this query every 6 hours via scheduled trigger, or immediately after authentication-related code deployments.\u003c/p\u003e\n\u003cp\u003eWhen anomalies surface, the automated response includes:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003eImmediate notification\u003c/strong\u003e to on-call security team via Slack webhook that they actually monitor\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eStructured incident ticket\u003c/strong\u003e with actionable investigation steps, not vague \u0026ldquo;something\u0026rsquo;s wrong\u0026rdquo; alerts\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eSeverity classification\u003c/strong\u003e that distinguishes between rate-limiting evasion attempts (medium) and coordinated breach attempts (critical)\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eISO 27001 control mapping\u003c/strong\u003e that satisfies auditor evidence requirements without manual documentation\u003c/li\u003e\n\u003c/ul\u003e\n\n\n\n\n\u003ch3 id=\"automated-rollback-when-code-becomes-incident\"\u003e\u003ca href=\"/posts/incident-response-github-actions/#automated-rollback-when-code-becomes-incident\" title=\"Automated Rollback: When Code Becomes Incident\"\u003eAutomated Rollback: When Code Becomes Incident\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eFailed deployments aren\u0026rsquo;t just DevOps problems—they\u0026rsquo;re security incidents when they expose sensitive data, disable authentication checks, or create configuration vulnerabilities. The rollback workflow treats deployment failures as incidents requiring the same rigor as authentication breaches:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-yaml\" data-lang=\"yaml\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c\"\u003e# Rollback procedure: automated, tested, auditable\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"l\"\u003egit log --format=\u0026#34;%H\u0026#34; --grep=\u0026#34;deploy: success\u0026#34; -1 \u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"c\"\u003e# Find last good commit\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"l\"\u003egit revert --no-commit $LAST_GOOD..$CURRENT       \u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"c\"\u003e# Create revert commit\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"l\"\u003egit push origin HEAD:main                         \u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"c\"\u003e# Restore production immediately\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis isn\u0026rsquo;t novel version control usage—it\u0026rsquo;s applying Git semantics to incident response. The last known good state is always recoverable. The rollback action is reversible. The entire incident timeline exists in commit history. Auditors can verify rollback procedures work because they\u0026rsquo;re executed automatically on every deployment failure, not just tested during annual disaster recovery drills.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"the-postmortem-requirement-learning-as-compliance\"\u003e\u003ca href=\"/posts/incident-response-github-actions/#the-postmortem-requirement-learning-as-compliance\" title=\"The Postmortem Requirement: Learning as Compliance\"\u003eThe Postmortem Requirement: Learning as Compliance\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eISO 27001 A.16.1 requires \u0026ldquo;information security incidents shall be assessed and it shall be decided if they are to be classified as information security incidents.\u0026rdquo; This assessment isn\u0026rsquo;t optional—it\u0026rsquo;s how organizations demonstrate continuous improvement.\u003c/p\u003e\n\u003cp\u003eThe incident ticket template includes a postmortem structure:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-markdown\" data-lang=\"markdown\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"gu\"\u003e### Postmortem Template\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003e- [ ]\u003c/span\u003e What was the intended change?\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003e- [ ]\u003c/span\u003e What actually happened?  \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003e- [ ]\u003c/span\u003e What was the root cause?\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003e- [ ]\u003c/span\u003e How was it detected?\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003e- [ ]\u003c/span\u003e How was it resolved?\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003e- [ ]\u003c/span\u003e What prevented earlier detection?\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003e- [ ]\u003c/span\u003e What will prevent recurrence?\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis isn\u0026rsquo;t bureaucratic box-checking. These questions systematically capture knowledge that prevents incident recurrence. The answers inform:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003eDetection improvements\u003c/strong\u003e: \u0026ldquo;What prevented earlier detection?\u0026rdquo; identifies monitoring gaps\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eResponse improvements\u003c/strong\u003e: \u0026ldquo;How was it resolved?\u0026rdquo; documents effective response patterns\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003ePrevention improvements\u003c/strong\u003e: \u0026ldquo;What will prevent recurrence?\u0026rdquo; drives architectural changes\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eWhen postmortems are structured GitHub Issues instead of meeting notes, they become searchable, linkable, and trackable. Teams can reference previous incidents when designing new features. Patterns emerge across incidents that single events don\u0026rsquo;t reveal. Knowledge persists beyond employee tenure.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"iso-27001-compliance-evidence-audit-trail-without-theater\"\u003e\u003ca href=\"/posts/incident-response-github-actions/#iso-27001-compliance-evidence-audit-trail-without-theater\" title=\"ISO 27001 Compliance Evidence: Audit Trail Without Theater\"\u003eISO 27001 Compliance Evidence: Audit Trail Without Theater\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eAuditors evaluating ISO 27001 A.16 compliance ask three questions:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e\u003cstrong\u003eHow do you detect incidents?\u003c/strong\u003e Manual monitoring vs. automated detection with defined thresholds\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eHow do you respond to incidents?\u003c/strong\u003e Ad-hoc improvisation vs. documented, tested procedures\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eHow do you improve after incidents?\u003c/strong\u003e Verbal discussions vs. systematic postmortem analysis\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eGitHub Actions provides auditable evidence for all three:\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eDetection Evidence\u003c/strong\u003e: Workflow run history shows detection workflows executing on schedule, incident tickets created with timestamps and triggering events, queries executed against monitoring platforms. Auditors can verify detection capabilities are tested continuously, not just demonstrated during audits.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eResponse Evidence\u003c/strong\u003e: GitHub Issues contain complete incident timeline from detection through resolution, automated actions taken (rollback commits, security patches, notifications), manual actions performed (investigation notes, coordination with external teams), and closure criteria (how was resolution verified?).\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eImprovement Evidence\u003c/strong\u003e: Postmortem issues linked to infrastructure changes, new detection workflows added after incident gaps identified, and rollback procedures refined based on actual incident experience. The Git history of workflow files shows incident response procedures evolving over time.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"github-audit-log-the-compliance-requirement-you-already-satisfy\"\u003e\u003ca href=\"/posts/incident-response-github-actions/#github-audit-log-the-compliance-requirement-you-already-satisfy\" title=\"GitHub Audit Log: The Compliance Requirement You Already Satisfy\"\u003eGitHub Audit Log: The Compliance Requirement You Already Satisfy\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eISO 27001 A.16.1.7 requires \u0026ldquo;organizations shall establish procedures for the collection and preservation of evidence.\u0026rdquo; GitHub\u0026rsquo;s audit log automatically provides:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003eWho\u003c/strong\u003e triggered security workflows (user attribution)\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eWhat\u003c/strong\u003e actions were taken (workflow execution logs)\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eWhen\u003c/strong\u003e incidents occurred (immutable timestamps)\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eWhy\u003c/strong\u003e actions were automated vs. manual (workflow trigger events)\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eThis audit trail is tamper-evident, continuously available, and doesn\u0026rsquo;t require additional infrastructure. Organizations already using GitHub for source control satisfy evidence requirements without additional compliance tooling—if they architect incident response as code instead of documentation.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"practical-implementation-from-theory-to-production\"\u003e\u003ca href=\"/posts/incident-response-github-actions/#practical-implementation-from-theory-to-production\" title=\"Practical Implementation: From Theory to Production\"\u003ePractical Implementation: From Theory to Production\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eThe workflow examples demonstrate capabilities, not prescriptive templates. Actual implementation requires adapting these patterns to your specific infrastructure, monitoring platforms, and organizational constraints.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"start-with-one-incident-type\"\u003e\u003ca href=\"/posts/incident-response-github-actions/#start-with-one-incident-type\" title=\"Start with One Incident Type\"\u003eStart with One Incident Type\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eDon\u0026rsquo;t attempt to automate all incident response simultaneously. Start with the incident type that\u0026rsquo;s both frequent enough to test regularly and impactful enough to justify automation effort:\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eFrequent incidents\u003c/strong\u003e: Failed deployments, dependency vulnerabilities, configuration drift\n\u003cstrong\u003eImpactful incidents\u003c/strong\u003e: Authentication anomalies, data exposure, privilege escalation\u003c/p\u003e\n\u003cp\u003eA production-ready starting point: automated rollback on deployment failure. This incident type occurs naturally during development, doesn\u0026rsquo;t require complex security instrumentation, and provides immediate value (reduced downtime) while building compliance evidence.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"test-incident-response-before-incidents-occur\"\u003e\u003ca href=\"/posts/incident-response-github-actions/#test-incident-response-before-incidents-occur\" title=\"Test Incident Response Before Incidents Occur\"\u003eTest Incident Response Before Incidents Occur\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eThe \u003ccode\u003eworkflow_dispatch\u003c/code\u003e trigger enables incident simulation:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-yaml\" data-lang=\"yaml\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003eworkflow_dispatch\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"nt\"\u003einputs\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003eincident_type\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e\u003cspan class=\"nt\"\u003edescription\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;Type of incident to simulate\u0026#39;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e\u003cspan class=\"nt\"\u003etype\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003echoice\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e\u003cspan class=\"nt\"\u003eoptions\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e- \u003cspan class=\"l\"\u003eauthentication_anomaly\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e- \u003cspan class=\"l\"\u003econfiguration_drift  \u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e- \u003cspan class=\"l\"\u003efailed_deployment\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eQuarterly incident response testing becomes: trigger the workflow manually, verify incident ticket creation, validate notification delivery, confirm automated containment actions execute correctly, and review postmortem template completeness.\u003c/p\u003e\n\u003cp\u003eThis simulation satisfies ISO 27001\u0026rsquo;s requirement for testing incident response procedures while building team familiarity with automated workflows before actual incidents occur.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"integrate-gradually-with-existing-tools\"\u003e\u003ca href=\"/posts/incident-response-github-actions/#integrate-gradually-with-existing-tools\" title=\"Integrate Gradually with Existing Tools\"\u003eIntegrate Gradually with Existing Tools\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eOrganizations already have monitoring platforms (Datadog, New Relic, Azure Monitor), ticketing systems (Jira, ServiceNow), and notification channels (Slack, Teams, PagerDuty). GitHub Actions integrates with all of them:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003eMonitoring integration\u003c/strong\u003e: Azure CLI action, Datadog API, custom API queries via \u003ccode\u003ecurl\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eTicketing integration\u003c/strong\u003e: REST APIs, webhook notifications, email gateways\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eNotification integration\u003c/strong\u003e: Slack actions, Teams webhooks, PagerDuty events\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eThe authentication anomaly workflow uses Application Insights, but the same pattern applies to any monitoring platform that exposes query APIs. Replace the Azure CLI step with your monitoring platform\u0026rsquo;s equivalent.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"the-honest-assessment-what-automation-cannot-do\"\u003e\u003ca href=\"/posts/incident-response-github-actions/#the-honest-assessment-what-automation-cannot-do\" title=\"The Honest Assessment: What Automation Cannot Do\"\u003eThe Honest Assessment: What Automation Cannot Do\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eAutomated incident response isn\u0026rsquo;t a replacement for security expertise. It\u0026rsquo;s a force multiplier that ensures the initial detection, triage, and containment happen reliably when experts aren\u0026rsquo;t immediately available.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eAutomation cannot\u003c/strong\u003e: Determine if an authentication anomaly is a credential stuffing attack vs. legitimate user behavior (requires context human analysts provide). Decide if a failed deployment justifies rollback vs. forward-fix (depends on business risk assessment). Coordinate incident response across organizational boundaries (legal, PR, customer support). Perform root cause analysis beyond correlation (distinguishing causation from coincidence requires domain knowledge).\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eAutomation excels at\u003c/strong\u003e: Detecting predefined patterns in monitoring data at scale. Executing documented procedures consistently under pressure. Creating structured evidence trails for compliance and analysis. Reducing time-to-containment for known incident types.\u003c/p\u003e\n\u003cp\u003eThe combination—automated detection and containment plus human expertise for investigation and improvement—delivers both operational resilience and compliance evidence. Neither component alone suffices.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"conclusion-incident-response-as-engineering-not-theater\"\u003e\u003ca href=\"/posts/incident-response-github-actions/#conclusion-incident-response-as-engineering-not-theater\" title=\"Conclusion: Incident Response as Engineering, Not Theater\"\u003eConclusion: Incident Response as Engineering, Not Theater\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eISO 27001 A.16 doesn\u0026rsquo;t mandate specific tools or technologies. It mandates effective incident management with evidence of continuous improvement. Organizations can satisfy this requirement with manual processes and documentation—but manual processes fail unpredictably when incidents occur outside business hours, involve unfamiliar attack vectors, or overwhelm available staff.\u003c/p\u003e\n\u003cp\u003eGitHub Actions transforms incident response from a compliance document into an operational system. Detection happens automatically. Notifications reach on-call teams through channels they monitor. Containment actions execute without requiring production access credentials shared across teams. Audit trails generate automatically without manual documentation overhead.\u003c/p\u003e\n\u003cp\u003eThis isn\u0026rsquo;t about replacing security teams with YAML files. It\u0026rsquo;s about ensuring that when Tuesday\u0026rsquo;s authentication anomaly occurs, your organization detects it on Tuesday—not the following Monday when a customer complains. The difference between seconds-to-detection and days-to-detection determines whether an incident is a containable event or a reportable breach.\u003c/p\u003e\n\u003cp\u003eThe incident response plan that satisfies auditors but fails operationally provides neither security nor compliance. Automated incident response provides both, with Git-backed evidence that demonstrates effectiveness rather than intent.\u003c/p\u003e\n\u003cp\u003eISO 27001 requires incident response procedures that actually work when incidents actually occur. GitHub Actions delivers executable procedures with immutable audit trails. The choice between compliant documentation and compliant operations determines which incidents become learning opportunities and which become crisis escalations.\u003c/p\u003e\n","date_modified":"2026-05-26T10:22:03+02:00","date_published":"2026-02-12T17:00:00+01:00","id":"https://daily-devops.net/posts/incident-response-github-actions/","language":"en","summary":"ISO 27001 demands effective incident response. GitHub Actions transforms your dusty Word doc into automated workflows that actually work at 3 AM.\n","tags":["security","github-actions","devops","automation","bestpractices","codequality"],"title":"Your Incident Response Plan Is a Lie. Here's How to Fix It.\n","url":"https://daily-devops.net/posts/incident-response-github-actions/"},{"authors":[{"name":"Martin Stühmer","url":"https://daily-devops.net/authors/martin/"}],"content_html":"\u003cp\u003e\u0026ldquo;We might need it someday.\u0026rdquo;\u003c/p\u003e\n\u003cp\u003eThat sentence has cost teams more compliance headaches than any technical decision I\u0026rsquo;ve encountered. It\u0026rsquo;s the battle cry of lazy schema design, the excuse that turns every user table into a dumping ground for speculative data collection. Development teams hoard every conceivable piece of personal information during initial implementation—birth dates, phone numbers, employment history, marital status—creating sprawling user tables that seem prudent at the time but are actually architectural time bombs.\u003c/p\u003e\n\u003cp\u003eThree years later, when GDPR deletion requests arrive or ISO 27701 audits roll around, these same teams discover they\u0026rsquo;re storing data that serves no business purpose whatsoever. And by then, the cost isn\u0026rsquo;t just regulatory fines. It\u0026rsquo;s the architectural debt that makes compliant deletion technically impossible.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"the-regulatory-reality\"\u003e\u003ca href=\"/posts/data-minimization-entity-framework/#the-regulatory-reality\" title=\"The Regulatory Reality\"\u003eThe Regulatory Reality\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eISO 27701 and GDPR both demand something straightforward: collect only what you need, document why you need it, and delete it when asked. None of this is revolutionary. It\u0026rsquo;s basic data hygiene that somehow became optional in the \u0026ldquo;move fast and break things\u0026rdquo; era.\u003c/p\u003e\n\u003cp\u003eThe problem is that \u0026ldquo;straightforward\u0026rdquo; becomes \u0026ldquo;impossible\u0026rdquo; when your schema was designed by someone who confused data collection with data strategy. Control 7.2.2 requires identifying the specific purpose \u003cem\u003ebefore\u003c/em\u003e collection—not retroactively inventing justifications when auditors show up. Control 7.2.8 limits collection to what\u0026rsquo;s adequate and necessary for that documented purpose. And Control 7.3.1 requires the ability to actually fulfill deletion requests, which is awkward when your schema makes deletion a referential integrity nightmare.\u003c/p\u003e\n\u003cp\u003eWhen auditors examine your database schemas, they ask uncomfortable questions: Why do you store this field? What business process requires it? Can you delete it when requested?\u003c/p\u003e\n\u003cp\u003eIf your answer involves the phrase \u0026ldquo;the original developer thought,\u0026rdquo; you\u0026rsquo;ve already failed. If your answer is \u0026ldquo;we\u0026rsquo;ve always collected that,\u0026rdquo; you\u0026rsquo;ve failed harder.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"the-monolithic-user-entity-problem\"\u003e\u003ca href=\"/posts/data-minimization-entity-framework/#the-monolithic-user-entity-problem\" title=\"The Monolithic User Entity Problem\"\u003eThe Monolithic User Entity Problem\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eHere\u0026rsquo;s the pattern I see repeatedly:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eUser\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"n\"\u003eId\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"k\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003eset\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kt\"\u003estring\u003c/span\u003e \u003cspan class=\"n\"\u003eEmail\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"k\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003eset\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003edefault\u003c/span\u003e\u003cspan class=\"p\"\u003e!;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kt\"\u003estring\u003c/span\u003e \u003cspan class=\"n\"\u003ePasswordHash\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"k\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003eset\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003edefault\u003c/span\u003e\u003cspan class=\"p\"\u003e!;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e// \u0026#34;Comprehensive\u0026#34; personal information\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kt\"\u003estring\u003c/span\u003e \u003cspan class=\"n\"\u003eFirstName\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"k\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003eset\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003edefault\u003c/span\u003e\u003cspan class=\"p\"\u003e!;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kt\"\u003estring\u003c/span\u003e \u003cspan class=\"n\"\u003eLastName\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"k\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003eset\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003edefault\u003c/span\u003e\u003cspan class=\"p\"\u003e!;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"n\"\u003eDateTime\u003c/span\u003e \u003cspan class=\"n\"\u003eDateOfBirth\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"k\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003eset\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kt\"\u003estring\u003c/span\u003e \u003cspan class=\"n\"\u003ePhoneNumber\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"k\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003eset\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003edefault\u003c/span\u003e\u003cspan class=\"p\"\u003e!;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kt\"\u003estring\u003c/span\u003e \u003cspan class=\"n\"\u003eStreetAddress\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"k\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003eset\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003edefault\u003c/span\u003e\u003cspan class=\"p\"\u003e!;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kt\"\u003estring\u003c/span\u003e \u003cspan class=\"n\"\u003eCity\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"k\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003eset\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003edefault\u003c/span\u003e\u003cspan class=\"p\"\u003e!;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e// Employment data \u0026#34;just in case\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kt\"\u003estring\u003c/span\u003e \u003cspan class=\"n\"\u003eEmployerName\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"k\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003eset\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003edefault\u003c/span\u003e\u003cspan class=\"p\"\u003e!;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kt\"\u003estring\u003c/span\u003e \u003cspan class=\"n\"\u003eJobTitle\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"k\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003eset\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003edefault\u003c/span\u003e\u003cspan class=\"p\"\u003e!;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kt\"\u003edecimal\u003c/span\u003e \u003cspan class=\"n\"\u003eAnnualIncome\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"k\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003eset\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e// Demographics \u0026#34;for future analytics\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kt\"\u003estring\u003c/span\u003e \u003cspan class=\"n\"\u003eMaritalStatus\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"k\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003eset\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003edefault\u003c/span\u003e\u003cspan class=\"p\"\u003e!;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"n\"\u003eNumberOfChildren\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"k\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003eset\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"n\"\u003eDateTime\u003c/span\u003e \u003cspan class=\"n\"\u003eCreatedAt\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"k\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003eset\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"n\"\u003eICollection\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eOrder\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003eOrders\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"k\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003eset\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e[];\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\n\n\n\u003ch3 id=\"when-speculative-fields-become-compliance-deadlocks\"\u003e\u003ca href=\"/posts/data-minimization-entity-framework/#when-speculative-fields-become-compliance-deadlocks\" title=\"When Speculative Fields Become Compliance Deadlocks\"\u003eWhen Speculative Fields Become Compliance Deadlocks\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eWhy does an e-commerce system need marital status? Nobody knows anymore. The field exists because someone thought demographic analysis \u003cem\u003emight\u003c/em\u003e be valuable someday. That person left the company two years ago. The analytics feature was never built. But the data collection persists, a monument to speculative thinking that nobody had the courage to question.\u003c/p\u003e\n\u003cp\u003eThe real problem emerges when a customer requests deletion. The \u003ccode\u003eUser\u003c/code\u003e entity has foreign key relationships with \u003ccode\u003eOrders\u003c/code\u003e. Delete the user? Breaks referential integrity. Keep it? Violates the deletion request. Anonymize it? You\u0026rsquo;re still retaining fields like \u003ccode\u003eAnnualIncome\u003c/code\u003e and \u003ccode\u003eNumberOfChildren\u003c/code\u003e in backups.\u003c/p\u003e\n\u003cp\u003eYou\u0026rsquo;ve created an architectural deadlock before writing a single DELETE statement. Congratulations—your database schema is now a compliance liability that will cost more to fix than it cost to build.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"purpose-driven-data-separation\"\u003e\u003ca href=\"/posts/data-minimization-entity-framework/#purpose-driven-data-separation\" title=\"Purpose-Driven Data Separation\"\u003ePurpose-Driven Data Separation\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eThe fix isn\u0026rsquo;t complex, which makes it all the more frustrating that teams don\u0026rsquo;t implement it from the start. Separate operational data from personal data:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eUserAccount\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"n\"\u003eId\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"k\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003eset\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kt\"\u003estring\u003c/span\u003e \u003cspan class=\"n\"\u003eEmail\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"k\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003eset\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003edefault\u003c/span\u003e\u003cspan class=\"p\"\u003e!;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kt\"\u003estring\u003c/span\u003e \u003cspan class=\"n\"\u003ePasswordHash\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"k\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003eset\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003edefault\u003c/span\u003e\u003cspan class=\"p\"\u003e!;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"n\"\u003eDateTime\u003c/span\u003e \u003cspan class=\"n\"\u003eCreatedAt\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"k\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003eset\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"n\"\u003eDateTime\u003c/span\u003e\u003cspan class=\"p\"\u003e?\u003c/span\u003e \u003cspan class=\"n\"\u003eDeletedAt\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"k\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003eset\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"n\"\u003eUserProfile\u003c/span\u003e\u003cspan class=\"p\"\u003e?\u003c/span\u003e \u003cspan class=\"n\"\u003eProfile\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"k\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003eset\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"n\"\u003eICollection\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eOrder\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003eOrders\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"k\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003eset\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e[];\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eUserProfile\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"n\"\u003eId\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"k\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003eset\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"n\"\u003eUserAccountId\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"k\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003eset\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kt\"\u003estring?\u003c/span\u003e \u003cspan class=\"n\"\u003eFirstName\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"k\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003eset\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kt\"\u003estring?\u003c/span\u003e \u003cspan class=\"n\"\u003eLastName\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"k\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003eset\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"n\"\u003eDateTime\u003c/span\u003e\u003cspan class=\"p\"\u003e?\u003c/span\u003e \u003cspan class=\"n\"\u003eDateOfBirth\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"k\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003eset\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kt\"\u003estring?\u003c/span\u003e \u003cspan class=\"n\"\u003eShippingAddress\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"k\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003eset\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"n\"\u003eDateTime\u003c/span\u003e \u003cspan class=\"n\"\u003eConsentGrantedAt\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"k\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003eset\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kt\"\u003estring\u003c/span\u003e \u003cspan class=\"n\"\u003eConsentPurpose\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"k\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003eset\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003edefault\u003c/span\u003e\u003cspan class=\"p\"\u003e!;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"n\"\u003eUserAccount\u003c/span\u003e \u003cspan class=\"n\"\u003eUserAccount\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"k\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003eset\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003edefault\u003c/span\u003e\u003cspan class=\"p\"\u003e!;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNotice what changed: \u003ccode\u003eUserAccount\u003c/code\u003e contains only what\u0026rsquo;s necessary for system operation. \u003ccode\u003eUserProfile\u003c/code\u003e contains optional personal data—every field nullable, every field requiring explicit consent. No employment history. No marital status. No speculative demographics.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"wiring-the-split-into-ef-core\"\u003e\u003ca href=\"/posts/data-minimization-entity-framework/#wiring-the-split-into-ef-core\" title=\"Wiring The Split Into EF Core\"\u003eWiring The Split Into EF Core\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eThe Entity Framework configuration enforces this separation:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"k\"\u003evoid\u003c/span\u003e \u003cspan class=\"n\"\u003eConfigure\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eEntityTypeBuilder\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eUserAccount\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003ebuilder\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003ebuilder\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eHasKey\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eu\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003eu\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eId\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003ebuilder\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eProperty\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eu\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003eu\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eEmail\u003c/span\u003e\u003cspan class=\"p\"\u003e).\u003c/span\u003e\u003cspan class=\"n\"\u003eIsRequired\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003ebuilder\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eProperty\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eu\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003eu\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ePasswordHash\u003c/span\u003e\u003cspan class=\"p\"\u003e).\u003c/span\u003e\u003cspan class=\"n\"\u003eIsRequired\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003ebuilder\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eHasQueryFilter\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eu\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003eu\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eDeletedAt\u003c/span\u003e \u003cspan class=\"p\"\u003e==\u003c/span\u003e \u003cspan class=\"kc\"\u003enull\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003ebuilder\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eHasOne\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eu\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003eu\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eProfile\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eWithOne\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ep\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003ep\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eUserAccount\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eHasForeignKey\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eUserProfile\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;(\u003c/span\u003e\u003cspan class=\"n\"\u003ep\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003ep\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eUserAccountId\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eOnDelete\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eDeleteBehavior\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eCascade\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003ebuilder\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eHasMany\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eu\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003eu\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eOrders\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eWithOne\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eo\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003eo\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eUser\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eHasForeignKey\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eo\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003eo\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eUserId\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eOnDelete\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eDeleteBehavior\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eRestrict\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe \u003ccode\u003e.HasQueryFilter(u =\u0026gt; u.DeletedAt == null)\u003c/code\u003e is critical. It automatically excludes soft-deleted accounts from queries, preventing accidental exposure while preserving referential integrity for order history.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"compliant-deletion-that-actually-works\"\u003e\u003ca href=\"/posts/data-minimization-entity-framework/#compliant-deletion-that-actually-works\" title=\"Compliant Deletion That Actually Works\"\u003eCompliant Deletion That Actually Works\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eWith proper separation, deletion requests become tractable:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kd\"\u003easync\u003c/span\u003e \u003cspan class=\"n\"\u003eTask\u003c/span\u003e \u003cspan class=\"n\"\u003eProcessDeletionRequest\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"n\"\u003euserAccountId\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003eaccount\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003e_context\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eUserAccounts\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eInclude\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eu\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003eu\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eProfile\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eIgnoreQueryFilters\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eFirstOrDefaultAsync\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eu\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003eu\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eId\u003c/span\u003e \u003cspan class=\"p\"\u003e==\u003c/span\u003e \u003cspan class=\"n\"\u003euserAccountId\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eaccount\u003c/span\u003e \u003cspan class=\"k\"\u003eis\u003c/span\u003e \u003cspan class=\"kc\"\u003enull\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eaccount\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eProfile\u003c/span\u003e \u003cspan class=\"k\"\u003eis\u003c/span\u003e \u003cspan class=\"n\"\u003enot\u003c/span\u003e \u003cspan class=\"kc\"\u003enull\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003e_context\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eUserProfiles\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eRemove\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eaccount\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eProfile\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eaccount\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eDeletedAt\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eDateTime\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eUtcNow\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eaccount\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eEmail\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s\"\u003e$\u0026#34;deleted-{account.Id}@example.invalid\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eaccount\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ePasswordHash\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"kt\"\u003estring\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eEmpty\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003e_context\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eSaveChangesAsync\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe \u003ccode\u003eUserProfile\u003c/code\u003e gets hard-deleted with all personal information. The \u003ccode\u003eUserAccount\u003c/code\u003e gets soft-deleted, maintaining foreign key relationships with orders. Authentication credentials get cleared. Subsequent queries automatically exclude the account due to the query filter.\u003c/p\u003e\n\u003cp\u003eThe customer effectively ceases to exist from an operational perspective while your database maintains consistency for historical records. No architectural gymnastics. No complex anonymization logic. No explaining to auditors why you still have someone\u0026rsquo;s annual income stored.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"validation-through-integration-tests\"\u003e\u003ca href=\"/posts/data-minimization-entity-framework/#validation-through-integration-tests\" title=\"Validation Through Integration Tests\"\u003eValidation Through Integration Tests\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eCompliance isn\u0026rsquo;t a one-time configuration. It requires continuous validation. Write integration tests that verify your API endpoints don\u0026rsquo;t leak unnecessary data:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"na\"\u003e[Fact]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kd\"\u003easync\u003c/span\u003e \u003cspan class=\"n\"\u003eTask\u003c/span\u003e \u003cspan class=\"n\"\u003eGetUser_ReturnsOnlyOperationalFields\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003eclient\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003e_factory\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eCreateClient\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003eresponse\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003eclient\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eGetAsync\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;/api/users/me\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003ejson\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003eresponse\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eContent\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eReadAsStringAsync\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003euserData\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eJsonDocument\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eParse\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ejson\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eAssert\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eTrue\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003euserData\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eRootElement\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eTryGetProperty\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;id\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"k\"\u003eout\u003c/span\u003e \u003cspan class=\"n\"\u003e_\u003c/span\u003e\u003cspan class=\"p\"\u003e));\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eAssert\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eTrue\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003euserData\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eRootElement\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eTryGetProperty\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;email\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"k\"\u003eout\u003c/span\u003e \u003cspan class=\"n\"\u003e_\u003c/span\u003e\u003cspan class=\"p\"\u003e));\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eAssert\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eFalse\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003euserData\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eRootElement\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eTryGetProperty\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;dateOfBirth\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"k\"\u003eout\u003c/span\u003e \u003cspan class=\"n\"\u003e_\u003c/span\u003e\u003cspan class=\"p\"\u003e));\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eAssert\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eFalse\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003euserData\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eRootElement\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eTryGetProperty\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;passwordHash\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"k\"\u003eout\u003c/span\u003e \u003cspan class=\"n\"\u003e_\u003c/span\u003e\u003cspan class=\"p\"\u003e));\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"na\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"na\"\u003e[Fact]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kd\"\u003easync\u003c/span\u003e \u003cspan class=\"n\"\u003eTask\u003c/span\u003e \u003cspan class=\"n\"\u003eDeletedUser_IsExcludedFromQueries\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003eclient\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003e_factory\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eCreateClient\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003eclient\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eDeleteAsync\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;/api/users/me\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003eresponse\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003eclient\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eGetAsync\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;/api/users/me\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eAssert\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eEqual\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eHttpStatusCode\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eNotFound\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eresponse\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eStatusCode\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eRun these in CI. When someone inadvertently exposes additional personal data through a new endpoint, the tests fail before the code reaches production. Compliance violations don\u0026rsquo;t ship.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"failing-the-build-on-leaked-fields\"\u003e\u003ca href=\"/posts/data-minimization-entity-framework/#failing-the-build-on-leaked-fields\" title=\"Failing The Build On Leaked Fields\"\u003eFailing The Build On Leaked Fields\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eYou can take this further with a GitHub Actions workflow that runs these tests on every pull request affecting your data layer:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-yaml\" data-lang=\"yaml\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eData Minimization Compliance\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003eon\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"nt\"\u003epull_request\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003epaths\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e- \u003cspan class=\"s1\"\u003e\u0026#39;src/**/*.cs\u0026#39;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003ejobs\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"nt\"\u003ecompliance\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003eruns-on\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eubuntu-latest\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003esteps\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e- \u003cspan class=\"nt\"\u003euses\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eactions/checkout@v4\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e- \u003cspan class=\"nt\"\u003euses\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eactions/setup-dotnet@v4\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"nt\"\u003ewith\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e          \u003c/span\u003e\u003cspan class=\"nt\"\u003edotnet-version\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;9.0.x\u0026#39;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e- \u003cspan class=\"nt\"\u003erun\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003edotnet test --filter \u0026#34;Category=DataMinimization\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe tests become guardrails. Someone adds a new property to the API response? The test fails. Someone forgets to exclude a sensitive field from serialization? The test fails. The compliance requirement becomes an engineering constraint that CI enforces automatically.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"document-your-purposes\"\u003e\u003ca href=\"/posts/data-minimization-entity-framework/#document-your-purposes\" title=\"Document Your Purposes\"\u003eDocument Your Purposes\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eCode alone doesn\u0026rsquo;t satisfy audit requirements. You need documented business purposes for each field:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eUserProfile\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"cs\"\u003e/// \u0026lt;summary\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"cs\"\u003e/// Purpose: Personalized communication in order confirmations.\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"cs\"\u003e/// Legal basis: Consent granted during registration.\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"cs\"\u003e/// Retention: Until account deletion or consent withdrawal.\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"cs\"\u003e/// \u0026lt;/summary\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kt\"\u003estring?\u003c/span\u003e \u003cspan class=\"n\"\u003eFirstName\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"k\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003eset\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"cs\"\u003e/// \u0026lt;summary\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"cs\"\u003e/// Purpose: Order fulfillment and delivery.\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"cs\"\u003e/// Legal basis: Contract performance (GDPR Art. 6(1)(b)).\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"cs\"\u003e/// Retention: 90 days after order completion.\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"cs\"\u003e/// \u0026lt;/summary\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kt\"\u003estring?\u003c/span\u003e \u003cspan class=\"n\"\u003eShippingAddress\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"k\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003eset\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWhen auditors review your code, they can trace each field to its documented purpose. If you can\u0026rsquo;t articulate why a field exists, remove it. That\u0026rsquo;s the entire point.\u003c/p\u003e\n\u003cp\u003eThis documentation serves a dual purpose. First, it satisfies the audit requirement for documented purposes. Second, it forces developers to think before adding fields. When you have to write a justification in the XML docs, you\u0026rsquo;re far less likely to add \u003ccode\u003eMaritalStatus\u003c/code\u003e just because someone mentioned demographics in a planning meeting.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"collect-data-when-it-becomes-necessary\"\u003e\u003ca href=\"/posts/data-minimization-entity-framework/#collect-data-when-it-becomes-necessary\" title=\"Collect Data When It Becomes Necessary\"\u003eCollect Data When It Becomes Necessary\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eI\u0026rsquo;ve reviewed applications where shipping addresses are required during registration—before a customer has placed any orders. This is compliance theater: collecting data you can\u0026rsquo;t justify because the registration form had empty fields that felt incomplete. It violates the principle that data collection must be necessary at the time of collection, and it tells me the team never actually thought about \u003cem\u003ewhy\u003c/em\u003e they were collecting what they were collecting.\u003c/p\u003e\n\u003cp\u003eThe timing matters. Collecting a shipping address during registration for a user who might never place an order means you\u0026rsquo;re storing personal data without a current legitimate purpose. When that user requests deletion three months later without having purchased anything, you\u0026rsquo;ve been storing their address for no reason.\u003c/p\u003e\n\u003cp\u003eCollect data in context. If the shipping feature doesn\u0026rsquo;t exist yet, don\u0026rsquo;t collect addresses speculatively. When checkout happens, prompt for the address. When a feature launches, update the flow to collect newly necessary information with proper consent. Progressive data collection aligns with how users actually interact with your application—they provide information when it becomes relevant, not upfront in a registration form that asks for everything.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"the-backup-problem-nobody-talks-about\"\u003e\u003ca href=\"/posts/data-minimization-entity-framework/#the-backup-problem-nobody-talks-about\" title=\"The Backup Problem Nobody Talks About\"\u003eThe Backup Problem Nobody Talks About\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eEven with proper separation and deletion logic, there\u0026rsquo;s a compliance trap that catches most teams: database backups.\u003c/p\u003e\n\u003cp\u003eWhen you soft-delete a user account and hard-delete their profile, the data is gone from your live database. But what about last night\u0026rsquo;s backup? Last week\u0026rsquo;s? The monthly snapshot from six months ago? That profile data still exists, sitting in backup storage, violating the deletion request.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"three-ways-to-reconcile-backups-with-deletion\"\u003e\u003ca href=\"/posts/data-minimization-entity-framework/#three-ways-to-reconcile-backups-with-deletion\" title=\"Three Ways To Reconcile Backups With Deletion\"\u003eThree Ways To Reconcile Backups With Deletion\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eYour retention policy for backups needs to align with your deletion obligations. Some options:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003eEncryption with user-specific keys\u003c/strong\u003e: If you encrypt personal data with a key derived from the user\u0026rsquo;s account, deleting that key makes the backup data unreadable.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eBackup rotation aligned with retention\u003c/strong\u003e: If your stated retention period is 90 days, your backup rotation should match.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eSelective restore procedures\u003c/strong\u003e: Document that restored backups will have deletion requests re-applied before going live.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eNone of these are simple. But ignoring the backup problem doesn\u0026rsquo;t make it go away—it just means you\u0026rsquo;re lying to customers when you confirm their data has been deleted. And that\u0026rsquo;s exactly what auditors will call it: a lie backed by technical negligence.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"the-real-cost-of-data-hoarding\"\u003e\u003ca href=\"/posts/data-minimization-entity-framework/#the-real-cost-of-data-hoarding\" title=\"The Real Cost of Data Hoarding\"\u003eThe Real Cost of Data Hoarding\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eEvery unnecessary field in your database represents breach exposure, technical debt, regulatory risk, and development friction. It\u0026rsquo;s technical debt that accrues interest in the form of compliance emergencies. Teams waste hours crafting complex anonymization queries for data that shouldn\u0026rsquo;t exist. During audits, they scramble to justify fields they\u0026rsquo;ve forgotten the purpose of—inventing post-hoc rationalizations for decisions made years ago by people who didn\u0026rsquo;t consider the consequences.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"data-you-did-not-collect-cannot-leak\"\u003e\u003ca href=\"/posts/data-minimization-entity-framework/#data-you-did-not-collect-cannot-leak\" title=\"Data You Did Not Collect Cannot Leak\"\u003eData You Did Not Collect Cannot Leak\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eThe breach exposure angle deserves emphasis. When your database contains only operational essentials and purpose-justified personal data, a breach is bad but bounded. When your database contains speculative demographics, employment history, and family information, a breach becomes catastrophic. The attacker gets everything. The notification requirements expand. The regulatory scrutiny intensifies. The headlines get worse.\u003c/p\u003e\n\u003cp\u003eData you don\u0026rsquo;t collect can\u0026rsquo;t be breached. That\u0026rsquo;s the simplest security control in existence, and it\u0026rsquo;s also a compliance requirement.\u003c/p\u003e\n\u003cp\u003eThe separated architecture I\u0026rsquo;ve shown costs nothing additional to implement initially. It saves thousands in compliance remediation later. More importantly, it makes the honest answer to audit questions actually honest: \u0026ldquo;We collect what we need, we documented why, and we can delete it when asked.\u0026rdquo;\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"making-this-part-of-your-process\"\u003e\u003ca href=\"/posts/data-minimization-entity-framework/#making-this-part-of-your-process\" title=\"Making This Part of Your Process\"\u003eMaking This Part of Your Process\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eData minimization works when it\u0026rsquo;s embedded in how you build software, not bolted on during audit preparation. A few practices that help:\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eSchema reviews\u003c/strong\u003e: Treat entity model changes like code reviews. When someone adds a property, the reviewer asks: What\u0026rsquo;s the documented purpose? Is it nullable? When is it collected? How is it deleted?\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eArchitecture decision records\u003c/strong\u003e: Document why you chose to collect specific data. When someone asks in two years why \u003ccode\u003eDateOfBirth\u003c/code\u003e exists, the ADR explains it\u0026rsquo;s for age verification on restricted products—not because someone thought demographics might be interesting.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eDeletion dry runs\u003c/strong\u003e: Periodically test your deletion logic against production-like data. Does it complete without errors? Does the query filter exclude deleted accounts? Can you still query order history for a deleted user\u0026rsquo;s past purchases?\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003ePeriodic field audits\u003c/strong\u003e: Once a quarter, export your entity models and review each property. Is it still used? Does the original purpose still apply? Has the feature it supported been deprecated? Fields that no longer serve a purpose should be removed, not retained indefinitely.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"the-bottom-line\"\u003e\u003ca href=\"/posts/data-minimization-entity-framework/#the-bottom-line\" title=\"The Bottom Line\"\u003eThe Bottom Line\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eStop hoarding personal data \u0026ldquo;just in case.\u0026rdquo; Define purposes. Collect minimally. Delete ruthlessly. Your deletion logic will be straightforward, your audit responses will be honest, and your customers\u0026rsquo; privacy will be respected.\u003c/p\u003e\n\u003cp\u003eThe monolithic \u003ccode\u003eUser\u003c/code\u003e entity pattern isn\u0026rsquo;t just non-compliant—it\u0026rsquo;s a symptom of teams that never asked \u0026ldquo;should we?\u0026rdquo; before asking \u0026ldquo;can we?\u0026rdquo; It\u0026rsquo;s expensive, risky, and harder to maintain than the separated alternative. Purpose-driven data architecture with \u003ccode\u003eUserAccount\u003c/code\u003e and \u003ccode\u003eUserProfile\u003c/code\u003e entities, nullable personal data fields, query filters for soft deletes, and integration tests for API boundaries isn\u0026rsquo;t regulatory overhead. It\u0026rsquo;s how data management should have worked all along.\u003c/p\u003e\n\u003cp\u003eThat\u0026rsquo;s not a constraint that makes development harder. It\u0026rsquo;s the bare minimum of responsible engineering that somehow became optional. Fix your schemas before the auditors do it for you.\u003c/p\u003e\n","date_modified":"2026-05-26T10:22:03+02:00","date_published":"2026-02-10T17:00:00+01:00","id":"https://daily-devops.net/posts/data-minimization-entity-framework/","language":"en","summary":"Monolithic user entities make GDPR deletion impossible. Separate operational from personal data in EF Core with nullable, purpose-documented fields.\n","tags":["privacy","dotnet","testing","architecture","bestpractices","codequality"],"title":"Stop Hoarding Personal Data in Entity Framework\n","url":"https://daily-devops.net/posts/data-minimization-entity-framework/"},{"authors":[{"name":"Martin Stühmer","url":"https://daily-devops.net/authors/martin/"}],"content_html":"\u003cp\u003eIn \u003ca href=\"../code-sharpens-thinking/\"\u003ePart 1\u003c/a\u003e, we established that \u0026ldquo;vibe coding\u0026rdquo;—describing what you want and shipping what AI generates—creates productivity illusions that collapse spectacularly under production load. \u003ca href=\"../feedback-loop-ai-cant-replace/\"\u003ePart 2\u003c/a\u003e explored the feedback loop that AI can\u0026rsquo;t replicate.\u003c/p\u003e\n\u003cp\u003eNow we confront the practical question: \u003cstrong\u003eWhat skills define real professionals when typing code becomes trivial?\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eAI code assistants accelerate the mechanical part extraordinarily well. GitHub Copilot autocompletes functions. ChatGPT generates entire APIs from prompts. The typing is handled.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eYet you remain indispensable.\u003c/strong\u003e Not in spite of AI\u0026rsquo;s code generation capabilities, but \u003cstrong\u003ebecause of them\u003c/strong\u003e.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eWhy?\u003c/strong\u003e When code generation becomes commoditized, the differentiator isn\u0026rsquo;t typing speed. It\u0026rsquo;s accumulated experience. Watching systems fail in production. Understanding \u003cstrong\u003ewhy\u003c/strong\u003e they failed. Applying that hard-won knowledge to prevent the next failure.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHere\u0026rsquo;s the uncomfortable truth:\u003c/strong\u003e Organizations that confuse \u0026ldquo;lines of code generated\u0026rdquo; with \u0026ldquo;productivity\u0026rdquo; discover the difference when production incidents spike—and the bill arrives.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"why-prompt-engineering-isnt-architecture\"\u003e\u003ca href=\"/posts/real-professional-software-engineering-ai-era/#why-prompt-engineering-isnt-architecture\" title=\"Why Prompt Engineering Isn\u0026rsquo;t Architecture\"\u003eWhy Prompt Engineering Isn\u0026rsquo;t Architecture\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003eAI code generation creates a seductive trap.\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eYou think:\u003c/strong\u003e If I can describe what I want in natural language and get working code, isn\u0026rsquo;t that sufficient? Why spend time understanding implementation details when AI handles them?\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHere\u0026rsquo;s why that\u0026rsquo;s wrong:\u003c/strong\u003e Prompts describe intent. Not constraints.\u003c/p\u003e\n\u003cp\u003eAnd software engineering? It\u0026rsquo;s fundamentally about managing \u003cstrong\u003econstraints\u003c/strong\u003e. Performance budgets. Memory limits. Concurrency safety. Error handling. Maintainability. Operational cost. Security boundaries.\u003c/p\u003e\n\u003cp\u003eConsider asking an AI to \u0026ldquo;implement caching for customer data.\u0026rdquo; You\u0026rsquo;ll get code that caches. But you \u003cstrong\u003ewon\u0026rsquo;t\u003c/strong\u003e get answers to:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eWhat\u0026rsquo;s the \u003cstrong\u003ememory budget\u003c/strong\u003e? When does caching become more expensive than repeated database calls?\u003c/li\u003e\n\u003cli\u003eHow do you handle \u003cstrong\u003ecache invalidation\u003c/strong\u003e across multiple application instances?\u003c/li\u003e\n\u003cli\u003eWhat\u0026rsquo;s the \u003cstrong\u003econsistency model\u003c/strong\u003e? Can stale data cause correctness issues downstream?\u003c/li\u003e\n\u003cli\u003eHow do you \u003cstrong\u003emonitor\u003c/strong\u003e cache hit rates to verify it\u0026rsquo;s actually improving performance?\u003c/li\u003e\n\u003cli\u003eWhat happens during \u003cstrong\u003ecache warming\u003c/strong\u003e? Do users experience degraded performance on cold starts?\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e\u003cstrong\u003eAI generates code that addresses the prompt. Professionals understand these questions emerge from production experience\u003c/strong\u003e—from watching systems fail, from debugging race conditions at 3 AM, from analyzing cost reports that show caching is more expensive than the problem it solved, from responding to incidents where stale cache data caused customer-visible bugs.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003ePrompt engineering optimizes for generating code quickly. Software architecture optimizes for systems that survive production reality. These are orthogonal skills.\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eI\u0026rsquo;ve seen teams adopt AI-heavy workflows where \u003cstrong\u003ejunior developers generate features rapidly using prompts\u003c/strong\u003e, and \u003cstrong\u003esenior developers spend weeks later refactoring the accumulated technical debt\u003c/strong\u003e. The AI-generated code worked in isolation. It failed as a system because no one understood how the pieces interacted, what assumptions each component made, or where performance would degrade under load.\u003c/p\u003e\n\u003cp\u003eThe skill that AI can\u0026rsquo;t replace: \u003cstrong\u003erecognizing which questions to ask before writing code\u003c/strong\u003e, not generating syntax after questions are answered. That recognition comes from the feedback loop—you write code, watch it fail, understand \u003cstrong\u003ewhy\u003c/strong\u003e it failed, and internalize the lesson.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003ePrompt-driven development skips this loop entirely, outsourcing both the implementation and the learning.\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eReal professionals don\u0026rsquo;t reject AI tools. They use them to accelerate the mechanical parts while maintaining ownership of the architectural decisions, performance analysis, and failure mode understanding that prompts can\u0026rsquo;t capture.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"technical-debt-where-abstract-design-becomes-concrete-burden\"\u003e\u003ca href=\"/posts/real-professional-software-engineering-ai-era/#technical-debt-where-abstract-design-becomes-concrete-burden\" title=\"Technical Debt: Where Abstract Design Becomes Concrete Burden\"\u003eTechnical Debt: Where Abstract Design Becomes Concrete Burden\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eTechnical debt is abstract thinking\u0026rsquo;s deferred consequences manifesting as maintenance burden. Design decisions that felt reasonable in isolation accumulate into complexity that resists change, harbors bugs, and drains productivity.\u003c/p\u003e\n\u003cp\u003eEvery architecture discussion includes statements like \u0026ldquo;we\u0026rsquo;ll refactor later\u0026rdquo; or \u0026ldquo;this is temporary\u0026rdquo; or \u0026ldquo;once we prove the concept, we\u0026rsquo;ll clean it up.\u0026rdquo; These are thought patterns that treat code as temporary scaffolding rather than operational reality. Code doesn\u0026rsquo;t stay temporary—it becomes production reality that teams maintain for years.\u003c/p\u003e\n\u003cp\u003eI\u0026rsquo;ve inherited codebases where \u0026ldquo;temporary\u0026rdquo; solutions from 2015 still run in production, calcified by dependencies and surrounded by defensive code that works around their limitations. The abstract thinking that justified shortcuts—\u0026ldquo;we\u0026rsquo;re moving fast,\u0026rdquo; \u0026ldquo;we\u0026rsquo;ll fix it in v2\u0026rdquo;—never accounted for the operational reality: v2 got deprioritized, teams changed, knowledge evaporated, and the technical debt persisted.\u003c/p\u003e\n\u003cp\u003eMicrosoft\u0026rsquo;s own guidance on technical debt management emphasizes measurement and prioritization based on impact—not on abstract severity, but on actual operational burden:\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e\u0026ldquo;Prioritize technical debt items based on their effects on workload functionality. Focus on addressing the issues that have the most significant effect on the performance, maintainability, and scalability of the workload.\u0026rdquo;\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003eThis requires executable code that can be measured, profiled, and analyzed. Abstract architectural concerns translate into concrete technical debt only when code exists to evaluate. You can\u0026rsquo;t measure maintainability, performance impact, or operational cost without code that runs in production-like conditions.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eAI-accelerated development amplifies this pattern.\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eWhen junior developers generate features using prompts, the code works immediately but accumulates technical debt invisibly. The AI optimized for \u0026ldquo;works now,\u0026rdquo; not \u0026ldquo;maintainable long-term.\u0026rdquo;\u003c/p\u003e\n\u003cp\u003eSix months later, when requirements change? \u003cstrong\u003eThe bill comes due.\u003c/strong\u003e What took 2 days to generate takes 2 weeks to refactor. Why? Because no one understands the generated foundations.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eReal cost:\u003c/strong\u003e Senior developers spending 40+ hours untangling AI-generated code instead of building new features. That\u0026rsquo;s €4,000-8,000 in lost productivity—per feature.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"real-professionals-in-the-ai-era-mastering-the-feedback-loop\"\u003e\u003ca href=\"/posts/real-professional-software-engineering-ai-era/#real-professionals-in-the-ai-era-mastering-the-feedback-loop\" title=\"Real Professionals in the AI Era: Mastering the Feedback Loop\"\u003eReal Professionals in the AI Era: Mastering the Feedback Loop\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003eDavid\u0026rsquo;s comment about real professionals not being replaced wasn\u0026rsquo;t wishful thinking or gatekeeping.\u003c/strong\u003e It was recognition that professional software engineering has \u003cstrong\u003enever\u003c/strong\u003e been about typing code—and in an era where typing is automated, that distinction becomes \u003cstrong\u003ebrutally\u003c/strong\u003e clear.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eThe skills that define professionals in 2026 and beyond:\u003c/strong\u003e\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"understanding-execution-characteristics\"\u003e\u003ca href=\"/posts/real-professional-software-engineering-ai-era/#understanding-execution-characteristics\" title=\"Understanding Execution Characteristics\"\u003eUnderstanding Execution Characteristics\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eWhen AI generates code, professionals can read it and immediately recognize:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eAllocation patterns that will cause garbage collection pressure\u003c/li\u003e\n\u003cli\u003eDatabase access patterns that create N+1 problems\u003c/li\u003e\n\u003cli\u003eSynchronization primitives that risk deadlocks\u003c/li\u003e\n\u003cli\u003eAPI contracts that will break under versioning\u003c/li\u003e\n\u003cli\u003eAbstractions that trade clarity for cleverness\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eThis isn\u0026rsquo;t about memorizing syntax. It\u0026rsquo;s about pattern recognition from seeing thousands of implementations and their production consequences. AI can generate the code. Professionals can predict where it fails before deployment.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"asking-questions-ai-cant-formulate\"\u003e\u003ca href=\"/posts/real-professional-software-engineering-ai-era/#asking-questions-ai-cant-formulate\" title=\"Asking Questions AI Can\u0026rsquo;t Formulate\"\u003eAsking Questions AI Can\u0026rsquo;t Formulate\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eAI optimizes for the prompt it receives. Professionals know which questions to ask before prompting:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eWhat\u0026rsquo;s the failure mode if this service is unavailable?\u003c/li\u003e\n\u003cli\u003eHow does this perform when the dataset grows 100x?\u003c/li\u003e\n\u003cli\u003eWhat happens during partial failures across service boundaries?\u003c/li\u003e\n\u003cli\u003eHow do we roll this back if production deployment reveals problems?\u003c/li\u003e\n\u003cli\u003eWhat operational metrics signal that this implementation is degrading?\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eThese questions emerge from production scars, not documentation. They represent thinking that can\u0026rsquo;t be prompted because the prompt itself requires experience to formulate.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"recognizing-when-ai-solutions-are-wrong\"\u003e\u003ca href=\"/posts/real-professional-software-engineering-ai-era/#recognizing-when-ai-solutions-are-wrong\" title=\"Recognizing When AI Solutions Are Wrong\"\u003eRecognizing When AI Solutions Are Wrong\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eAI generates plausible code. Professionals recognize when plausible diverges from correct:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eThe generated caching looks reasonable but introduces race conditions\u003c/li\u003e\n\u003cli\u003eThe suggested refactoring breaks semantic guarantees the original code maintained\u003c/li\u003e\n\u003cli\u003eThe performance optimization trades correctness for speed\u003c/li\u003e\n\u003cli\u003eThe error handling silences failures that should propagate\u003c/li\u003e\n\u003cli\u003eThe abstraction solves the described problem but makes the actual problem harder\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eThis skill—recognizing subtle wrongness—requires understanding not just what code does, but what it should do in context. AI has no context beyond the prompt. Professionals carry context from the entire system, the organization\u0026rsquo;s constraints, and production failure history.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"debugging-when-ai-generated-code-fails\"\u003e\u003ca href=\"/posts/real-professional-software-engineering-ai-era/#debugging-when-ai-generated-code-fails\" title=\"Debugging When AI-Generated Code Fails\"\u003eDebugging When AI-Generated Code Fails\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eAI can\u0026rsquo;t debug its own output effectively because it has no execution model. It can suggest changes based on error messages, but it can\u0026rsquo;t reason about:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eWhy the garbage collector is thrashing\u003c/li\u003e\n\u003cli\u003eWhere the memory leak originates across object graphs\u003c/li\u003e\n\u003cli\u003eWhy this specific race condition appears under production load but not in testing\u003c/li\u003e\n\u003cli\u003eHow this performance degradation emerged from the interaction of six separate components\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eProfessionals debug by understanding execution: what the CPU is doing, how memory is managed, where I/O blocking occurs, how the runtime schedules work. This understanding comes from the feedback loop—watching code execute, measuring behavior, correlating symptoms with causes.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"maintaining-code-ai-generated-yesterday\"\u003e\u003ca href=\"/posts/real-professional-software-engineering-ai-era/#maintaining-code-ai-generated-yesterday\" title=\"Maintaining Code AI Generated Yesterday\"\u003eMaintaining Code AI Generated Yesterday\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eThe code AI generates today becomes the maintenance burden of tomorrow. Professionals understand that maintainability isn\u0026rsquo;t syntax elegance—it\u0026rsquo;s whether future developers (including AI-assisted ones) can understand intent, modify behavior safely, and reason about consequences.\u003c/p\u003e\n\u003cp\u003eAI-generated code often optimizes for immediate functionality over long-term maintainability because prompts rarely include \u0026ldquo;make this easy to modify in six months when requirements change.\u0026rdquo; Professionals review AI output through the lens of future maintenance: Does this abstraction clarify or obscure? Will this pattern scale when similar features are added? Can someone unfamiliar with this code understand its failure modes?\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"the-economic-reality-of-ai-accelerated-development\"\u003e\u003ca href=\"/posts/real-professional-software-engineering-ai-era/#the-economic-reality-of-ai-accelerated-development\" title=\"The Economic Reality of AI-Accelerated Development\"\u003eThe Economic Reality of AI-Accelerated Development\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eAI tools make junior developers dramatically more productive at generating code. Sounds like pure upside, right?\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eUntil you measure the total lifecycle cost:\u003c/strong\u003e\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eFeatures ship faster \u003cstrong\u003ebut\u003c/strong\u003e accumulate technical debt faster\u003c/li\u003e\n\u003cli\u003eCode coverage is high \u003cstrong\u003ebut\u003c/strong\u003e defect rates increase by 25-40%\u003c/li\u003e\n\u003cli\u003eDevelopment velocity looks impressive \u003cstrong\u003euntil\u003c/strong\u003e production incidents spike\u003c/li\u003e\n\u003cli\u003eRefactoring becomes more expensive because no one understands the AI-generated foundations\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e\u003cstrong\u003eTwo types of organizations:\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eType 1 measures productivity by lines of code generated or features shipped per sprint. They see AI as a massive win.\u003c/p\u003e\n\u003cp\u003eType 2 measures productivity by system reliability, operational cost, and maintenance burden. They see a more complex picture—and higher total cost.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eYour value proposition shifts:\u003c/strong\u003e From \u0026ldquo;can write code\u0026rdquo; to \u0026ldquo;can ensure AI-generated code survives production.\u0026rdquo;\u003c/p\u003e\n\u003cp\u003eThat\u0026rsquo;s not a diminished role. \u003cstrong\u003eIt\u0026rsquo;s a more critical one.\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eThe ability to generate code becomes commoditized. The ability to evaluate, refine, and maintain that code? \u003cstrong\u003eThat becomes your differentiator.\u003c/strong\u003e\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"why-the-feedback-loop-cant-be-automated\"\u003e\u003ca href=\"/posts/real-professional-software-engineering-ai-era/#why-the-feedback-loop-cant-be-automated\" title=\"Why the Feedback Loop Can\u0026rsquo;t Be Automated\"\u003eWhy the Feedback Loop Can\u0026rsquo;t Be Automated\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eAI can participate in parts of the feedback loop:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eIt can suggest implementations based on requirements\u003c/li\u003e\n\u003cli\u003eIt can generate tests based on code\u003c/li\u003e\n\u003cli\u003eIt can propose refactorings based on patterns\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eBut it can\u0026rsquo;t close the loop because closing the loop requires:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e\u003cstrong\u003eExecution in realistic conditions\u003c/strong\u003e: Production load, real data volumes, actual failure scenarios\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eMeasurement of consequences\u003c/strong\u003e: Performance under stress, cost implications, operational burden\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eInterpretation of results\u003c/strong\u003e: Understanding why this metric degraded, why this pattern emerged, why this assumption failed\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eRefinement of thinking\u003c/strong\u003e: Updating mental models about what works, what fails, and why\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eApplication to future decisions\u003c/strong\u003e: Recognizing similar patterns in new contexts and avoiding repeated mistakes\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eAI can help with steps 1 and 2. Steps 3, 4, and 5 require human judgment informed by accumulated experience. This is the feedback loop David referenced—the mechanism that sharpens thinking through repeated collision with executable reality.\u003c/p\u003e\n\u003cp\u003eReal professionals master this loop. They write code (or review AI-generated code), watch it execute, measure its behavior, understand its failure modes, and refine their thinking. Each iteration strengthens their ability to recognize what will work before writing it, what will fail before deploying it, and what will cost more than it\u0026rsquo;s worth before building it.\u003c/p\u003e\n\u003cp\u003eThis skill can\u0026rsquo;t be replaced because it\u0026rsquo;s not about having the right answer immediately—it\u0026rsquo;s about knowing how to find the right answer through disciplined iteration between abstract thinking and concrete execution.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"conclusion-code-demands-honest-thinking\"\u003e\u003ca href=\"/posts/real-professional-software-engineering-ai-era/#conclusion-code-demands-honest-thinking\" title=\"Conclusion: Code Demands Honest Thinking\"\u003eConclusion: Code Demands Honest Thinking\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eYes, thinking is hard. Reasoning through constraints, evaluating trade-offs, understanding system dynamics—these require deep intellectual work. \u003cstrong\u003eI\u0026rsquo;ve never disputed this.\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eBut \u003cstrong\u003ehere\u0026rsquo;s what the \u0026ldquo;thinking is everything\u0026rdquo; narrative misses:\u003c/strong\u003e code is not just the mechanical output of that thinking. Code is the form that \u003cstrong\u003eforces thinking into honesty\u003c/strong\u003e. It\u0026rsquo;s where vague reasoning gets \u003cstrong\u003ebrutally exposed\u003c/strong\u003e, deferred decisions become \u003cstrong\u003eunavoidable\u003c/strong\u003e, and abstract consequences materialize as \u003cstrong\u003eoperational reality that costs real money and wakes you up at 3 AM\u003c/strong\u003e.\u003c/p\u003e\n\u003cp\u003eTreating code as \u0026ldquo;just another language\u0026rdquo; undersells what programming actually does: it transforms thought from abstract possibility into \u003cstrong\u003eexecutable certainty\u003c/strong\u003e. It makes performance \u003cstrong\u003emeasurable\u003c/strong\u003e, correctness \u003cstrong\u003etestable\u003c/strong\u003e, and complexity \u003cstrong\u003evisible\u003c/strong\u003e. It forces precision where thought allows \u003cstrong\u003ecomfortable ambiguity\u003c/strong\u003e.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eSoftware engineering isn\u0026rsquo;t thinking OR programming. It\u0026rsquo;s thinking made rigorous through programming.\u003c/strong\u003e It\u0026rsquo;s the tight feedback loop where abstract reasoning and executable verification sharpen each other iteratively.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eOne without the other doesn\u0026rsquo;t scale:\u003c/strong\u003e\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eThinking without executable form stays untested and \u003cstrong\u003eoften wrong\u003c/strong\u003e\u003c/li\u003e\n\u003cli\u003eCode without thoughtful design becomes \u003cstrong\u003eunmaintainable complexity\u003c/strong\u003e\u003c/li\u003e\n\u003cli\u003eAI-generated code without understanding becomes \u003cstrong\u003etechnical debt that compounds with every sprint\u003c/strong\u003e\u003c/li\u003e\n\u003cli\u003ePrompt engineering without production experience becomes \u003cstrong\u003ea liability dressed as productivity\u003c/strong\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eEngineering quality emerges from the discipline of moving between abstract reasoning and concrete implementation—\u003cstrong\u003erepeatedly, rigorously, honestly\u003c/strong\u003e.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eThat\u0026rsquo;s what makes software engineering difficult.\u003c/strong\u003e Not the typing. Not even just the thinking. But the intellectual discipline of forcing thought into executable form that \u003cstrong\u003esurvives contact with production reality\u003c/strong\u003e.\u003c/p\u003e\n\u003cp\u003eAI can type code faster than you. It can suggest implementations, generate tests, propose refactorings. \u003cstrong\u003eWhat it can\u0026rsquo;t do is learn from watching systems fail in production, understand why they failed, and apply that hard-won knowledge to prevent the next failure.\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eAnd that discipline, the feedback loop David referenced, cannot be replaced.\u003c/strong\u003e\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"series-summary\"\u003e\u003ca href=\"/posts/real-professional-software-engineering-ai-era/#series-summary\" title=\"Series Summary\"\u003eSeries Summary\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003ePart 1: \u003ca href=\"../code-sharpens-thinking/\"\u003eWhy Real Professionals Will Never Be Replaced by AI\u003c/a\u003e\u003c/strong\u003e\u003cbr\u003e\nEstablished that AI-generated code without understanding creates productivity illusions. Vibe coding collapses when code generation becomes trivial and understanding execution, failure modes, and operational cost becomes everything.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003ePart 2: \u003ca href=\"../feedback-loop-ai-cant-replace/\"\u003eThe Feedback Loop That AI Can\u0026rsquo;t Replace\u003c/a\u003e\u003c/strong\u003e\u003cbr\u003e\nExamined the mechanisms that transform abstract thinking into operational understanding: compilers validate logic, tests expose behavioral gaps, profilers measure performance reality, production reveals deferred decisions.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003ePart 3: Real Professional Software Engineering in the AI Era\u003c/strong\u003e (this article)\u003cbr\u003e\nExplored the irreplaceable professional skillset: recognizing execution characteristics, asking questions AI can\u0026rsquo;t formulate, debugging failures AI can\u0026rsquo;t reason about, maintaining code AI generated yesterday, and understanding the economic reality where \u0026ldquo;AI productivity\u0026rdquo; often means faster technical debt accumulation.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eThe throughline:\u003c/strong\u003e Real professionals will never be replaced because they\u0026rsquo;ve mastered the feedback loop: the iterative discipline of writing code, watching it fail, understanding why, and refining thinking. AI participates in parts of this loop but can\u0026rsquo;t close it. That\u0026rsquo;s where professionals remain indispensable.\u003c/p\u003e\n","date_modified":"2026-05-25T22:06:34+02:00","date_published":"2026-01-20T17:00:00+01:00","id":"https://daily-devops.net/posts/real-professional-software-engineering-ai-era/","language":"en","summary":"AI generates code instantly. Professionals spot when it is subtly wrong, debug failures AI cannot reason about, and see through the productivity narrative.\n","tags":["softwareengineering","codequality","bestpractices","architecture","dotnet","csharp","technicaldebt","ai-code-assistant","github-copilot"],"title":"Real Professional Software Engineering in the AI Era\n","url":"https://daily-devops.net/posts/real-professional-software-engineering-ai-era/"},{"authors":[{"name":"Martin Stühmer","url":"https://daily-devops.net/authors/martin/"}],"content_html":"\u003cp\u003eIn \u003ca href=\"../code-sharpens-thinking/\"\u003ePart 1 of this series\u003c/a\u003e, we explored why AI code generation creates an illusion of productivity that collapses when \u0026ldquo;vibe coding\u0026rdquo; meets production reality.\u003c/p\u003e\n\u003cp\u003eTyping code is now trivial. AI handles it faster than humans can type.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eBut here\u0026rsquo;s the critical skill:\u003c/strong\u003e Understanding what that code costs. Where it fails. Why it breaks under load.\u003c/p\u003e\n\u003cp\u003eThe differentiator between professionals and prompt engineers? \u003cstrong\u003eThe feedback loop.\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eYou write code (or review AI-generated code). Watch it execute. Measure its behavior. Understand its failure modes. Refine your thinking. Each iteration sharpens your ability to recognize what will work before implementing it, what will fail before deploying it, and what will cost more than it\u0026rsquo;s worth before building it.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eSo what exactly is this feedback loop? And why can\u0026rsquo;t AI replicate it?\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eThis article examines the mechanisms that transform abstract thinking into operational understanding:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003eCompilers\u003c/strong\u003e that validate logical consistency and force completeness\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003ePerformance profilers\u003c/strong\u003e that expose what abstract analysis defers\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eTesting frameworks\u003c/strong\u003e that reveal behavioral gaps\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eProduction environments\u003c/strong\u003e that materialize every deferred decision\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eThese aren\u0026rsquo;t just development tools—they\u0026rsquo;re thinking validators that expose where reasoning was incomplete. AI can participate in parts of this loop, but it can\u0026rsquo;t close it. Understanding why reveals why real professionals remain irreplaceable.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"the-compiler-as-thought-validator\"\u003e\u003ca href=\"/posts/feedback-loop-ai-cant-replace/#the-compiler-as-thought-validator\" title=\"The Compiler as Thought Validator\"\u003eThe Compiler as Thought Validator\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eModern compilers do more than translate syntax—they validate logical consistency. Static analysis, type checking, nullability analysis, and pattern exhaustiveness checks all function as automated reasoning validators. They catch the gaps that pure thought leaves unresolved.\u003c/p\u003e\n\u003cp\u003eConsider exhaustive pattern matching introduced in C# 8:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kd\"\u003eenum\u003c/span\u003e \u003cspan class=\"n\"\u003eOrderStatus\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"n\"\u003ePending\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eConfirmed\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eShipped\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eDelivered\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eCancelled\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kt\"\u003estring\u003c/span\u003e \u003cspan class=\"n\"\u003eGetStatusMessage\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eOrderStatus\u003c/span\u003e \u003cspan class=\"n\"\u003estatus\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003estatus\u003c/span\u003e \u003cspan class=\"k\"\u003eswitch\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003eOrderStatus\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ePending\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;Order is pending\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003eOrderStatus\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eConfirmed\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;Order confirmed\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003eOrderStatus\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eShipped\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;Order shipped\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"c1\"\u003e// Compiler error CS8509: The switch expression does not handle all possible values\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e};\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe compiler refuses to accept incomplete reasoning. In abstract discussion, you might focus on the \u0026ldquo;normal\u0026rdquo; states and unconsciously ignore edge cases. The compiler forces completeness.\u003c/p\u003e\n\u003cp\u003eOr consider cyclomatic complexity analysis built into Visual Studio and available through analyzers. High complexity scores (typically above 10) indicate control flow that\u0026rsquo;s difficult to reason about and test thoroughly. The code analyzer doesn\u0026rsquo;t just flag style violations—it measures cognitive load and highlights where thinking has likely become too tangled to maintain reliably.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// Complexity: 15 (Warning CS1591)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kt\"\u003edecimal\u003c/span\u003e \u003cspan class=\"n\"\u003eCalculateDiscount\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eOrder\u003c/span\u003e \u003cspan class=\"n\"\u003eorder\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eCustomer\u003c/span\u003e \u003cspan class=\"n\"\u003ecustomer\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eDateTime\u003c/span\u003e \u003cspan class=\"n\"\u003eorderDate\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ecustomer\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eIsPremium\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eorder\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eTotal\u003c/span\u003e \u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"m\"\u003e1000\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eorderDate\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eMonth\u003c/span\u003e \u003cspan class=\"p\"\u003e==\u003c/span\u003e \u003cspan class=\"m\"\u003e12\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"m\"\u003e0.25\u003c/span\u003e\u003cspan class=\"n\"\u003em\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"k\"\u003eelse\u003c/span\u003e \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ecustomer\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eYearsActive\u003c/span\u003e \u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"m\"\u003e5\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"m\"\u003e0.20\u003c/span\u003e\u003cspan class=\"n\"\u003em\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"k\"\u003eelse\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"m\"\u003e0.15\u003c/span\u003e\u003cspan class=\"n\"\u003em\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003eelse\u003c/span\u003e \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eorder\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eTotal\u003c/span\u003e \u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"m\"\u003e500\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"m\"\u003e0.10\u003c/span\u003e\u003cspan class=\"n\"\u003em\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003eelse\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"m\"\u003e0.05\u003c/span\u003e\u003cspan class=\"n\"\u003em\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eelse\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eorder\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eTotal\u003c/span\u003e \u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"m\"\u003e1000\u003c/span\u003e \u003cspan class=\"p\"\u003e\u0026amp;\u0026amp;\u003c/span\u003e \u003cspan class=\"n\"\u003eorderDate\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eMonth\u003c/span\u003e \u003cspan class=\"p\"\u003e==\u003c/span\u003e \u003cspan class=\"m\"\u003e12\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"m\"\u003e0.15\u003c/span\u003e\u003cspan class=\"n\"\u003em\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003eelse\u003c/span\u003e \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eorder\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eTotal\u003c/span\u003e \u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"m\"\u003e500\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"m\"\u003e0.05\u003c/span\u003e\u003cspan class=\"n\"\u003em\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"m\"\u003e0\u003c/span\u003e\u003cspan class=\"n\"\u003em\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis method might make sense in abstract discussion: \u0026ldquo;We give discounts based on customer status, order size, and date.\u0026rdquo; But complexity analysis reveals what abstract thinking hides—the decision tree is convoluted, error-prone, and unmaintainable.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eLook closer at the logic:\u003c/strong\u003e Business rules state that long-term premium customers (6+ years) should get the highest discount (30%) for high-value orders—even better than the December holiday bonus. But a premium customer with 7 years active ordering €1,500 in December only gets 25%—the \u003ccode\u003eelse if (customer.YearsActive \u0026gt; 5)\u003c/code\u003e branch returning 0.20m is \u003cstrong\u003eunreachable\u003c/strong\u003e because the December check already returned. \u003cstrong\u003eThe nested if-structure makes the bug invisible in code review but obvious when a test fails:\u003c/strong\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"na\"\u003e[Fact]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"k\"\u003evoid\u003c/span\u003e \u003cspan class=\"n\"\u003eCalculateDiscount_LoyalPremiumCustomer_December_ShouldGetLoyaltyBonus\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e// Long-term customers should get loyalty discount even in December\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003ecustomer\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003enew\u003c/span\u003e \u003cspan class=\"n\"\u003eCustomer\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"n\"\u003eIsPremium\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eYearsActive\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"m\"\u003e7\u003c/span\u003e \u003cspan class=\"p\"\u003e};\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003eorder\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003enew\u003c/span\u003e \u003cspan class=\"n\"\u003eOrder\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"n\"\u003eTotal\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"m\"\u003e1500\u003c/span\u003e \u003cspan class=\"p\"\u003e};\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003edate\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003enew\u003c/span\u003e \u003cspan class=\"n\"\u003eDateTime\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"m\"\u003e2025\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"m\"\u003e12\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"m\"\u003e15\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003ediscount\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003e_calculator\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eCalculateDiscount\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eorder\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003ecustomer\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003edate\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eAssert\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eEqual\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"m\"\u003e0.30\u003c/span\u003e\u003cspan class=\"n\"\u003em\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003ediscount\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e \u003cspan class=\"c1\"\u003e// FAILS: Returns 0.25m instead\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                                    \u003cspan class=\"c1\"\u003e// The YearsActive\u0026gt;5 branch is unreachable!\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe code forces you to confront what clean thinking would have structured differently:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// Complexity: 4\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kt\"\u003edecimal\u003c/span\u003e \u003cspan class=\"n\"\u003eCalculateDiscount\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eOrder\u003c/span\u003e \u003cspan class=\"n\"\u003eorder\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eCustomer\u003c/span\u003e \u003cspan class=\"n\"\u003ecustomer\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eDateTime\u003c/span\u003e \u003cspan class=\"n\"\u003eorderDate\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003erules\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003enew\u003c/span\u003e \u003cspan class=\"n\"\u003eDiscountRuleEngine\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAddRule\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"k\"\u003enew\u003c/span\u003e \u003cspan class=\"n\"\u003ePremiumCustomerRule\u003c/span\u003e\u003cspan class=\"p\"\u003e())\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAddRule\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"k\"\u003enew\u003c/span\u003e \u003cspan class=\"n\"\u003eHighValueOrderRule\u003c/span\u003e\u003cspan class=\"p\"\u003e())\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAddRule\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"k\"\u003enew\u003c/span\u003e \u003cspan class=\"n\"\u003eHolidayPromotionRule\u003c/span\u003e\u003cspan class=\"p\"\u003e());\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003erules\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eCalculateDiscount\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eorder\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003ecustomer\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eorderDate\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eRefactoring didn\u0026rsquo;t just clean up syntax—it exposed and resolved structural thinking problems that abstract reasoning missed. The rule engine evaluates all rules and picks the highest discount, making the business logic explicit and the bug impossible.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"performance-where-theory-meets-production-reality\"\u003e\u003ca href=\"/posts/feedback-loop-ai-cant-replace/#performance-where-theory-meets-production-reality\" title=\"Performance: Where Theory Meets Production Reality\"\u003ePerformance: Where Theory Meets Production Reality\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eAlgorithmic complexity feels manageable in theoretical discussion. O(n) sounds reasonable. O(n²) seems acceptable for small datasets. O(n log n) feels efficient. Then production traffic hits, datasets grow larger than anticipated, and theoretical complexity translates into CPU cost, memory pressure, and timeout failures.\u003c/p\u003e\n\u003cp\u003eI\u0026rsquo;ve debugged production incidents where perfectly logical code—code that passed all functional tests—caused cascading performance failures. \u003cstrong\u003eHours wasted.\u003c/strong\u003e Customer complaints. Emergency hotfixes.\u003c/p\u003e\n\u003cp\u003eWhy? Complexity analysis happened in abstract terms rather than executable measurement.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eExample:\u003c/strong\u003e Nested LINQ queries that looked clean and expressive during development:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// Looks elegant, reads well\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"n\"\u003eIEnumerable\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eOrderSummary\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003eGetCustomerOrders\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"n\"\u003ecustomerId\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003e_orders\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eWhere\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eo\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003eo\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eCustomerId\u003c/span\u003e \u003cspan class=\"p\"\u003e==\u003c/span\u003e \u003cspan class=\"n\"\u003ecustomerId\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eSelect\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eo\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"k\"\u003enew\u003c/span\u003e \u003cspan class=\"n\"\u003eOrderSummary\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"n\"\u003eOrderId\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eo\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eId\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"n\"\u003eTotal\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eo\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eLineItems\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eSum\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eli\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003eli\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ePrice\u003c/span\u003e \u003cspan class=\"p\"\u003e*\u003c/span\u003e \u003cspan class=\"n\"\u003eli\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eQuantity\u003c/span\u003e\u003cspan class=\"p\"\u003e),\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"n\"\u003eItemCount\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eo\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eLineItems\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eCount\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"n\"\u003eCategories\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eo\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eLineItems\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eSelect\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eli\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003eli\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eProduct\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eCategory\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eDistinct\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eOrderBy\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ec\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003ec\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eName\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eToList\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e})\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eOrderByDescending\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003es\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003es\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eTotal\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eToList\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis code communicates intent clearly. In abstract reasoning, it feels straightforward: \u0026ldquo;Get orders, calculate summaries, sort by total.\u0026rdquo; But execute it with real data and watch database query patterns, memory allocations, and execution time:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eMultiple database round trips per order (N+1 query problem)\u003c/li\u003e\n\u003cli\u003eRepeated calculations over the same collections\u003c/li\u003e\n\u003cli\u003eUnnecessary allocations for intermediate collections\u003c/li\u003e\n\u003cli\u003eLinear scans for categories on every line item\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eThe abstract reasoning missed what executable profiling makes obvious:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// Same intent, different execution characteristics\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kd\"\u003easync\u003c/span\u003e \u003cspan class=\"n\"\u003eTask\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eIEnumerable\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eOrderSummary\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003eGetCustomerOrders\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"n\"\u003ecustomerId\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003eorders\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003e_context\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eOrders\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eWhere\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eo\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003eo\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eCustomerId\u003c/span\u003e \u003cspan class=\"p\"\u003e==\u003c/span\u003e \u003cspan class=\"n\"\u003ecustomerId\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eInclude\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eo\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003eo\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eLineItems\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eThenInclude\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eli\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003eli\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eProduct\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eThenInclude\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ep\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003ep\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eCategory\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAsNoTracking\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eToListAsync\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003eorders\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eSelect\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eo\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"k\"\u003enew\u003c/span\u003e \u003cspan class=\"n\"\u003eOrderSummary\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"n\"\u003eOrderId\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eo\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eId\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"n\"\u003eTotal\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eo\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eLineItems\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eSum\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eli\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003eli\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ePrice\u003c/span\u003e \u003cspan class=\"p\"\u003e*\u003c/span\u003e \u003cspan class=\"n\"\u003eli\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eQuantity\u003c/span\u003e\u003cspan class=\"p\"\u003e),\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"n\"\u003eItemCount\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eo\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eLineItems\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eCount\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"n\"\u003eCategories\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eo\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eLineItems\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eSelect\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eli\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003eli\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eProduct\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eCategory\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eName\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eDistinct\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eOrder\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eToList\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e})\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eOrderByDescending\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003es\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003es\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eTotal\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eToList\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eCode forced the performance implications into measurable form. Profiling revealed what abstract thought deferred—database round trips, allocation patterns, execution cost. Without writing and measuring executable code, these consequences remain invisible.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"the-feedback-loop-programming-provides\"\u003e\u003ca href=\"/posts/feedback-loop-ai-cant-replace/#the-feedback-loop-programming-provides\" title=\"The Feedback Loop Programming Provides\"\u003eThe Feedback Loop Programming Provides\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eProgramming isn\u0026rsquo;t just thinking\u0026rsquo;s output—it\u0026rsquo;s thinking\u0026rsquo;s verification mechanism. The discipline of translating thought into executable form exposes inconsistencies, reveals missing decisions, and surfaces consequences that abstract reasoning defers.\u003c/p\u003e\n\u003cp\u003eThis feedback loop operates at multiple levels:\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"compilation-immediate-logical-feedback\"\u003e\u003ca href=\"/posts/feedback-loop-ai-cant-replace/#compilation-immediate-logical-feedback\" title=\"Compilation: Immediate Logical Feedback\"\u003eCompilation: Immediate Logical Feedback\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eThe compiler catches type mismatches, null reference possibilities, exhaustiveness gaps, and logical inconsistencies within seconds. No mental review provides this consistency and speed.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"testing-behavioral-verification\"\u003e\u003ca href=\"/posts/feedback-loop-ai-cant-replace/#testing-behavioral-verification\" title=\"Testing: Behavioral Verification\"\u003eTesting: Behavioral Verification\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eUnit tests, integration tests, and property-based tests validate that your mental model of system behavior matches actual execution. I\u0026rsquo;ve written tests expecting specific behavior only to discover the code does something entirely different—not because implementation was wrong, but because reasoning was incomplete.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"na\"\u003e[Fact]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"k\"\u003evoid\u003c/span\u003e \u003cspan class=\"n\"\u003eCalculateDiscount_PremiumCustomer_HighValue_December_Returns25Percent\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e// Test reveals the logic we thought we implemented doesn\u0026#39;t match what we actually coded\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003ecustomer\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003enew\u003c/span\u003e \u003cspan class=\"n\"\u003eCustomer\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"n\"\u003eIsPremium\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eYearsActive\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"m\"\u003e3\u003c/span\u003e \u003cspan class=\"p\"\u003e};\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003eorder\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003enew\u003c/span\u003e \u003cspan class=\"n\"\u003eOrder\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"n\"\u003eTotal\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"m\"\u003e1500\u003c/span\u003e \u003cspan class=\"p\"\u003e};\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003edate\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003enew\u003c/span\u003e \u003cspan class=\"n\"\u003eDateTime\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"m\"\u003e2025\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"m\"\u003e12\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"m\"\u003e15\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003ediscount\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003e_calculator\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eCalculateDiscount\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eorder\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003ecustomer\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003edate\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eAssert\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eEqual\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"m\"\u003e0.25\u003c/span\u003e\u003cspan class=\"n\"\u003em\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003ediscount\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e \u003cspan class=\"c1\"\u003e// Fails: Returns 0.15m instead\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe test didn\u0026rsquo;t catch a bug in isolation—it caught incomplete thinking that manifested as unexpected behavior. Without executable code and explicit testing, that gap stays hidden until production.\u003c/p\u003e\n\n\n\n\n\u003ch4 id=\"the-ai-testing-trap\"\u003e\u003ca href=\"/posts/feedback-loop-ai-cant-replace/#the-ai-testing-trap\" title=\"The AI Testing Trap\"\u003eThe AI Testing Trap\u003c/a\u003e\u003c/h4\u003e\n\u003cp\u003eAI can generate tests as easily as it generates implementations. Ask for unit tests, and you\u0026rsquo;ll get methods that exercise code paths and verify outputs. \u003cstrong\u003eThis creates a dangerous illusion: high code coverage with low confidence.\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eAI-generated tests typically verify \u003cstrong\u003ehappy paths\u003c/strong\u003e—the scenarios explicitly described in prompts. They \u003cstrong\u003erarely\u003c/strong\u003e test:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eEdge cases that emerge from domain understanding\u003c/li\u003e\n\u003cli\u003eConcurrency issues that only appear under load\u003c/li\u003e\n\u003cli\u003eError propagation through system boundaries\u003c/li\u003e\n\u003cli\u003eIntegration failures when dependencies behave unexpectedly\u003c/li\u003e\n\u003cli\u003ePerformance degradation with realistic data volumes\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e\u003cstrong\u003eI\u0026rsquo;ve reviewed codebases with 90%+ test coverage where AI generated both implementation and tests.\u003c/strong\u003e Every test passed. Yet production revealed critical bugs because the tests verified that the code did \u003cstrong\u003ewhat it was written to do\u003c/strong\u003e, not that it \u003cstrong\u003esolved the actual problem correctly\u003c/strong\u003e.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eThe professional\u0026rsquo;s advantage:\u003c/strong\u003e knowing what to test comes from understanding how systems fail in production. That knowledge \u003cstrong\u003ecan\u0026rsquo;t be prompted\u003c/strong\u003e—it must be experienced, internalized, and applied deliberately.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"profiling-performance-reality-check\"\u003e\u003ca href=\"/posts/feedback-loop-ai-cant-replace/#profiling-performance-reality-check\" title=\"Profiling: Performance Reality Check\"\u003eProfiling: Performance Reality Check\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eProfilers measure actual CPU consumption, memory allocation patterns, I/O bottlenecks, and threading contention. Abstract complexity analysis (Big-O notation) provides theoretical bounds. Profiling provides operational reality.\u003c/p\u003e\n\u003cp\u003eVisual Studio\u0026rsquo;s .NET Object Allocation tool shows exactly which code paths allocate memory and how much. BenchmarkDotNet provides precise execution timing with statistical analysis. These tools don\u0026rsquo;t just measure code—they validate or invalidate reasoning about performance characteristics.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"na\"\u003e[MemoryDiagnoser]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eStringBuildingBenchmark\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"na\"\u003e    [Benchmark]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kt\"\u003estring\u003c/span\u003e \u003cspan class=\"n\"\u003eConcatenationInLoop\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"kt\"\u003estring\u003c/span\u003e \u003cspan class=\"n\"\u003eresult\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003efor\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"n\"\u003ei\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"m\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"n\"\u003ei\u003c/span\u003e \u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e \u003cspan class=\"m\"\u003e1000\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"n\"\u003ei\u003c/span\u003e\u003cspan class=\"p\"\u003e++)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"n\"\u003eresult\u003c/span\u003e \u003cspan class=\"p\"\u003e+=\u003c/span\u003e \u003cspan class=\"n\"\u003ei\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eToString\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e \u003cspan class=\"c1\"\u003e// Abstract: \u0026#34;Should be fine for 1000 iterations\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003eresult\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"na\"\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"na\"\u003e    [Benchmark]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kt\"\u003estring\u003c/span\u003e \u003cspan class=\"n\"\u003eStringBuilderInLoop\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003ebuilder\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003enew\u003c/span\u003e \u003cspan class=\"n\"\u003eStringBuilder\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003efor\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"n\"\u003ei\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"m\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"n\"\u003ei\u003c/span\u003e \u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e \u003cspan class=\"m\"\u003e1000\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"n\"\u003ei\u003c/span\u003e\u003cspan class=\"p\"\u003e++)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"n\"\u003ebuilder\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAppend\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ei\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003ebuilder\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eToString\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// Results expose reality abstract thinking missed:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// ConcatenationInLoop:  3,450 μs,  allocated: 2,031,616 B\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// StringBuilderInLoop:     45 μs,  allocated:     24,624 B\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe difference between \u0026ldquo;seems reasonable\u0026rdquo; and \u0026ldquo;actually performs\u0026rdquo; is 75x execution time and 80x memory allocation. Abstract reasoning deferred these consequences. Executable code and measurement made them visible.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"production-ultimate-reality-validation\"\u003e\u003ca href=\"/posts/feedback-loop-ai-cant-replace/#production-ultimate-reality-validation\" title=\"Production: Ultimate Reality Validation\"\u003eProduction: Ultimate Reality Validation\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eProduction exposes every assumption abstract thinking made: scale, concurrency, failure modes, dependency availability, network latency, operational complexity. Code that worked flawlessly in development reveals hidden assumptions when deployed at scale with real users, real data, and real failure conditions.\u003c/p\u003e\n\u003cp\u003eMonitoring, telemetry, and distributed tracing provide feedback about system behavior under actual conditions. Without executable code running in production, all architectural reasoning remains theoretical.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"programming-and-thinking-inseparable-not-sequential\"\u003e\u003ca href=\"/posts/feedback-loop-ai-cant-replace/#programming-and-thinking-inseparable-not-sequential\" title=\"Programming and Thinking: Inseparable, Not Sequential\"\u003eProgramming and Thinking: Inseparable, Not Sequential\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eThe original framing positioned thinking and programming sequentially: think first (the hard part), then program (the easy translation). This model fundamentally misrepresents the relationship.\u003c/p\u003e\n\u003cp\u003eProgramming and thinking are inseparable, iterative, and mutually reinforcing:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e\u003cstrong\u003eAbstract thinking\u003c/strong\u003e identifies problems, explores solution spaces, and proposes approaches.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eCode writing\u003c/strong\u003e forces abstraction into precise, executable form, exposing gaps and inconsistencies.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eExecution and measurement\u003c/strong\u003e reveal consequences—performance, resource consumption, failure modes—that abstract thought deferred.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eRefinement\u003c/strong\u003e incorporates execution reality back into thinking, improving the mental model.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eRepeat\u003c/strong\u003e until thinking and execution align.\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eNeither operates effectively alone. Thinking without code stays vague and unvalidated. Code without thinking becomes mechanical translation without understanding. High-quality software emerges from tight iteration between abstract reasoning and executable verification.\u003c/p\u003e\n\u003cp\u003eThis isn\u0026rsquo;t pedantry about implementation details. This is recognition that software engineering is fundamentally about managing complexity in executable systems. Complexity that can\u0026rsquo;t be reasoned about produces brittle, unmaintainable systems. Complexity that remains purely abstract never confronts operational reality.\u003c/p\u003e\n\u003cp\u003eThe discipline of programming (writing code, measuring behavior, refactoring based on feedback) is how abstract thinking becomes operational reality. It\u0026rsquo;s not the easy part that follows hard thinking. It\u0026rsquo;s the verification mechanism that sharpens thinking and exposes where reasoning was incomplete.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"what-makes-professionals-irreplaceable\"\u003e\u003ca href=\"/posts/feedback-loop-ai-cant-replace/#what-makes-professionals-irreplaceable\" title=\"What Makes Professionals Irreplaceable\"\u003eWhat Makes Professionals Irreplaceable\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eCompilers validate logic. Tests reveal behavioral gaps. Profilers measure performance reality. Production exposes every deferred decision. These tools generate feedback constantly.\u003c/p\u003e\n\u003cp\u003eBut feedback is worthless without interpretation. And interpretation requires experience.\u003c/p\u003e\n\u003cp\u003eWhen a profiler shows 75x performance degradation, the junior developer sees a red flag. The senior engineer sees a memory allocation pattern they\u0026rsquo;ve debugged before, recognizes the architectural constraint it reveals, and knows three ways to fix it based on context. When production monitoring shows intermittent timeout spikes, AI suggests retry logic. The experienced architect recognizes a connection pool exhaustion pattern and addresses the root cause.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eThe irreplaceable skill isn\u0026rsquo;t generating code. It\u0026rsquo;s closing the feedback loop.\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eThat means watching code fail, understanding \u003cem\u003ewhy\u003c/em\u003e it fails, and refining your mental model until your intuition predicts failure modes before they manifest. AI participates in generating code and even in analyzing errors. But it can\u0026rsquo;t internalize the lessons. It can\u0026rsquo;t build the judgment that comes from years of production incidents, debugging sessions, and architectural decisions that played out over time.\u003c/p\u003e\n\u003cp\u003eIn the \u003ca href=\"../real-professional-software-engineering-ai-era/\"\u003efinal part of this series\u003c/a\u003e, we\u0026rsquo;ll examine what this means for professional development. When code generation is commoditized, what skills actually matter? How do you build the cognitive architecture that AI can\u0026rsquo;t replicate?\u003c/p\u003e\n\u003cp\u003eThe answer shapes how we train developers, evaluate expertise, and define what \u0026ldquo;senior engineer\u0026rdquo; means in an AI-augmented world.\u003c/p\u003e\n","date_modified":"2026-05-26T10:22:03+02:00","date_published":"2026-01-15T17:00:00+01:00","id":"https://daily-devops.net/posts/feedback-loop-ai-cant-replace/","language":"en","summary":"Compilers validate logic, profilers expose performance lies, and production reveals every deferred decision. AI cannot close that feedback loop for you.\n","tags":["softwareengineering","codequality","bestpractices","architecture","dotnet","csharp","technicaldebt","ai-code-assistant","github-copilot"],"title":"The Feedback Loop That AI Can't Replace\n","url":"https://daily-devops.net/posts/feedback-loop-ai-cant-replace/"},{"authors":[{"name":"Martin Stühmer","url":"https://daily-devops.net/authors/martin/"}],"content_html":"\u003cp\u003eIn Swabia, a region in southern Germany, there\u0026rsquo;s a cultural concept that outsiders tend to misunderstand as a local joke. \u003cem\u003eKehrwoche\u003c/em\u003e is often translated as \u0026ldquo;sweeping week,\u0026rdquo; which sounds harmless, almost folkloric. In reality, it\u0026rsquo;s a social mechanism for enforcing long-term responsibility in shared environments.\u003c/p\u003e\n\u003cp\u003eEvery household gets assigned a recurring time slot in which it must clean communal areas—stairwells, hallways, sidewalks in front of the building. The scope is clearly defined. The cadence is predictable. The expectation is absolute. What makes this system effective isn\u0026rsquo;t enforcement by authority, but enforcement by culture. Skipping your turn isn\u0026rsquo;t illegal, but it\u0026rsquo;s socially expensive.\u003c/p\u003e\n\u003cp\u003eYour neighbors know when it\u0026rsquo;s your week. They notice if the stairs look questionable on Tuesday morning. They\u0026rsquo;ll mention it. Not aggressively—just a casual observation that somehow carries the weight of communal judgment. The enforcement mechanism isn\u0026rsquo;t bureaucratic. It\u0026rsquo;s the quiet awareness that someone is watching, and someone remembers.\u003c/p\u003e\n\u003cp\u003eThis isn\u0026rsquo;t surveillance in the oppressive sense. It\u0026rsquo;s accountability baked into the social contract. You\u0026rsquo;re not cleaning because Big Brother demands it. You\u0026rsquo;re cleaning because Mrs. Schmid from the second floor has standards, and you\u0026rsquo;re going to face her in the elevator tomorrow.\u003c/p\u003e\n\u003cp\u003e\u003cem\u003eKehrwoche\u003c/em\u003e is scarier than breaking the build on Friday afternoon. At least the build doesn\u0026rsquo;t remember next Tuesday, and it won\u0026rsquo;t give you that look in the hallway.\u003c/p\u003e\n\u003cp\u003eThat dynamic should feel uncomfortably familiar to anyone who has worked on a software system longer than a few months.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"what-kehrwoche-really-means\"\u003e\u003ca href=\"/posts/kehrwoche-technical-debt/#what-kehrwoche-really-means\" title=\"What Kehrwoche Really Means\"\u003eWhat \u003cem\u003eKehrwoche\u003c/em\u003e Really Means\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003e\u003cem\u003eKehrwoche\u003c/em\u003e isn\u0026rsquo;t about cleanliness as an outcome. It\u0026rsquo;s about preventing entropy from becoming visible.\u003c/p\u003e\n\u003cp\u003eNo one expects perfection. Dust will return. Dirt is inevitable. The goal isn\u0026rsquo;t to eliminate mess, but to ensure it never reaches a point where it disrupts daily life. Maintenance is continuous, not reactive.\u003c/p\u003e\n\u003cp\u003eThis mindset is fundamentally different from how many software teams approach quality. In practice, teams often accept slow degradation until it becomes painful enough to justify a large intervention. \u003cem\u003eKehrwoche\u003c/em\u003e deliberately avoids that escalation by making small maintenance unavoidable and routine.\u003c/p\u003e\n\u003cp\u003eIt\u0026rsquo;s discipline by design, not by motivation.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"the-direct-translation-to-software-development\"\u003e\u003ca href=\"/posts/kehrwoche-technical-debt/#the-direct-translation-to-software-development\" title=\"The Direct Translation to Software Development\"\u003eThe Direct Translation to Software Development\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eTechnical debt behaves exactly like dirt in shared spaces. Some of it is created intentionally. Some of it accumulates unintentionally. None of it disappears on its own.\u003c/p\u003e\n\u003cp\u003eThe critical mistake many teams make is treating technical debt as an abstract future concern. It gets tracked in tickets, discussed in retrospectives, and postponed in planning meetings. Over time, it becomes normalized background noise.\u003c/p\u003e\n\u003cp\u003e\u003cem\u003eKehrwoche\u003c/em\u003e doesn\u0026rsquo;t allow for that kind of abstraction. Responsibility is assigned to people, not to the building. In software, responsibility often dissolves into process, tooling, or vague ownership models. When everyone owns the code, nobody cleans it.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"when-everyone-owns-the-code\"\u003e\u003ca href=\"/posts/kehrwoche-technical-debt/#when-everyone-owns-the-code\" title=\"When Everyone Owns The Code\"\u003eWhen Everyone Owns The Code\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eI\u0026rsquo;ve seen this pattern repeat itself across enterprise teams. Build times creep from five minutes to twenty. Test suites grow flaky. Configuration files accumulate \u0026ldquo;temporary\u0026rdquo; exceptions that live for years. Each change makes sense locally. The aggregate effect is paralysis.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"the-stairwell-problem-in-codebases\"\u003e\u003ca href=\"/posts/kehrwoche-technical-debt/#the-stairwell-problem-in-codebases\" title=\"The Stairwell Problem in Codebases\"\u003eThe Stairwell Problem in Codebases\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eShared infrastructure is where technical debt becomes most toxic.\u003c/p\u003e\n\u003cp\u003eBuild pipelines grow slower and more fragile. Configuration files accumulate exceptions. Authentication flows gain special cases that no one fully understands anymore. These aren\u0026rsquo;t edge areas of the system. They\u0026rsquo;re the paths every developer walks through daily.\u003c/p\u003e\n\u003cp\u003eLike a stairwell, these areas are rarely \u0026ldquo;owned\u0026rdquo; by a single team. They evolve through small, justified changes that make sense locally and degrade the global structure over time. No single change is catastrophic. The aggregate effect is.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"how-a-pipeline-becomes-a-stairwell\"\u003e\u003ca href=\"/posts/kehrwoche-technical-debt/#how-a-pipeline-becomes-a-stairwell\" title=\"How A Pipeline Becomes A Stairwell\"\u003eHow A Pipeline Becomes A Stairwell\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eConsider a CI/CD pipeline. Year one, it\u0026rsquo;s ten lines of configuration: trigger on main branch, run the build. Clean. Obvious. Everyone understands it.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-yaml\" data-lang=\"yaml\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003etrigger\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"nt\"\u003ebranches\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"l\"\u003emain]\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003esteps\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e- \u003cspan class=\"nt\"\u003etask\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eDotNetCoreCLI@2\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThree years later, it\u0026rsquo;s dirty, slow, and fragile. Every team has added exceptions to accommodate their needs. Flaky tests are skipped. Build steps are conditionally executed based on branch names. Maintenance tasks are shoehorned into the pipeline because \u0026ldquo;there\u0026rsquo;s no better place.\u0026rdquo; The configuration file has ballooned to hundreds of lines.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-yaml\" data-lang=\"yaml\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003etrigger\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"nt\"\u003ebranches\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"l\"\u003emain, release/*, hotfix/*]\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"nt\"\u003epaths\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003eexclude\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"l\"\u003edocs/*, \u0026#39;*.md\u0026#39;, \u0026#39;!critical.md\u0026#39;]\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003esteps\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e- \u003cspan class=\"nt\"\u003etask\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eDotNetCoreCLI@2\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003econdition\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eand(succeeded(), ne(variables[\u0026#39;Skip.Build\u0026#39;], \u0026#39;true\u0026#39;))\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"c\"\u003e# ... plus 15 more lines of arguments and workarounds\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eEach addition solved a real problem. \u0026ldquo;We need to skip flaky tests until they\u0026rsquo;re fixed.\u0026rdquo; \u0026ldquo;Let\u0026rsquo;s exclude docs to speed up builds.\u0026rdquo; \u0026ldquo;Add a variable to disable builds when we\u0026rsquo;re doing maintenance.\u0026rdquo; Every decision was rational. Every comment explained the context. Nobody was careless.\u003c/p\u003e\n\u003cp\u003eBut nobody was responsible for the aggregate state. The pipeline became the project\u0026rsquo;s stairwell—walked through daily, maintained by nobody, degrading incrementally until simple changes became risky.\u003c/p\u003e\n\u003cp\u003eAt that point, teams don\u0026rsquo;t complain about dirt. They complain about friction, unpredictability, and fear of change. The dirt metaphor just becomes technical language.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"why-big-cleanups-fail-systematically\"\u003e\u003ca href=\"/posts/kehrwoche-technical-debt/#why-big-cleanups-fail-systematically\" title=\"Why Big Cleanups Fail Systematically\"\u003eWhy Big Cleanups Fail Systematically\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eThe instinctive response to accumulated technical debt is the big cleanup. A refactoring initiative. A platform rewrite. A dedicated sprint to \u0026ldquo;fix the foundation.\u0026rdquo;\u003c/p\u003e\n\u003cp\u003eThis approach fails for structural reasons. Maintenance requires context, and context decays quickly. The longer cleanup is delayed, the more expensive it becomes to understand what can be safely changed. Meanwhile, the product continues to evolve, reintroducing new debt in parallel.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"why-context-decay-beats-big-refactors\"\u003e\u003ca href=\"/posts/kehrwoche-technical-debt/#why-context-decay-beats-big-refactors\" title=\"Why Context Decay Beats Big Refactors\"\u003eWhy Context Decay Beats Big Refactors\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eI\u0026rsquo;ve watched teams spend entire quarters on \u0026ldquo;technical debt sprints\u0026rdquo; only to see the same problems resurface within months. The work wasn\u0026rsquo;t wrong. The timing was. By the time they got permission to clean up, they\u0026rsquo;d lost the context needed to do it efficiently.\u003c/p\u003e\n\u003cp\u003e\u003cem\u003eKehrwoche\u003c/em\u003e works precisely because it avoids this trap. The work is small enough to fit into normal life. It doesn\u0026rsquo;t require special ceremonies, approvals, or justifications. It\u0026rsquo;s simply part of living in the system.\u003c/p\u003e\n\u003cp\u003eSoftware maintenance should be treated the same way.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"the-boy-scout-rule-isnt-enough\"\u003e\u003ca href=\"/posts/kehrwoche-technical-debt/#the-boy-scout-rule-isnt-enough\" title=\"The Boy Scout Rule Isn\u0026rsquo;t Enough\"\u003eThe Boy Scout Rule Isn\u0026rsquo;t Enough\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003e\u0026ldquo;Leave the code better than you found it\u0026rdquo; sounds like \u003cem\u003eKehrwoche\u003c/em\u003e. In practice, it rarely functions the same way.\u003c/p\u003e\n\u003cp\u003eThe Boy Scout Rule depends on individual motivation and opportunity. It works when developers have the capacity to improve things proactively. It fails when teams are under pressure to ship features, when codebases are too complex to understand quickly, or when improvements require coordination across multiple teams.\u003c/p\u003e\n\u003cp\u003eMore fundamentally, the Boy Scout Rule makes cleanup optional and contextual. You improve things when you happen to touch them. When you have time. When it seems safe. The decision happens at the worst possible moment—when you\u0026rsquo;re already focused on something else.\u003c/p\u003e\n\u003cp\u003eI\u0026rsquo;ve seen what this produces: teams that talk about code quality constantly but never improve it systematically. Developers who want to clean up but can\u0026rsquo;t justify the time. Pull requests that get blocked because \u0026ldquo;this change is out of scope.\u0026rdquo; The Boy Scout Rule becomes aspirational rather than operational.\u003c/p\u003e\n\u003cp\u003eThe problem isn\u0026rsquo;t lack of goodwill. It\u0026rsquo;s lack of structure. When cleanup depends on individual initiative, it competes with everything else. Feature delivery has deadlines. Bugs have severity. Technical debt has neither. It loses by default.\u003c/p\u003e\n\u003cp\u003e\u003cem\u003eKehrwoche\u003c/em\u003e doesn\u0026rsquo;t depend on motivation. It depends on assignment. You don\u0026rsquo;t clean when you feel like it. You clean when it\u0026rsquo;s your turn. That removes the decision-making burden and the social awkwardness of \u0026ldquo;wasting time\u0026rdquo; on cleanup. There\u0026rsquo;s no debate about whether cleaning is valuable. There\u0026rsquo;s only the question of whether you showed up for your assigned time.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"a-rotation-schedule-for-codebases\"\u003e\u003ca href=\"/posts/kehrwoche-technical-debt/#a-rotation-schedule-for-codebases\" title=\"A Rotation Schedule For Codebases\"\u003eA Rotation Schedule For Codebases\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eA practical translation might look like this: Every team member gets assigned one week per quarter as their \u0026ldquo;cleanup week.\u0026rdquo; During that week, they spend their first hour each day working through a shared technical debt backlog. Not optional. Not negotiable. Just like \u003cem\u003eKehrwoche\u003c/em\u003e, everyone knows the rotation schedule. Everyone takes their turn. The rest of the team knows it\u0026rsquo;s your week and adjusts expectations accordingly.\u003c/p\u003e\n\u003cp\u003eThe items should be small enough to fit the time window. Simplify a confusing method name. Remove a dead code path. Update a misleading comment. Fix a flaky test. Add missing documentation to a cryptic function. Extract a reusable component from duplicated code. The goal isn\u0026rsquo;t heroic refactoring. It\u0026rsquo;s routine grooming.\u003c/p\u003e\n\u003cp\u003eThis shifts the question from \u0026ldquo;Should we clean up?\u0026rdquo; to \u0026ldquo;What do we clean up today?\u0026rdquo; That subtle change in framing eliminates most of the friction. Cleanup stops being a negotiation and becomes a rhythm. And just like with stairwells, your teammates notice when it\u0026rsquo;s your week. They\u0026rsquo;ll mention if nothing improved. Not aggressively—just a casual observation that carries weight.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"humor-helps-but-culture-does-the-real-work\"\u003e\u003ca href=\"/posts/kehrwoche-technical-debt/#humor-helps-but-culture-does-the-real-work\" title=\"Humor Helps, but Culture Does the Real Work\"\u003eHumor Helps, but Culture Does the Real Work\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eSwabians complain loudly about \u003cem\u003eKehrwoche\u003c/em\u003e. There are jokes, stereotypes, and endless exaggerations about perfectionist neighbors and impossibly high cleaning standards. None of that weakens the system. If anything, it reinforces it by making the obligation visible and shared. The complaining is part of the ritual, not resistance to it.\u003c/p\u003e\n\u003cp\u003eIn software teams, humor around technical debt often serves the opposite function. It becomes a coping mechanism that replaces action. Legacy jokes turn into excuses. Irony becomes a substitute for responsibility.\u003c/p\u003e\n\u003cp\u003eI\u0026rsquo;ve sat through retrospectives where teams laugh about the \u0026ldquo;haunted module\u0026rdquo; nobody dares to touch. Everyone agrees it\u0026rsquo;s a problem. Everyone acknowledges someone should fix it. Then the meeting ends, and nothing changes. The joke releases tension without creating obligation.\u003c/p\u003e\n\u003cp\u003e\u0026ldquo;We\u0026rsquo;ll fix it in the next sprint\u0026rdquo; becomes the technical debt equivalent of \u0026ldquo;thoughts and prayers.\u0026rdquo; It signals concern without committing action. The laughter acknowledges shared pain but doesn\u0026rsquo;t demand shared responsibility.\u003c/p\u003e\n\u003cp\u003eThe difference is structural. In Swabia, you can complain about \u003cem\u003eKehrwoche\u003c/em\u003e all you want. You still have to clean when it\u0026rsquo;s your week. The humor doesn\u0026rsquo;t grant an exemption. In software, humor often becomes the exemption itself. We laugh instead of fixing.\u003c/p\u003e\n\u003cp\u003eA healthy culture allows humor without letting it undermine discipline. Complaining is fine. Avoiding cleanup is not. The question is whether your jokes postpone work or acknowledge the work you\u0026rsquo;re already doing.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"a-practical-takeaway\"\u003e\u003ca href=\"/posts/kehrwoche-technical-debt/#a-practical-takeaway\" title=\"A Practical Takeaway\"\u003eA Practical Takeaway\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003e\u003cem\u003eKehrwoche\u003c/em\u003e scales because it\u0026rsquo;s boring, predictable, and non-negotiable. It doesn\u0026rsquo;t rely on heroics or passion. It relies on consistency and social expectation. That\u0026rsquo;s the part most software teams get wrong. They wait for inspiration, alignment, or the perfect moment. None of those ever arrive.\u003c/p\u003e\n\u003cp\u003eApplied to software development, this means treating technical debt as a first-class operational concern. Cleanup must be small, frequent, and attached to real ownership. Not a future initiative. Not a side project. Not something that only happens when everything else is done.\u003c/p\u003e\n\u003cp\u003eStart with the rotation. Assign cleanup weeks to team members on a predictable schedule. Make it visible. Make it normal. Make it expected. When it\u0026rsquo;s your week, you clean. When it\u0026rsquo;s not, you respect that someone else is handling the work you\u0026rsquo;ll do next quarter.\u003c/p\u003e\n\u003cp\u003eThe work itself should be unglamorous. If it feels heroic, you\u0026rsquo;ve waited too long. Simplify method names. Remove dead imports. Fix misleading comments. Update outdated documentation. Delete code paths nobody uses anymore. These aren\u0026rsquo;t the changes that go in blog posts. They\u0026rsquo;re the changes that keep the system navigable.\u003c/p\u003e\n\u003cp\u003eClean systems aren\u0026rsquo;t the result of exceptional engineers. They\u0026rsquo;re the result of ordinary engineers doing unglamorous work regularly. That\u0026rsquo;s the uncomfortable truth \u003cem\u003eKehrwoche\u003c/em\u003e makes unavoidable.\u003c/p\u003e\n\u003cp\u003eIn Swabia, that work involves a broom.\u003c/p\u003e\n\u003cp\u003eIn software, it involves discipline, restraint, and the willingness to clean up after yourself before someone else has to.\u003c/p\u003e\n\u003cp\u003eThe stairwell is shared. Everyone walks through it. Everyone keeps it clean. Your turn comes around whether you like it or not. The only question is whether you\u0026rsquo;ll show up.\u003c/p\u003e\n\u003cp\u003eAnd if you need a place to start sweeping—that \u003cem\u003e4,000-line\u003c/em\u003e \u003ccode\u003eUtils.cs\u003c/code\u003e file everyone\u0026rsquo;s afraid to touch is basically the digital equivalent of a stairwell that hasn\u0026rsquo;t seen a broom in three years. Mrs. Schmid would be \u003cstrong\u003edisappointed\u003c/strong\u003e.\u003c/p\u003e\n","date_modified":"2026-05-26T10:22:03+02:00","date_published":"2026-01-09T11:00:00+01:00","id":"https://daily-devops.net/posts/kehrwoche-technical-debt/","language":"en","summary":"A Swabian tradition reveals why small, routine maintenance beats big cleanup initiatives—and what software teams get wrong about technical debt.\n","tags":["technicaldebt","softwareengineering","codequality","bestpractices"],"title":"Kehrwoche: What Swabian Cleaning Teaches About Technical Debt","url":"https://daily-devops.net/posts/kehrwoche-technical-debt/"},{"authors":[{"name":"Martin Stühmer","url":"https://daily-devops.net/authors/martin/"}],"content_html":"\n\n\n\n\u003ch2 id=\"your-tuesday-morning-a-true-story\"\u003e\u003ca href=\"/posts/alphabet-soup-file-formats/#your-tuesday-morning-a-true-story\" title=\"Your Tuesday Morning: A True Story\"\u003eYour Tuesday Morning: A True Story\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eIt\u0026rsquo;s 9 AM. You\u0026rsquo;re debugging why the Kubernetes deployment failed overnight. The YAML looked perfect. Indentation? Check. Syntax? Check. The problem? Someone used \u003ccode\u003eNO\u003c/code\u003e as an environment variable value. YAML helpfully parsed it as boolean \u003ccode\u003efalse\u003c/code\u003e. Your pod never started.\u003c/p\u003e\n\u003cp\u003eBy 10 AM, you\u0026rsquo;re fixing the CSV export that accounting requested. Excel mangled the employee IDs—turned \u003ccode\u003e00123\u003c/code\u003e into \u003ccode\u003e123\u003c/code\u003e, converted the date column into something unrecognizable, and decided that gene names like \u003ccode\u003eSEPT2\u003c/code\u003e are obviously September 2nd.\u003c/p\u003e\n\u003cp\u003eAt 11 AM, the build pipeline breaks because someone added a trailing comma to \u003ccode\u003eappsettings.json\u003c/code\u003e. JSON doesn\u0026rsquo;t allow those. The error message is cryptic. The fix takes 30 seconds. Finding it took 20 minutes.\u003c/p\u003e\n\u003cp\u003eLunch is spent explaining to a junior dev why we have YAML for CI/CD, JSON for app config, TOML for the Rust tool, INI for the legacy service, and CSV for data exports. \u0026ldquo;Can\u0026rsquo;t we just pick one?\u0026rdquo; they ask.\u003c/p\u003e\n\u003cp\u003eNo. We can\u0026rsquo;t. This is software development in 2025.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"the-format-parade-whos-who-in-the-chaos\"\u003e\u003ca href=\"/posts/alphabet-soup-file-formats/#the-format-parade-whos-who-in-the-chaos\" title=\"The Format Parade: Who\u0026rsquo;s Who in the Chaos\"\u003eThe Format Parade: Who\u0026rsquo;s Who in the Chaos\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eLet\u0026rsquo;s meet the contestants in this never-ending format beauty pageant. Spoiler: nobody wins.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"csv-the-universal-disaster\"\u003e\u003ca href=\"/posts/alphabet-soup-file-formats/#csv-the-universal-disaster\" title=\"CSV: The Universal Disaster\"\u003e\u003cstrong\u003eCSV: The Universal Disaster\u003c/strong\u003e\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eComma-Separated Values promised simplicity: rows, columns, commas. That\u0026rsquo;s it.\u003c/p\u003e\n\u003cp\u003eThe reality? There\u0026rsquo;s no real standard. RFC 4180 tried in 2005, but thousands of tools had already shipped their own interpretations. Comma delimiter? Sometimes semicolon. Sometimes tab. Quote your strings? Maybe. Escape quotes by doubling them? Or backslashes? Depends on the tool.\u003c/p\u003e\n\u003cp\u003eExcel is CSV\u0026rsquo;s natural enemy. It will \u0026ldquo;fix\u0026rdquo; your data by converting dates (\u003ccode\u003e2025-12-31\u003c/code\u003e becomes Excel\u0026rsquo;s internal date format), stripping leading zeros (\u003ccode\u003e00123\u003c/code\u003e → \u003ccode\u003e123\u003c/code\u003e), and famously turning gene names into dates (\u003ccode\u003eSEPT2\u003c/code\u003e → Sep 2). Biologists have a special hatred for CSV because of this.\u003c/p\u003e\n\u003cp\u003eYet CSV survives because it\u0026rsquo;s \u003cstrong\u003euniversal\u003c/strong\u003e. Every tool exports it. Every developer can edit it in Notepad. It compresses well. It\u0026rsquo;s the lowest common denominator when nothing else works.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eVerdict:\u003c/strong\u003e Like democracy, it\u0026rsquo;s the worst format except for all the others.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"ini-the-minimalist\"\u003e\u003ca href=\"/posts/alphabet-soup-file-formats/#ini-the-minimalist\" title=\"INI: The Minimalist\u0026rsquo;s Dream\"\u003e\u003cstrong\u003eINI: The Minimalist\u0026rsquo;s Dream\u003c/strong\u003e\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eKey-value pairs. Sections. That\u0026rsquo;s the entire spec. Humans understand it instantly.\u003c/p\u003e\n\u003cp\u003eThe problem? No nested structures. No lists. No type system—everything\u0026rsquo;s a string. The moment you need hierarchy, INI taps out.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eVerdict:\u003c/strong\u003e Perfect for simple configs. Useless for everything else.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"xml-the-enterprise-albatross\"\u003e\u003ca href=\"/posts/alphabet-soup-file-formats/#xml-the-enterprise-albatross\" title=\"XML: The Enterprise Albatross\"\u003e\u003cstrong\u003eXML: The Enterprise Albatross\u003c/strong\u003e\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eXML promised schema validation, namespaces, self-describing tags—enterprise-grade power.\u003c/p\u003e\n\u003cp\u003eWhat we got: angle bracket hell. Every value wrapped in opening and closing tags. Signal-to-noise ratio so poor that even enterprise architects—people who \u003cem\u003ethrive\u003c/em\u003e on complexity—started looking for alternatives.\u003c/p\u003e\n\u003cp\u003eWhen the people who love complexity want simpler, you\u0026rsquo;ve failed at usability.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eVerdict:\u003c/strong\u003e Still haunting legacy systems. Nobody voluntarily starts new projects with XML anymore.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"json-the-machine\"\u003e\u003ca href=\"/posts/alphabet-soup-file-formats/#json-the-machine\" title=\"JSON: The Machine\u0026rsquo;s Format\"\u003e\u003cstrong\u003eJSON: The Machine\u0026rsquo;s Format\u003c/strong\u003e\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eJSON solved XML\u0026rsquo;s verbosity. Clean syntax. Maps directly to data structures. Every language has a parser.\u003c/p\u003e\n\u003cp\u003eBut it was designed for \u003cstrong\u003emachines\u003c/strong\u003e, not humans. No comments (explain your config changes in commit messages, I guess). Trailing commas forbidden. Rigid quoting everywhere. It works, but it\u0026rsquo;s tedious to hand-edit.\u003c/p\u003e\n\u003cp\u003eOpenAI\u0026rsquo;s function calling? JSON-only. Why? Because it\u0026rsquo;s deterministic. One correct way to structure it.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eVerdict:\u003c/strong\u003e Boring, reliable, ubiquitous. The Toyota Camry of data formats.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"yaml-the-beautiful-disaster\"\u003e\u003ca href=\"/posts/alphabet-soup-file-formats/#yaml-the-beautiful-disaster\" title=\"YAML: The Beautiful Disaster\"\u003e\u003cstrong\u003eYAML: The Beautiful Disaster\u003c/strong\u003e\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eYAML threw JSON\u0026rsquo;s rigidity out the window. Minimal syntax. Comments everywhere. Indentation-based. Human-friendly!\u003c/p\u003e\n\u003cp\u003eExcept it\u0026rsquo;s \u003cstrong\u003ewhitespace-sensitive\u003c/strong\u003e. One misaligned space breaks everything, often silently. Implicit type conversions bite constantly (\u003ccode\u003eNO\u003c/code\u003e → \u003ccode\u003efalse\u003c/code\u003e, \u003ccode\u003eon\u003c/code\u003e → \u003ccode\u003etrue\u003c/code\u003e, \u003ccode\u003e0123\u003c/code\u003e → octal 83). The spec is 23,000 words and allows multiple ways to represent the same data.\u003c/p\u003e\n\u003cp\u003eKubernetes chose YAML. Docker Compose chose YAML. GitHub Actions chose YAML. The ecosystem standardized, so now you\u0026rsquo;re learning YAML whether you like it or not.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eVerdict:\u003c/strong\u003e Feels great until it doesn\u0026rsquo;t. Then you\u0026rsquo;re debugging indentation at 2 AM.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"toml-the-pragmatic-middle-ground\"\u003e\u003ca href=\"/posts/alphabet-soup-file-formats/#toml-the-pragmatic-middle-ground\" title=\"TOML: The Pragmatic Middle Ground\"\u003e\u003cstrong\u003eTOML: The Pragmatic Middle Ground\u003c/strong\u003e\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eTom\u0026rsquo;s Obvious Minimal Language tried to be INI + structure + types. Explicit syntax. No whitespace sensitivity. Comments allowed.\u003c/p\u003e\n\u003cp\u003eIt works. It\u0026rsquo;s clear. It\u0026rsquo;s unambiguous. The ecosystem is smaller than YAML/JSON, but growing.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eVerdict:\u003c/strong\u003e Underrated. Use it for build configs if you can.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"taml-radical-minimalism\"\u003e\u003ca href=\"/posts/alphabet-soup-file-formats/#taml-radical-minimalism\" title=\"TAML: Radical Minimalism\"\u003e\u003cstrong\u003eTAML: Radical Minimalism\u003c/strong\u003e\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eTab Annotated Markup Language: tabs for hierarchy, newlines for structure. No brackets, colons, or quotes.\u003c/p\u003e\n\u003cp\u003eOne tab = one level deeper. That\u0026rsquo;s the entire format.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eVerdict:\u003c/strong\u003e Interesting experiment. Tiny ecosystem. Good for greenfield projects if you\u0026rsquo;re willing to bet on niche formats.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"toon-designed-for-ai\"\u003e\u003ca href=\"/posts/alphabet-soup-file-formats/#toon-designed-for-ai\" title=\"TOON: Designed for AI\"\u003e\u003cstrong\u003eTOON: Designed for AI\u003c/strong\u003e\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eToken-Oriented Object Notation emerged in 2025 specifically to solve LLM generation problems.\u003c/p\u003e\n\u003cp\u003e~40% fewer tokens than JSON. Explicit \u003ccode\u003e[N]\u003c/code\u003e array lengths and \u003ccode\u003e{fields}\u003c/code\u003e headers give AI models clear guardrails. Better accuracy (74% vs JSON\u0026rsquo;s 70%) because the schema is baked into the syntax.\u003c/p\u003e\n\u003cp\u003eIt\u0026rsquo;s \u0026ldquo;JSON optimized for transformer models.\u0026rdquo; Human-readable like YAML, compact like CSV, schema-aware like XML.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eVerdict:\u003c/strong\u003e If you\u0026rsquo;re building systems where LLMs frequently generate structured data, TOON might save you. Otherwise, wait to see if it gains traction.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"ccl-category-theory-elegance\"\u003e\u003ca href=\"/posts/alphabet-soup-file-formats/#ccl-category-theory-elegance\" title=\"CCL: Category Theory Elegance\"\u003e\u003cstrong\u003eCCL: Category Theory Elegance\u003c/strong\u003e\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eCategorical Configuration Language built on mathematical principles. Pure key-value pairs with recursive nesting.\u003c/p\u003e\n\u003cp\u003eMinimal syntax: \u003ccode\u003ekey = value\u003c/code\u003e. Comments via \u003ccode\u003e/=\u003c/code\u003e. Merging configs is associative with an identity element. Provably correct composition.\u003c/p\u003e\n\u003cp\u003eThe ecosystem is tiny (OCaml, Rust implementations). Practical? Depends on whether you value theoretical soundness over ecosystem maturity.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eVerdict:\u003c/strong\u003e For people who think in category theory. Everyone else, use TOML.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"bson-the-binary-option\"\u003e\u003ca href=\"/posts/alphabet-soup-file-formats/#bson-the-binary-option\" title=\"BSON: The Binary Option\"\u003e\u003cstrong\u003eBSON: The Binary Option\u003c/strong\u003e\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eBinary JSON. Optimized for machine efficiency. Fast parsing. Compact storage.\u003c/p\u003e\n\u003cp\u003eOpen it in a text editor? Gibberish. It\u0026rsquo;s for databases (MongoDB), not human editing.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eVerdict:\u003c/strong\u003e Right tool for the right job. Don\u0026rsquo;t hand-edit BSON.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"why-we-cant-have-nice-things\"\u003e\u003ca href=\"/posts/alphabet-soup-file-formats/#why-we-cant-have-nice-things\" title=\"Why We Can\u0026rsquo;t Have Nice Things\"\u003eWhy We Can\u0026rsquo;t Have Nice Things\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eHere\u0026rsquo;s the uncomfortable truth: \u003cstrong\u003eevery format genuinely solved real problems\u003c/strong\u003e.\u003c/p\u003e\n\u003cp\u003eCSV ended proprietary spreadsheet lock-in. XML brought schema validation. JSON fixed XML\u0026rsquo;s verbosity. YAML made configs readable. TOML removed YAML\u0026rsquo;s gotchas. TAML went minimal. TOON optimized for AI. CCL brought mathematical rigor.\u003c/p\u003e\n\u003cp\u003eEach improvement was real. Each one also created new problems.\u003c/p\u003e\n\u003cp\u003eThe xkcd comic about competing standards isn\u0026rsquo;t a joke anymore—it\u0026rsquo;s your job description. We don\u0026rsquo;t have 15 standards. We have 50. Maybe 100.\u003c/p\u003e\n\u003cp\u003eFormats don\u0026rsquo;t converge because \u003cstrong\u003etrade-offs are real\u003c/strong\u003e:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eHuman readability ↔ Machine efficiency\u003c/li\u003e\n\u003cli\u003eFlexibility ↔ Parsability\u003c/li\u003e\n\u003cli\u003eSimplicity ↔ Features\u003c/li\u003e\n\u003cli\u003eEcosystem size ↔ Specialized optimization\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eAs AI systems join the picture, the calculus shifts again. Formats designed for humans sometimes hurt AI. Formats designed for machines frustrate humans. Designing for both? That\u0026rsquo;s what TOON attempts.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"the-ai-problem-makes-everything-worse\"\u003e\u003ca href=\"/posts/alphabet-soup-file-formats/#the-ai-problem-makes-everything-worse\" title=\"The AI Problem Makes Everything Worse\"\u003eThe AI Problem Makes Everything Worse\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eLarge language models changed the game.\u003c/p\u003e\n\u003cp\u003eChatGPT generates text token by token via pattern matching. Rigid formats like JSON? Manageable. Structure it one of 3-4 ways, models usually succeed.\u003c/p\u003e\n\u003cp\u003eYAML? Disaster. Whitespace sensitivity, implicit type conversions, multiple valid representations—LLMs generate frequently invalid YAML. The \u003cem\u003estructure\u003c/em\u003e looks right, but subtle indentation errors or quoting mistakes break parsing.\u003c/p\u003e\n\u003cp\u003eThis drove OpenAI to mandate JSON-only for function calling. Not because engineers are lazy. Because JSON has \u003cstrong\u003eone correct way\u003c/strong\u003e, and models can learn it reliably.\u003c/p\u003e\n\u003cp\u003eThe irony: we designed YAML for human convenience. AI exposed that \u0026ldquo;flexibility\u0026rdquo; creates too many ways to fail.\u003c/p\u003e\n\u003cp\u003eTOON exists specifically to solve this. Explicit schema headers, deterministic structure, fewer tokens. It\u0026rsquo;s pragmatic engineering: \u0026ldquo;YAML breaks AI, JSON works but is verbose, so let\u0026rsquo;s design something in between that models can generate correctly.\u0026rdquo;\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"survival-guide-five-strategies-that-actually-work\"\u003e\u003ca href=\"/posts/alphabet-soup-file-formats/#survival-guide-five-strategies-that-actually-work\" title=\"Survival Guide: Five Strategies That Actually Work\"\u003eSurvival Guide: Five Strategies That Actually Work\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eYou\u0026rsquo;re stuck with this chaos. Here\u0026rsquo;s how to survive it:\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e1. Choose Deliberately\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eIf you\u0026rsquo;re using JSON for config, \u003cstrong\u003eown it\u003c/strong\u003e. Document the decision. Set up schema validation. If you\u0026rsquo;re exporting CSV, specify delimiter, encoding, quoting rules explicitly.\u003c/p\u003e\n\u003cp\u003eDon\u0026rsquo;t drift into formats by accident. Make conscious choices and commit to them.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e2. Invest in Tooling\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eLinters. Schema validators. Type safety. These matter \u003cstrong\u003emore than the format\u003c/strong\u003e.\u003c/p\u003e\n\u003cp\u003eGood tooling makes mediocre formats manageable:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eCSV: parsers with RFC 4180 support\u003c/li\u003e\n\u003cli\u003eJSON: JSON Schema validators\u003c/li\u003e\n\u003cli\u003eYAML: linters that catch indentation issues\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e\u003cstrong\u003e3. Be Skeptical of \u0026ldquo;Revolutionary\u0026rdquo; Formats\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eEvery new format promises to be The One. TOON and CCL might be useful for specific niches (LLM generation, category theory), but they\u0026rsquo;re not replacing JSON tomorrow.\u003c/p\u003e\n\u003cp\u003eEvaluate pragmatically. Bet on ecosystems, not elegance.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e4. Plan for AI Integration\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eIf AI generates structured data in your system:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003eSafe choice:\u003c/strong\u003e JSON with aggressive validation\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eExperimental:\u003c/strong\u003e TOON if you\u0026rsquo;re adopting early-stage formats\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eAvoid:\u003c/strong\u003e YAML unless you enjoy debugging AI-generated indentation errors\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e\u003cstrong\u003e5. Don\u0026rsquo;t Refactor Just to Standardize\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eLegacy system using INI, XML, or CSV? If it works, \u003cstrong\u003eleave it alone\u003c/strong\u003e.\u003c/p\u003e\n\u003cp\u003eRefactoring formats doesn\u0026rsquo;t fix business problems. It creates migration risk. Only change formats when you\u0026rsquo;re solving actual pain, not pursuing theoretical purity.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"living-with-the-chaos\"\u003e\u003ca href=\"/posts/alphabet-soup-file-formats/#living-with-the-chaos\" title=\"Living with the Chaos\"\u003eLiving with the Chaos\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eThe light at the end of the tunnel isn\u0026rsquo;t format convergence. It never was.\u003c/p\u003e\n\u003cp\u003eIt\u0026rsquo;s accepting that we\u0026rsquo;ll always have multiple formats. CSV for exports. JSON for APIs. YAML for infrastructure. TOML for builds. Maybe TOON for AI outputs.\u003c/p\u003e\n\u003cp\u003eThe real problem was never the format. It was always the \u003cstrong\u003edata\u003c/strong\u003e—complex, context-dependent, requiring human judgment.\u003c/p\u003e\n\u003cp\u003ePick the right tool for each job. Invest in validation. Build good tooling. Stay skeptical of salvation promises.\u003c/p\u003e\n\u003cp\u003eWelcome to file format hell. You\u0026rsquo;re going to be here a while.\u003c/p\u003e\n","date_modified":"2026-05-26T10:22:03+02:00","date_published":"2026-01-08T17:00:00+01:00","id":"https://daily-devops.net/posts/alphabet-soup-file-formats/","language":"en","summary":"CSV breaks on commas. YAML breaks on spaces. JSON breaks on trailing commas. TOML, TAML, TOON, CCL joined the chaos. Nobody wins. Here's why.\n","tags":["configuration","devops","dotnet","bestpractices","softwareengineering","codequality"],"title":"Alphabet Soup: The Format Buffet Nobody Ordered\n","url":"https://daily-devops.net/posts/alphabet-soup-file-formats/"},{"authors":[{"name":"Martin Stühmer","url":"https://daily-devops.net/authors/martin/"}],"content_html":"\u003cp\u003eA \u003ca href=\"https://www.linkedin.com/posts/davideguida_i-feel-its-time-to-make-something-clear-activity-7411391768283271168-naE4\" target=\"_blank\" rel=\"noopener external noreferrer\"\u003eLinkedIn post by David Guida\u003c/a\u003e sparked a discussion that cuts to the bone: \u003cstrong\u003eIs software engineering about thinking or typing?\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eDavid argued forcefully that \u0026ldquo;software engineering is NOT about writing code\u0026rdquo;—that code is merely mechanical output, the easy part, just another language. The hard part, he wrote, is thinking: \u0026ldquo;Programming is a byproduct of the thinking process. And that one, my friends, is the hard part.\u0026rdquo;\u003c/p\u003e\n\u003cp\u003eI responded with a point that needed more space than a LinkedIn comment allows:\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e\u0026ldquo;Strong point, but it slightly overcorrects. Yes, typing code is the easy, mechanical part. The hard part is reasoning, trade-offs, and understanding constraints. Agreed. \u003cstrong\u003eBut dismissing code as \u0026lsquo;just another language\u0026rsquo; undersells its impact.\u003c/strong\u003e Code is not only expression, it is execution, cost, failure modes, and long-term operational risk. Thinking without being forced into precise, executable form often stays vague. Writing code is where weak thinking gets exposed. Programming is a byproduct of thinking, \u003cstrong\u003ebut it is also the feedback loop that sharpens that thinking.\u003c/strong\u003e One without the other does not scale.\u0026rdquo;\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003eDavid\u0026rsquo;s response captured what I\u0026rsquo;m exploring here: \u0026ldquo;I totally agree! I must have oversimplified my thoughts. Your closing note on the feedback loop \u003cstrong\u003ecaptures the reason why real professionals will never be replaced.\u003c/strong\u003e\u0026rdquo;\u003c/p\u003e\n\u003cp\u003eThat last phrase wasn\u0026rsquo;t casual. It addresses the elephant everyone sees but few acknowledge: \u003cstrong\u003eAI code assistants everywhere.\u003c/strong\u003e GitHub Copilot. ChatGPT generating entire applications from prompts. The emerging \u0026ldquo;vibe coding\u0026rdquo; trend where developers describe vibes and let AI handle the dirty work.\u003c/p\u003e\n\u003cp\u003eThe timing matters. We\u0026rsquo;re in an era where typing code has \u003cstrong\u003enever been easier\u003c/strong\u003e. AI generates syntactically correct implementations \u003cstrong\u003efaster than any human can type\u003c/strong\u003e.\u003c/p\u003e\n\u003cp\u003eYet here\u0026rsquo;s what David and I both realized: This makes the feedback loop between thinking and code \u003cstrong\u003emore critical, not less\u003c/strong\u003e.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eAsk yourself:\u003c/strong\u003e When code generation becomes trivial, what separates you from a prompt engineer who thinks they\u0026rsquo;re building software?\u003c/p\u003e\n\u003cp\u003eThe answer: Understanding what that code actually \u003cstrong\u003edoes\u003c/strong\u003e. What it \u003cstrong\u003ecosts\u003c/strong\u003e. Where it \u003cstrong\u003efails\u003c/strong\u003e. Why it \u003cstrong\u003ebreaks under load\u003c/strong\u003e.\u003c/p\u003e\n\u003cp\u003eThis article expands on that feedback loop—the relationship between thinking and code that AI can\u0026rsquo;t replicate. It explores why AI-generated code without deep understanding creates an \u003cstrong\u003eillusion of productivity that collapses catastrophically under production load\u003c/strong\u003e.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"where-vague-thinking-hides\"\u003e\u003ca href=\"/posts/code-sharpens-thinking/#where-vague-thinking-hides\" title=\"Where Vague Thinking Hides\"\u003eWhere Vague Thinking Hides\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eWalk through any architecture review where diagrams look perfect, responsibilities seem clear, and everyone nods in agreement. Then watch what happens when someone starts writing the actual implementation. Suddenly, the clean boundaries blur. The \u0026ldquo;simple\u0026rdquo; abstraction requires five parameters. The proposed interface doesn\u0026rsquo;t fit half the use cases. The design that felt obvious in discussion becomes ambiguous when translated to executable code.\u003c/p\u003e\n\u003cp\u003eThis isn\u0026rsquo;t implementation failing design. This is design revealing itself to be incomplete.\u003c/p\u003e\n\u003cp\u003eI\u0026rsquo;ve sat through countless discussions where proposed solutions felt reasonable until we asked: \u0026ldquo;Show me the code.\u0026rdquo; Not production code—just a sketch. Suddenly, implicit assumptions surface. Missing responsibilities become visible. Performance implications emerge. The architecture that seemed solid in abstract terms crumbles when forced into compilable form.\u003c/p\u003e\n\u003cp\u003eCode demands precision that thought alone doesn\u0026rsquo;t require. When you think through a problem, your mind fills gaps unconsciously, papers over inconsistencies, and substitutes intuition for rigor. When you write code, the compiler—and eventually production—refuses to cooperate with vague intent.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"how-the-compiler-exposes-vague-thinking\"\u003e\u003ca href=\"/posts/code-sharpens-thinking/#how-the-compiler-exposes-vague-thinking\" title=\"How The Compiler Exposes Vague Thinking\"\u003eHow The Compiler Exposes Vague Thinking\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eConsider nullable reference types in C#. Without explicit declaration, you can mentally handwave nullability concerns:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// Vague thinking: \u0026#34;customer will always have a name\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eCustomer\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kt\"\u003estring\u003c/span\u003e \u003cspan class=\"n\"\u003eName\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"k\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003eset\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eEnable nullable reference types, and the compiler forces you to confront reality:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cp\"\u003e#nullable\u003c/span\u003e \u003cspan class=\"n\"\u003eenable\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eCustomer\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kt\"\u003estring\u003c/span\u003e \u003cspan class=\"n\"\u003eName\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"k\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003eset\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e \u003cspan class=\"c1\"\u003e// Warning CS8618: Non-nullable property \u0026#39;Name\u0026#39; must contain \u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                                      \u003cspan class=\"c1\"\u003e// a non-null value when exiting constructor\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis isn\u0026rsquo;t pedantry. This is thinking being forced into honest, executable form. Either you guarantee initialization, accept nullability explicitly, or redesign the constructor contract:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eCustomer\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"n\"\u003eCustomer\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003estring\u003c/span\u003e \u003cspan class=\"n\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003eName\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003ename\u003c/span\u003e \u003cspan class=\"p\"\u003e??\u003c/span\u003e \u003cspan class=\"k\"\u003ethrow\u003c/span\u003e \u003cspan class=\"k\"\u003enew\u003c/span\u003e \u003cspan class=\"n\"\u003eArgumentNullException\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003enameof\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e));\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kt\"\u003estring\u003c/span\u003e \u003cspan class=\"n\"\u003eName\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"k\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe act of writing code exposed a decision that pure thought glossed over. Was \u003ccode\u003eName\u003c/code\u003e required or optional? The compiler didn\u0026rsquo;t care about your mental model—it demanded an explicit answer.\u003c/p\u003e\n\u003cp\u003eThis happens at every level: API contracts, concurrency assumptions, resource ownership, error propagation. Abstract thinking lets you defer these decisions indefinitely. Code forces resolution.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"the-vibe-coding-illusion-when-ai-generates-code-without-understanding\"\u003e\u003ca href=\"/posts/code-sharpens-thinking/#the-vibe-coding-illusion-when-ai-generates-code-without-understanding\" title=\"The Vibe Coding Illusion: When AI Generates Code Without Understanding\"\u003eThe Vibe Coding Illusion: When AI Generates Code Without Understanding\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eAI code assistants accelerate the mechanical part—the typing—\u003cstrong\u003eextraordinarily well\u003c/strong\u003e. Describe a function in natural language, and GitHub Copilot suggests an implementation within seconds. Ask ChatGPT to build a REST API, and it generates hundreds of lines of code that compile and often run.\u003c/p\u003e\n\u003cp\u003eThis feels like \u003cstrong\u003emagic\u003c/strong\u003e until you ask the critical question: \u003cem\u003edoes the generated code do what you actually need, not what you described?\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003eI\u0026rsquo;ve reviewed pull requests where developers used AI to generate complete features. The code compiled. Tests passed. The PR description matched the implementation. Everything looked \u003cstrong\u003efine\u003c/strong\u003e. Until production deployment revealed that the AI had:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eGenerated \u003cstrong\u003ethread-unsafe code\u003c/strong\u003e for concurrent scenarios the prompt didn\u0026rsquo;t mention\u003c/li\u003e\n\u003cli\u003eAllocated memory in hot paths \u003cstrong\u003ewithout consideration\u003c/strong\u003e for garbage collection pressure\u003c/li\u003e\n\u003cli\u003eImplemented \u003cstrong\u003eO(n²) algorithms\u003c/strong\u003e where O(n) solutions existed\u003c/li\u003e\n\u003cli\u003eCreated database queries that worked with test data but \u003cstrong\u003efailed catastrophically\u003c/strong\u003e with production scale\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eIgnored error handling\u003c/strong\u003e edge cases that weren\u0026rsquo;t in the prompt\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e\u003cstrong\u003eThe AI didn\u0026rsquo;t fail—it did exactly what was asked.\u003c/strong\u003e The developer failed by not understanding that code generated from a prompt is a starting point, \u003cstrong\u003enot a solution\u003c/strong\u003e. The feedback loop—write code, measure behavior, understand consequences, refine thinking—got \u003cstrong\u003eshort-circuited\u003c/strong\u003e.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"why-vibe-coding-collapses-under-load\"\u003e\u003ca href=\"/posts/code-sharpens-thinking/#why-vibe-coding-collapses-under-load\" title=\"Why Vibe Coding Collapses Under Load\"\u003eWhy Vibe Coding Collapses Under Load\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003e\u0026ldquo;\u003cstrong\u003eVibe coding\u003c/strong\u003e\u0026rdquo; is the term emerging for this pattern: describe the vibe of what you want, let AI generate implementation, ship it if it passes basic tests. It treats code as expression divorced from execution reality. It assumes that if code compiles and handles the happy path, \u003cstrong\u003eit\u0026rsquo;s correct\u003c/strong\u003e.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eThis assumption works until it doesn\u0026rsquo;t.\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eAnd when it doesn\u0026rsquo;t? You\u0026rsquo;re stuck. No foundation for debugging. Can\u0026rsquo;t reason about performance. Can\u0026rsquo;t identify where implementation diverges from requirements. Can\u0026rsquo;t refactor intelligently.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eWhy?\u003c/strong\u003e Because you don\u0026rsquo;t understand what the code actually does.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHere\u0026rsquo;s your professional advantage:\u003c/strong\u003e You recognize what the compiler \u003cstrong\u003ecan\u0026rsquo;t\u003c/strong\u003e tell you:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eThat the generated code works for the described case but \u003cstrong\u003efails for the dozen edge cases\u003c/strong\u003e you didn\u0026rsquo;t think to mention\u003c/li\u003e\n\u003cli\u003eThat the algorithm performs acceptably with 100 records but \u003cstrong\u003ecollapses with 100,000\u003c/strong\u003e\u003c/li\u003e\n\u003cli\u003eThat the abstraction looks clean but \u003cstrong\u003ecreates maintenance nightmares\u003c/strong\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e\u003cstrong\u003eAI generates syntax. Professionals understand semantics, performance characteristics, failure modes, and operational implications.\u003c/strong\u003e The gap between these is where \u0026ldquo;real professionals will never be replaced.\u0026rdquo;\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"code-materializes-consequences\"\u003e\u003ca href=\"/posts/code-sharpens-thinking/#code-materializes-consequences\" title=\"Code Materializes Consequences\"\u003eCode Materializes Consequences\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eCode isn\u0026rsquo;t just structured thought—it\u0026rsquo;s thought with operational consequences. When you design an architecture, you\u0026rsquo;re reasoning about responsibilities and boundaries. When you implement it, you\u0026rsquo;re creating CPU consumption patterns, memory allocation profiles, I/O bottlenecks, and long-term maintenance burdens.\u003c/p\u003e\n\u003cp\u003eThese aren\u0026rsquo;t secondary concerns. They\u0026rsquo;re the actual impact of your decisions.\u003c/p\u003e\n\u003cp\u003eTake a straightforward example: caching. In discussion, caching sounds simple—\u0026ldquo;we\u0026rsquo;ll cache frequently accessed data.\u0026rdquo; The thinking feels complete. Then you implement it:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// Looks reasonable in isolation\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003eprivate\u003c/span\u003e \u003cspan class=\"k\"\u003ereadonly\u003c/span\u003e \u003cspan class=\"n\"\u003eDictionary\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eCustomer\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003e_cache\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003enew\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"n\"\u003eCustomer\u003c/span\u003e\u003cspan class=\"p\"\u003e?\u003c/span\u003e \u003cspan class=\"n\"\u003eGetCustomer\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"n\"\u003eid\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003e_cache\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eTryGetValue\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eid\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"k\"\u003eout\u003c/span\u003e \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003ecustomer\u003c/span\u003e\u003cspan class=\"p\"\u003e))\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003ecustomer\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003ecustomer\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003e_repository\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eLoad\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eid\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ecustomer\u003c/span\u003e \u003cspan class=\"p\"\u003e!=\u003c/span\u003e \u003cspan class=\"kc\"\u003enull\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003e_cache\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"n\"\u003eid\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003ecustomer\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003ecustomer\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis code compiles. It runs. It even passes basic functional tests.\u003c/p\u003e\n\u003cp\u003eThen production hits.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"what-abstract-thinking-defers\"\u003e\u003ca href=\"/posts/code-sharpens-thinking/#what-abstract-thinking-defers\" title=\"What Abstract Thinking Defers\"\u003eWhat Abstract Thinking Defers\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003e\u003cstrong\u003eWhat abstract thinking missed:\u003c/strong\u003e\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003eMemory\u003c/strong\u003e: Cache grows unbounded. No eviction policy. Memory consumption increases until the process crashes or triggers garbage collection storms that degrade response times by 300%.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eConcurrency\u003c/strong\u003e: No synchronization. Multiple threads corrupt dictionary state, causing crashes or silent data corruption that costs hours of incident response time.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eConsistency\u003c/strong\u003e: Cache never invalidates. Stale data persists indefinitely, creating subtle bugs that customer support escalates—costing reputation and revenue.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eObservability\u003c/strong\u003e: No metrics. You can\u0026rsquo;t tell if caching helps or hurts performance without instrumenting separately.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eEach of these issues represents thinking that felt complete in abstract terms but was fundamentally incomplete in executable reality. The \u0026ldquo;simple\u0026rdquo; caching decision materialized as:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003eprivate\u003c/span\u003e \u003cspan class=\"k\"\u003ereadonly\u003c/span\u003e \u003cspan class=\"n\"\u003eConcurrentDictionary\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eCacheEntry\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eCustomer\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003e_cache\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003enew\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003eprivate\u003c/span\u003e \u003cspan class=\"k\"\u003ereadonly\u003c/span\u003e \u003cspan class=\"n\"\u003eTimeSpan\u003c/span\u003e \u003cspan class=\"n\"\u003e_expirationWindow\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eTimeSpan\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eFromMinutes\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"m\"\u003e5\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003eprivate\u003c/span\u003e \u003cspan class=\"k\"\u003ereadonly\u003c/span\u003e \u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"n\"\u003e_maxCacheSize\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"m\"\u003e10000\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"n\"\u003eCustomer\u003c/span\u003e\u003cspan class=\"p\"\u003e?\u003c/span\u003e \u003cspan class=\"n\"\u003eGetCustomer\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"n\"\u003eid\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003e_cache\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eTryGetValue\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eid\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"k\"\u003eout\u003c/span\u003e \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003eentry\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e\u0026amp;\u0026amp;\u003c/span\u003e \u003cspan class=\"p\"\u003e!\u003c/span\u003e\u003cspan class=\"n\"\u003eentry\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eIsExpired\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003e_expirationWindow\u003c/span\u003e\u003cspan class=\"p\"\u003e))\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003eMetrics\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eCacheHitCounter\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eIncrement\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003eentry\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eValue\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eMetrics\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eCacheMissCounter\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eIncrement\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003ecustomer\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003e_repository\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eLoad\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eid\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ecustomer\u003c/span\u003e \u003cspan class=\"p\"\u003e!=\u003c/span\u003e \u003cspan class=\"kc\"\u003enull\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003e_cache\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eCount\u003c/span\u003e \u003cspan class=\"p\"\u003e\u0026gt;=\u003c/span\u003e \u003cspan class=\"n\"\u003e_maxCacheSize\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"n\"\u003eEvictOldestEntry\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003e_cache\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"n\"\u003eid\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003enew\u003c/span\u003e \u003cspan class=\"n\"\u003eCacheEntry\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eCustomer\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;(\u003c/span\u003e\u003cspan class=\"n\"\u003ecustomer\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eDateTimeOffset\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eUtcNow\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003ecustomer\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eCode didn\u0026rsquo;t complicate a simple idea—it revealed that the idea was never actually simple. Abstract thinking deferred decisions about memory, concurrency, staleness, observability, and eviction. Code forced those decisions into concrete form where consequences become visible and measurable.\u003c/p\u003e\n\u003cp\u003eThis is not implementation detail obscuring elegant design. This is reality asserting itself.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"what-comes-next-the-feedback-loop-ai-cannot-replicate\"\u003e\u003ca href=\"/posts/code-sharpens-thinking/#what-comes-next-the-feedback-loop-ai-cannot-replicate\" title=\"What Comes Next: The Feedback Loop AI Cannot Replicate\"\u003eWhat Comes Next: The Feedback Loop AI Cannot Replicate\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eAI-generated code without understanding creates productivity illusions that collapse in production. Code forces abstract thinking into executable form, exposing gaps that pure reasoning glosses over. That much is clear.\u003c/p\u003e\n\u003cp\u003eBut understanding the problem doesn\u0026rsquo;t answer the deeper question: What exactly is this feedback loop between code and reality, and why can\u0026rsquo;t AI replicate it? What mechanisms transform vague reasoning into concrete understanding?\u003c/p\u003e\n\u003cp\u003eThe answer lies in the tools we use every day: compilers, profilers, tests, production environments. These aren\u0026rsquo;t just validation gates. They\u0026rsquo;re \u003cstrong\u003ereality engines\u003c/strong\u003e that do something AI fundamentally cannot: they execute your assumptions against actual constraints and report back with unfiltered truth.\u003c/p\u003e\n\u003cp\u003e\u003cem\u003eIn the next part of this series, we\u0026rsquo;ll explore how these mechanisms form a cognitive feedback loop that sharpens professional thinking in ways no AI prompt can simulate.\u003c/em\u003e\u003c/p\u003e\n","date_modified":"2026-05-26T10:22:03+02:00","date_published":"2026-01-06T17:00:00+01:00","id":"https://daily-devops.net/posts/code-sharpens-thinking/","language":"en","summary":"Typing code is trivial now—AI does it instantly. So why will real professionals never be replaced? Because vibe coding collapses under production reality.\n","tags":["softwareengineering","codequality","bestpractices","architecture","dotnet","csharp","technicaldebt","ai-code-assistant","github-copilot"],"title":"Why Real Professionals Will Never Be Replaced by AI\n","url":"https://daily-devops.net/posts/code-sharpens-thinking/"},{"authors":[{"name":"Martin Stühmer","url":"https://daily-devops.net/authors/martin/"}],"content_html":"\u003cp\u003e\u003cem\u003e\u003cstrong\u003eHappy New Year 2026! 🎉\u003c/strong\u003e\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003eSkip the generic wishes. My wish: fix the technical debt you\u0026rsquo;ve been promising since 2023. Stop telling yourself it will happen \u003cem\u003enext quarter.\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003eEvery January, the same ritual. Sprint planning. Someone mentions that problematic module—you know the one. \u0026ldquo;We\u0026rsquo;ll refactor it next quarter,\u0026rdquo; they say. Ticket created. Backlog updated.\u003c/p\u003e\n\u003cp\u003eBy mid-January, forgotten.\u003c/p\u003e\n\u003cp\u003eI\u0026rsquo;ve taught enough .NET courses and consulted with enough teams to know: Everyone has technical debt. The Fortune 500 companies have it. The startups have it. You have it. The difference between teams that succeed in 2026 and teams that burn out isn\u0026rsquo;t whether they have technical debt—it\u0026rsquo;s whether they\u0026rsquo;re honest about it.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"why-next-quarter-never-comes\"\u003e\u003ca href=\"/posts/happy-new-year-2026/#why-next-quarter-never-comes\" title=\"Why Next Quarter Never Comes\"\u003eWhy \u003cem\u003eNext Quarter\u003c/em\u003e Never Comes\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eThe pattern is always the same. A feature ships. It works—barely. \u0026ldquo;We\u0026rsquo;ll clean this up next quarter,\u0026rdquo; someone says. The team knows it\u0026rsquo;s a lie. Management knows it\u0026rsquo;s a lie. Everyone pretends anyway.\u003c/p\u003e\n\u003cp\u003eWhy? Because admitting you\u0026rsquo;re building on a foundation of compromises feels like failure. It\u0026rsquo;s not. It\u0026rsquo;s reality. But we\u0026rsquo;d rather maintain the fiction.\u003c/p\u003e\n\u003cp\u003eTemporary solutions become permanent infrastructure. That \u0026ldquo;quick integration\u0026rdquo; from 2019 is now mission-critical and touches everything. The developer who wrote it left in 2021. The documentation? Nonexistent.\u003c/p\u003e\n\u003cp\u003eThis compounds. Every shortcut builds on the previous shortcut. Every \u0026ldquo;we\u0026rsquo;ll fix it later\u0026rdquo; adds to the pile. Fast forward to 2026, and you\u0026rsquo;re spending more time working around bad decisions than you would have spent making good ones.\u003c/p\u003e\n\u003cp\u003eTechnical debt feels free when you create it. Your sprint metrics look great. You shipped the feature. Everyone\u0026rsquo;s happy.\u003c/p\u003e\n\u003cp\u003eEighteen months later, that code is load-bearing. Developers quit because debugging incomprehensible code is soul-crushing. Your velocity drops. The business wonders why. You can\u0026rsquo;t admit the foundation is crumbling.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"every-january-same-promises\"\u003e\u003ca href=\"/posts/happy-new-year-2026/#every-january-same-promises\" title=\"Every January, Same Promises\"\u003eEvery January, Same Promises\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eI teach .NET courses for students and apprentices. Every year, developers tell me about the refactoring they\u0026rsquo;re planning.\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cem\u003eThis year we\u0026rsquo;ll finally add tests.\u003c/em\u003e\u003c/li\u003e\n\u003cli\u003e\u003cem\u003eThis year we\u0026rsquo;ll upgrade from .NET Framework 4.8.\u003c/em\u003e\u003c/li\u003e\n\u003cli\u003e\u003cem\u003eThis year we\u0026rsquo;ll split up that 4,000-line controller.\u003c/em\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eBy February, they\u0026rsquo;re back to shipping features. The tests? Still at 12% coverage. The Framework migration? Still \u0026ldquo;risky.\u0026rdquo; The controller? Now 4,300 lines.\u003c/p\u003e\n\u003cp\u003eHere\u0026rsquo;s the code everyone writes on January 1st:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eOrderProcessor\u003c/span\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e// TODO 2023: Refactor this - too much responsibility\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e// TODO 2024: Seriously, we need to split this up\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e// TODO 2025: I\u0026#39;m not even joking anymore\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e// TODO 2026: Kill me\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003eprivate\u003c/span\u003e \u003cspan class=\"kd\"\u003estatic\u003c/span\u003e \u003cspan class=\"n\"\u003eDictionary\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eOrderState\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003e_stateCache\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003enew\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e \u003cspan class=\"c1\"\u003e// Race condition central\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kd\"\u003easync\u003c/span\u003e \u003cspan class=\"n\"\u003eTask\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"kt\"\u003ebool\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003eProcessOrder\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eOrder\u003c/span\u003e \u003cspan class=\"n\"\u003eorder\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"c1\"\u003e// CA2007 warning since 2022, still ignored\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"c1\"\u003e// Checking null in 2026 like it\u0026#39;s 2015\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eorder\u003c/span\u003e \u003cspan class=\"p\"\u003e==\u003c/span\u003e \u003cspan class=\"kc\"\u003enull\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"kc\"\u003efalse\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003eresult\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003eSaveToDatabase\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eorder\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e \u003cspan class=\"c1\"\u003e// Deadlock count: 23 and climbing\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003eSendEmail\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eorder\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eCustomer\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eEmail\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e \u003cspan class=\"c1\"\u003e// NullReferenceException #47 this month\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003eresult\u003c/span\u003e \u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"m\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThose CA2007 warnings from ConfigureAwait? Been there since you upgraded to .NET Core 3.1 in 2020. You keep meaning to fix them. You suppress them instead because \u0026ldquo;we\u0026rsquo;ll do it properly in the refactoring.\u0026rdquo;\u003c/p\u003e\n\u003cp\u003eThis isn\u0026rsquo;t developer failure. It\u0026rsquo;s organizational reality. You can only refactor when the business prioritizes it (rarely happens), you have time between features (almost never), you\u0026rsquo;re not fighting production fires (frequently untrue), and nobody\u0026rsquo;s pressuring you to ship faster (never).\u003c/p\u003e\n\u003cp\u003eThe system ensures technical debt is always someone else\u0026rsquo;s problem. Always scheduled for later. Later never arrives.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"why-technical-debt-compounds-like-credit-card-interest\"\u003e\u003ca href=\"/posts/happy-new-year-2026/#why-technical-debt-compounds-like-credit-card-interest\" title=\"Why Technical Debt Compounds Like Credit Card Interest\"\u003eWhy Technical Debt Compounds Like Credit Card Interest\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eYour 2026 codebase reflects every shortcut from 2025. Every \u0026quot;we\u0026rsquo;ll fix it later.\u0026quot; Every suppressed warning. Every test you didn\u0026rsquo;t write. Cause and effect.\u003c/p\u003e\n\u003cp\u003eMost teams think they\u0026rsquo;re trading speed for quality. That\u0026rsquo;s not even wrong—it\u0026rsquo;s nonsense disguised as pragmatism. You\u0026rsquo;re choosing whether to pay now or pay later with interest. Later always costs more.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"the-cost-nobody-tracks\"\u003e\u003ca href=\"/posts/happy-new-year-2026/#the-cost-nobody-tracks\" title=\"The Cost Nobody Tracks\"\u003eThe Cost Nobody Tracks\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eTechnical debt is a business decision. Treat it like one.\u003c/p\u003e\n\u003cp\u003eWriting code fast but wrong costs more than writing it right. The 3 AM production incident. The Friday afternoon rollback. The six hours debugging something that proper async patterns would have prevented.\u003c/p\u003e\n\u003cp\u003eThese costs don\u0026rsquo;t show up in sprint reports. They show up in exhausted developers, missed deadlines, and customer incidents.\u003c/p\u003e\n\u003cp\u003eFeatures that should take days take weeks because the codebase fights you. Every change risks breaking something unrelated. \u0026ldquo;Just be careful\u0026rdquo; doesn\u0026rsquo;t scale.\u003c/p\u003e\n\u003cp\u003eDevelopers quit. Not because of salary. Because maintaining incomprehensible code destroys your soul. That new hire still lost after six months? Your architecture is the problem.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"what-actually-works-from-15-years-of-watching-teams-fail\"\u003e\u003ca href=\"/posts/happy-new-year-2026/#what-actually-works-from-15-years-of-watching-teams-fail\" title=\"What Actually Works (From 15 Years of Watching Teams Fail)\"\u003eWhat Actually Works (From 15 Years of Watching Teams Fail)\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eSustainable doesn\u0026rsquo;t mean perfect. It means the codebase doesn\u0026rsquo;t actively fight you.\u003c/p\u003e\n\u003cp\u003eWrite tests because they save debugging time, not because some \u0026ldquo;best practices\u0026rdquo; document says to. I\u0026rsquo;ve watched developers spend three days tracking down a bug that a 15-line unit test would have caught in three seconds. That\u0026rsquo;s not a best practice—that\u0026rsquo;s basic economics.\u003c/p\u003e\n\u003cp\u003eRefactor as you go. Not in some mythical future sprint. When you\u0026rsquo;re in a file and you see garbage code, fix it then. Yes, even if it\u0026rsquo;s \u0026ldquo;out of scope.\u0026rdquo; Especially if it\u0026rsquo;s out of scope. The Boy Scout Rule isn\u0026rsquo;t a suggestion—it\u0026rsquo;s how you avoid code rot.\u003c/p\u003e\n\u003cp\u003ePush back on scope creep with data. \u0026ldquo;This will take three days with tests, one day without\u0026rdquo; is a lie everyone tells. It takes three days either way—you\u0026rsquo;re just choosing whether to spend them now or during the 2 AM production incident.\u003c/p\u003e\n\u003cp\u003eYour .NET project in 2026 has every tool needed to avoid this. Roslyn analyzers like \u003ca href=\"https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1062\" target=\"_blank\" rel=\"noopener external noreferrer\"\u003eCA1062\u003c/a\u003e catch null reference exceptions before they ship. \u003ca href=\"https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2007\" target=\"_blank\" rel=\"noopener external noreferrer\"\u003eCA2007\u003c/a\u003e prevents ConfigureAwait deadlocks automatically. In my MCT courses, I enable these analyzers on legacy projects. Within hours, they\u0026rsquo;ve found bugs that lived in production for years.\u003c/p\u003e\n\u003cp\u003eNUnit\u0026rsquo;s parameterized tests let you cover edge cases in three lines. C# 12\u0026rsquo;s primary constructors eliminate the boilerplate nobody ever tested. .NET 9\u0026rsquo;s performance improvements mean you can write cleaner code that\u0026rsquo;s also faster.\u003c/p\u003e\n\u003cp\u003eTools aren\u0026rsquo;t the problem. You can download Visual Studio 2022, enable all the analyzers, and catch 80% of common bugs before your first commit.\u003c/p\u003e\n\u003cp\u003eDiscipline is the problem.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"what-2026-actually-offers\"\u003e\u003ca href=\"/posts/happy-new-year-2026/#what-2026-actually-offers\" title=\"What 2026 Actually Offers\"\u003eWhat 2026 Actually Offers\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003e2026 isn\u0026rsquo;t special. .NET 9 is mature and stable now—shipped November 2024. C# 13 brought some nice features. The ecosystem keeps improving.\u003c/p\u003e\n\u003cp\u003eBut here\u0026rsquo;s what matters: The tools to build maintainable software have been available for years. Roslyn analyzers. Testing frameworks. Structured logging. Observability tools. None of this is new.\u003c/p\u003e\n\u003cp\u003eThe bottleneck was never tooling. It\u0026rsquo;s discipline.\u003c/p\u003e\n\u003cp\u003eWhat makes 2026 different? Nothing, unless you decide it is.\u003c/p\u003e\n\u003cp\u003eYou can start the year like every other year—good intentions, abandoned by February. Or you can actually change something.\u003c/p\u003e\n\u003cp\u003eNot by adopting the newest framework. Not by rewriting everything in the latest architectural pattern. By making the unsexy choice: Fix one thing at a time. Add tests as you go. Enable analyzers. Refactor when you touch code, not \u0026ldquo;next quarter.\u0026rdquo;\u003c/p\u003e\n\u003cp\u003e.NET 9\u0026rsquo;s \u003ca href=\"https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-9/\" target=\"_blank\" rel=\"noopener external noreferrer\"\u003eperformance improvements\u003c/a\u003e are real—LINQ is faster, JSON serialization allocates less, the JIT is smarter. Migrating from .NET 6 or 8 is straightforward. Most teams can do it in days.\u003c/p\u003e\n\u003cp\u003eC# 13\u0026rsquo;s params collections and field keyword are fine. Use them where they help. Ignore them where they don\u0026rsquo;t.\u003c/p\u003e\n\u003cp\u003eAzure\u0026rsquo;s container and serverless offerings are stable now. Pick what fits your team\u0026rsquo;s expertise. Ignore what Hacker News says is \u0026ldquo;modern.\u0026rdquo;\u003c/p\u003e\n\u003cp\u003eAI integration will be 2026\u0026rsquo;s buzzword. Most will be snake oil. Some—like GitHub Copilot for boilerplate—actually helps. Don\u0026rsquo;t chase hype. Solve real problems.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"code-that-doesnt-wake-you-up-at-3-am\"\u003e\u003ca href=\"/posts/happy-new-year-2026/#code-that-doesnt-wake-you-up-at-3-am\" title=\"Code That Doesn\u0026rsquo;t Wake You Up at 3 AM\"\u003eCode That Doesn\u0026rsquo;t Wake You Up at 3 AM\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eIntentional development looks boring. No clever patterns. No abstraction for abstraction\u0026rsquo;s sake. Just code that works and can be debugged when it doesn\u0026rsquo;t.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eusing\u003c/span\u003e \u003cspan class=\"nn\"\u003eMicrosoft.Extensions.Logging\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eusing\u003c/span\u003e \u003cspan class=\"nn\"\u003eSystem.Diagnostics\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eCustomerOrderService\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003eprivate\u003c/span\u003e \u003cspan class=\"k\"\u003ereadonly\u003c/span\u003e \u003cspan class=\"n\"\u003eILogger\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eCustomerOrderService\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003e_logger\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003eprivate\u003c/span\u003e \u003cspan class=\"k\"\u003ereadonly\u003c/span\u003e \u003cspan class=\"n\"\u003eActivitySource\u003c/span\u003e \u003cspan class=\"n\"\u003e_activitySource\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003eprivate\u003c/span\u003e \u003cspan class=\"k\"\u003ereadonly\u003c/span\u003e \u003cspan class=\"n\"\u003eIOrderRepository\u003c/span\u003e \u003cspan class=\"n\"\u003e_repository\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"n\"\u003eCustomerOrderService\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003eILogger\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eCustomerOrderService\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003elogger\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003eActivitySource\u003c/span\u003e \u003cspan class=\"n\"\u003eactivitySource\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003eIOrderRepository\u003c/span\u003e \u003cspan class=\"n\"\u003erepository\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003e_logger\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003elogger\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003e_activitySource\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eactivitySource\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003e_repository\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003erepository\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kd\"\u003easync\u003c/span\u003e \u003cspan class=\"n\"\u003eTask\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eOrderResult\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003eProcessOrder\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003eOrderRequest\u003c/span\u003e \u003cspan class=\"n\"\u003erequest\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003eCancellationToken\u003c/span\u003e \u003cspan class=\"n\"\u003ecancellationToken\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"c1\"\u003e// OpenTelemetry distributed tracing - you\u0026#39;ll thank me during the incident\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003eusing\u003c/span\u003e \u003cspan class=\"nn\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003eactivity\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003e_activitySource\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eStartActivity\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;ProcessOrder\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003eactivity\u003c/span\u003e\u003cspan class=\"p\"\u003e?.\u003c/span\u003e\u003cspan class=\"n\"\u003eSetTag\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;order.id\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003erequest\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eOrderId\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003eactivity\u003c/span\u003e\u003cspan class=\"p\"\u003e?.\u003c/span\u003e\u003cspan class=\"n\"\u003eSetTag\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;customer.id\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003erequest\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eCustomerId\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003e_logger\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eLogInformation\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"s\"\u003e\u0026#34;Processing order {OrderId} for customer {CustomerId}\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"n\"\u003erequest\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eOrderId\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"n\"\u003erequest\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eCustomerId\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"c1\"\u003e// CA2007 compliant - no deadlocks in ASP.NET synchronization context\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003evalidationResult\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003eValidateOrder\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003erequest\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003ecancellationToken\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eConfigureAwait\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kc\"\u003efalse\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(!\u003c/span\u003e\u003cspan class=\"n\"\u003evalidationResult\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eIsValid\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"c1\"\u003e// Structured logging means you can query this in Application Insights\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"n\"\u003e_logger\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eLogWarning\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"s\"\u003e\u0026#34;Order validation failed for {OrderId}: {ValidationErrors}\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"n\"\u003erequest\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eOrderId\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"kt\"\u003estring\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eJoin\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;, \u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003evalidationResult\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eErrors\u003c/span\u003e\u003cspan class=\"p\"\u003e));\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"n\"\u003eactivity\u003c/span\u003e\u003cspan class=\"p\"\u003e?.\u003c/span\u003e\u003cspan class=\"n\"\u003eSetStatus\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eActivityStatusCode\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eError\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;Validation failed\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003eOrderResult\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eValidationFailed\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003evalidationResult\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eErrors\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003eorder\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003e_repository\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eSaveOrder\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003evalidationResult\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eOrder\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003ecancellationToken\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eConfigureAwait\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kc\"\u003efalse\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003eactivity\u003c/span\u003e\u003cspan class=\"p\"\u003e?.\u003c/span\u003e\u003cspan class=\"n\"\u003eSetTag\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;order.total\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eorder\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eTotalAmount\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003eOrderResult\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eSuccess\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eorder\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis is production code, adapted from a real order-processing system. Nothing fancy. Dependency injection makes it testable—I can mock \u003ccode\u003eIOrderRepository\u003c/code\u003e in unit tests. Structured logging means when something breaks at 3 AM, I can find it in Azure Application Insights in thirty seconds instead of thirty minutes. OpenTelemetry gives me distributed traces across services. ConfigureAwait prevents the deadlocks that plagued the previous version.\u003c/p\u003e\n\u003cp\u003eIt\u0026rsquo;s not clever. It\u0026rsquo;s reliable. After fifteen years, I\u0026rsquo;ll take reliable over clever every single time.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"making-2026-different\"\u003e\u003ca href=\"/posts/happy-new-year-2026/#making-2026-different\" title=\"Making 2026 Different\"\u003eMaking 2026 Different\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eLearning new C# features isn\u0026rsquo;t hard. Optimizing Azure costs isn\u0026rsquo;t hard. Discipline is hard.\u003c/p\u003e\n\u003cp\u003eSaying no when a VP wants a feature that solves no real problem. Budgeting time for maintenance and defending it. Writing tests when nobody\u0026rsquo;s watching. Code reviewing properly when you\u0026rsquo;re swamped. Having uncomfortable conversations about quality.\u003c/p\u003e\n\u003cp\u003eMeasuring what matters: incident rates, recovery time, PR review duration, developer retention. Not just velocity.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"what-january-looks-like-for-most-teams\"\u003e\u003ca href=\"/posts/happy-new-year-2026/#what-january-looks-like-for-most-teams\" title=\"What January Looks Like for Most Teams\"\u003eWhat January Looks Like for Most Teams\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eJanuary: \u0026ldquo;This year will be different.\u0026rdquo;\u003c/p\u003e\n\u003cp\u003eFebruary: Feature roadmap consumed the quarter.\u003c/p\u003e\n\u003cp\u003eMarch: Production incident. All hands on deck.\u003c/p\u003e\n\u003cp\u003eApril-December: Repeat.\u003c/p\u003e\n\u003cp\u003eDiscipline is hard because it requires saying no to immediate pressure for long-term stability. The pressure is real. The meetings asking \u0026ldquo;why so long\u0026rdquo; are real. The 5 PM Friday Slack messages are real.\u003c/p\u003e\n\u003cp\u003eTeams that survive aren\u0026rsquo;t smarter. They\u0026rsquo;re not using better frameworks. They have organizational support for saying no. Or they\u0026rsquo;re stubborn enough to do it anyway.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"what-winning-in-2026-looks-like\"\u003e\u003ca href=\"/posts/happy-new-year-2026/#what-winning-in-2026-looks-like\" title=\"What Winning in 2026 Looks Like\"\u003eWhat Winning in 2026 Looks Like\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eShip fewer features that work, instead of many features that half work.\u003c/p\u003e\n\u003cp\u003eIncident rates go down. Developers stay. You can respond to market changes because you\u0026rsquo;re not buried in debt.\u003c/p\u003e\n\u003cp\u003eNot exciting. Pragmatic.\u003c/p\u003e\n\u003cp\u003eSo here\u0026rsquo;s my actual New Year wish for you: Stop lying to yourself about \u0026ldquo;next quarter.\u0026rdquo; Fix one thing this week. Enable one analyzer. Write one test. Refactor one function.\u003c/p\u003e\n\u003cp\u003eNot next quarter. This week.\u003c/p\u003e\n\u003cp\u003eThat\u0026rsquo;s how software survives to 2027.\u003c/p\u003e\n","date_modified":"2026-05-26T10:22:03+02:00","date_published":"2026-01-01T14:00:00+01:00","id":"https://daily-devops.net/posts/happy-new-year-2026/","language":"en","summary":"Stop promising to fix technical debt next quarter. .NET 10, analyzers, and tests are ready in 2026; only the engineering discipline is missing.","tags":["technicaldebt","dotnet","csharp","softwareengineering","codequality"],"title":"Most Software Teams Are Lying to Themselves—2026 Needs to Be Different","url":"https://daily-devops.net/posts/happy-new-year-2026/"},{"authors":[{"name":"Martin Stühmer","url":"https://daily-devops.net/authors/martin/"}],"content_html":"\u003cp\u003eWhen someone handed me \u003ca href=\"https://spinroot.com/gerard/pdf/P10.pdf\" target=\"_blank\" rel=\"noopener external noreferrer\"\u003eGerard Holzmann\u0026rsquo;s \u0026ldquo;Power of Ten\u0026rdquo;\u003c/a\u003e rules and asked whether they still apply to C# 10/.NET, my answer was immediate: Absolutely, and we can enforce them better than Gerard Holzmann could have dreamed in 2006. The Power of Ten originated at NASA\u0026rsquo;s Jet Propulsion Laboratory for safety-critical C code in spacecraft systems. Not academic theory—principles where violations kill people. NASA\u0026rsquo;s \u003ca href=\"https://users.ece.cmu.edu/~koopman/pubs/koopman14_toyota_ua_slides.pdf\" target=\"_blank\" rel=\"noopener external noreferrer\"\u003e2010 technical assessment of Toyota\u0026rsquo;s electronic throttle control\u003c/a\u003e found 243 violations contributing to unintended acceleration deaths. When prosecutors examine catastrophic embedded failures, these ten rules are the baseline for \u0026ldquo;did you even try?\u0026rdquo;\u003c/p\u003e\n\u003cp\u003eWhat\u0026rsquo;s changed in two decades? In 2006, following these rules meant discipline, manual code reviews, and static analysis tools at $5,000 per seat. You verified bounded loops by hand, tracked pointer indirection with spreadsheets, enforced function length through policy documents nobody read. I\u0026rsquo;ve reviewed enough legacy embedded code to know most teams failed. The tooling wasn\u0026rsquo;t there, and manual enforcement doesn\u0026rsquo;t scale past three developers. Modern C# flips this completely. Roslyn analyzers catch violations at compile time—before code review, before testing, before anyone debugs at 2 AM wondering why the production system locked up. Nullable reference types enforce null-safety that C developers achieved through religious discipline and hope. The type system makes pointer arithmetic bugs physically impossible. Built into the compiler, zero additional cost, enforced automatically.\u003c/p\u003e\n\u003cp\u003eThe catch—and there\u0026rsquo;s always a catch—is that not every rule translates directly. The managed runtime fundamentally rewrites what\u0026rsquo;s dangerous and what\u0026rsquo;s safe. Dynamic allocation is forbidden in embedded systems but powers every .NET framework feature. Recursion crashes spacecraft but handles expression trees beautifully when the CLR manages your stack. The real question isn\u0026rsquo;t \u0026ldquo;do these rules apply to C#?\u0026rdquo; It\u0026rsquo;s \u0026ldquo;how do modern language features enforce the underlying principles better than 2006 C ever could?\u0026rdquo; Let\u0026rsquo;s examine each rule systematically.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"rule-1-avoid-complex-control-flow-no-goto-setjmplongjmp-recursion\"\u003e\u003ca href=\"/posts/dotnet-power-of-ten-rules/#rule-1-avoid-complex-control-flow-no-goto-setjmplongjmp-recursion\" title=\"Rule 1: Avoid Complex Control Flow (No goto, setjmp/longjmp, Recursion)\"\u003eRule 1: Avoid Complex Control Flow (No goto, setjmp/longjmp, Recursion)\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003eOriginal Intent:\u003c/strong\u003e Simplify static analysis and prevent unpredictable control flow in embedded systems.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eC#/.NET Applicability:\u003c/strong\u003e Partially valid, but context matters.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"what-still-applies\"\u003e\u003ca href=\"/posts/dotnet-power-of-ten-rules/#what-still-applies\" title=\"What Still Applies\"\u003eWhat Still Applies\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003e\u003ccode\u003egoto\u003c/code\u003e remains controversial in C#, though the language supports it. I\u0026rsquo;ve seen developers defend it passionately in code reviews, and honestly, for breaking out of deeply nested loops, it\u0026rsquo;s occasionally the least-bad option. But \u0026ldquo;occasionally\u0026rdquo; is doing a lot of work in that sentence:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// Acceptable use case: breaking out of nested loops\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003eprivate\u003c/span\u003e \u003cspan class=\"kt\"\u003ebool\u003c/span\u003e \u003cspan class=\"n\"\u003eTryFindPosition\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e\u003cspan class=\"p\"\u003e[,]\u003c/span\u003e \u003cspan class=\"n\"\u003ematrix\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"n\"\u003etarget\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"k\"\u003eout\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"n\"\u003erow\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"n\"\u003ecol\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"n\"\u003eposition\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003efor\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"n\"\u003ei\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"m\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"n\"\u003ei\u003c/span\u003e \u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e \u003cspan class=\"n\"\u003erows\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"n\"\u003ei\u003c/span\u003e\u003cspan class=\"p\"\u003e++)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003efor\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"n\"\u003ej\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"m\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"n\"\u003ej\u003c/span\u003e \u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e \u003cspan class=\"n\"\u003ecols\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"n\"\u003ej\u003c/span\u003e\u003cspan class=\"p\"\u003e++)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ematrix\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"n\"\u003ei\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003ej\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e \u003cspan class=\"p\"\u003e==\u003c/span\u003e \u003cspan class=\"n\"\u003etarget\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"k\"\u003egoto\u003c/span\u003e \u003cspan class=\"n\"\u003efound\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"kc\"\u003efalse\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003efound\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eBut this is clearer:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// Better: extract to method with early return\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003eprivate\u003c/span\u003e \u003cspan class=\"kt\"\u003ebool\u003c/span\u003e \u003cspan class=\"n\"\u003eTryFindPosition\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e\u003cspan class=\"p\"\u003e[,]\u003c/span\u003e \u003cspan class=\"n\"\u003ematrix\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"n\"\u003etarget\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"k\"\u003eout\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"n\"\u003erow\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"n\"\u003ecol\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"n\"\u003eposition\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003efor\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"n\"\u003ei\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"m\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"n\"\u003ei\u003c/span\u003e \u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e \u003cspan class=\"n\"\u003ematrix\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eGetLength\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"m\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e \u003cspan class=\"n\"\u003ei\u003c/span\u003e\u003cspan class=\"p\"\u003e++)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003efor\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"n\"\u003ej\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"m\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"n\"\u003ej\u003c/span\u003e \u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e \u003cspan class=\"n\"\u003ematrix\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eGetLength\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"m\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e \u003cspan class=\"n\"\u003ej\u003c/span\u003e\u003cspan class=\"p\"\u003e++)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ematrix\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"n\"\u003ei\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003ej\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e \u003cspan class=\"p\"\u003e==\u003c/span\u003e \u003cspan class=\"n\"\u003etarget\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"n\"\u003eposition\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ei\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003ej\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eposition\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003edefault\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"kc\"\u003efalse\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\n\n\n\u003ch3 id=\"whats-different\"\u003e\u003ca href=\"/posts/dotnet-power-of-ten-rules/#whats-different\" title=\"What\u0026rsquo;s Different\"\u003eWhat\u0026rsquo;s Different\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003e\u003cstrong\u003eRecursion\u003c/strong\u003e tells a completely different story. The JIT compiler doesn\u0026rsquo;t guarantee tail-call optimization like F# does. Still valuable for tree structures, parsing, algorithms where iterative alternatives become unreadable spaghetti. Key difference? Stack overflows throw exceptions you can catch and handle. In embedded C, stack overflow crashes the spacecraft. No recovery, no logging, just silence.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// Recursion is perfectly acceptable for bounded structures\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"n\"\u003eTreeNode\u003c/span\u003e\u003cspan class=\"p\"\u003e?\u003c/span\u003e \u003cspan class=\"n\"\u003eFind\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eTreeNode\u003c/span\u003e\u003cspan class=\"p\"\u003e?\u003c/span\u003e \u003cspan class=\"n\"\u003enode\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"k\"\u003evalue\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003enode\u003c/span\u003e \u003cspan class=\"p\"\u003e==\u003c/span\u003e \u003cspan class=\"kc\"\u003enull\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"kc\"\u003enull\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003enode\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eValue\u003c/span\u003e \u003cspan class=\"p\"\u003e==\u003c/span\u003e \u003cspan class=\"k\"\u003evalue\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003enode\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003eFind\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003enode\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eLeft\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"k\"\u003evalue\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e??\u003c/span\u003e \u003cspan class=\"n\"\u003eFind\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003enode\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eRight\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"k\"\u003evalue\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eFor deep recursion, modern C# offers alternatives:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// Convert to iteration with explicit stack\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"n\"\u003eTreeNode\u003c/span\u003e\u003cspan class=\"p\"\u003e?\u003c/span\u003e \u003cspan class=\"n\"\u003eFind\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eTreeNode\u003c/span\u003e\u003cspan class=\"p\"\u003e?\u003c/span\u003e \u003cspan class=\"n\"\u003enode\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"k\"\u003evalue\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003enode\u003c/span\u003e \u003cspan class=\"p\"\u003e==\u003c/span\u003e \u003cspan class=\"kc\"\u003enull\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"kc\"\u003enull\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003estack\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003enew\u003c/span\u003e \u003cspan class=\"n\"\u003eStack\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eTreeNode\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003estack\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ePush\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003enode\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003ewhile\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003estack\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eCount\u003c/span\u003e \u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"m\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003ecurrent\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003estack\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ePop\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ecurrent\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eValue\u003c/span\u003e \u003cspan class=\"p\"\u003e==\u003c/span\u003e \u003cspan class=\"k\"\u003evalue\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003ecurrent\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ecurrent\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eRight\u003c/span\u003e \u003cspan class=\"p\"\u003e!=\u003c/span\u003e \u003cspan class=\"kc\"\u003enull\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"n\"\u003estack\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ePush\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ecurrent\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eRight\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ecurrent\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eLeft\u003c/span\u003e \u003cspan class=\"p\"\u003e!=\u003c/span\u003e \u003cspan class=\"kc\"\u003enull\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"n\"\u003estack\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ePush\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ecurrent\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eLeft\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"kc\"\u003enull\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cstrong\u003eVerdict:\u003c/strong\u003e Use recursion where it makes sense, but be aware of stack depth. Avoid \u003ccode\u003egoto\u003c/code\u003e unless you have a compelling reason. The .NET runtime gives you safety nets that embedded C never had.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"rule-2-all-loops-must-have-fixed-upper-bounds\"\u003e\u003ca href=\"/posts/dotnet-power-of-ten-rules/#rule-2-all-loops-must-have-fixed-upper-bounds\" title=\"Rule 2: All Loops Must Have Fixed Upper Bounds\"\u003eRule 2: All Loops Must Have Fixed Upper Bounds\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003eOriginal Intent:\u003c/strong\u003e Enable static analysis to prove termination and prevent infinite loops.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eC#/.NET Applicability:\u003c/strong\u003e The principle is sound, but enforcement differs dramatically.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"modern-interpretation\"\u003e\u003ca href=\"/posts/dotnet-power-of-ten-rules/#modern-interpretation\" title=\"Modern Interpretation\"\u003eModern Interpretation\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eProvable termination is still the goal, but .NET gives you runtime safety nets that embedded C never had:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// Bad: unbounded loop\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003ewhile\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003eitem\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003equeue\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eDequeue\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e \u003cspan class=\"c1\"\u003e// What if queue is empty?\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eProcess\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eitem\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// Better: explicit bound with timeout\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003ects\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003enew\u003c/span\u003e \u003cspan class=\"n\"\u003eCancellationTokenSource\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eTimeSpan\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eFromMinutes\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"m\"\u003e5\u003c/span\u003e\u003cspan class=\"p\"\u003e));\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003ewhile\u003c/span\u003e \u003cspan class=\"p\"\u003e(!\u003c/span\u003e\u003cspan class=\"n\"\u003ects\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eToken\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eIsCancellationRequested\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003equeue\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eTryDequeue\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"k\"\u003eout\u003c/span\u003e \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003eitem\u003c/span\u003e\u003cspan class=\"p\"\u003e))\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003eProcess\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eitem\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eelse\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003eTask\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eDelay\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"m\"\u003e100\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003ects\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eToken\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eModern .NET provides powerful static analysis through \u003ca href=\"https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/overview\" target=\"_blank\" rel=\"noopener external noreferrer\"\u003eRoslyn analyzers\u003c/a\u003e that can detect unbounded loops during compilation. Enable nullable reference types and the latest analysis level to catch these issues early:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-xml\" data-lang=\"xml\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003e\u0026lt;PropertyGroup\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nt\"\u003e\u0026lt;Nullable\u0026gt;\u003c/span\u003eenable\u003cspan class=\"nt\"\u003e\u0026lt;/Nullable\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nt\"\u003e\u0026lt;AnalysisLevel\u0026gt;\u003c/span\u003elatest\u003cspan class=\"nt\"\u003e\u0026lt;/AnalysisLevel\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003e\u0026lt;/PropertyGroup\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cstrong\u003eVerdict:\u003c/strong\u003e Always use bounded loops. Modern C# makes this easier with \u003ccode\u003eforeach\u003c/code\u003e, LINQ\u0026rsquo;s \u003ccode\u003eTake()\u003c/code\u003e, and cancellation tokens. The difference from embedded C: Your loops can be bounded at runtime with safe defaults rather than requiring compile-time constants.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"rule-3-no-dynamic-memory-allocation-after-initialization\"\u003e\u003ca href=\"/posts/dotnet-power-of-ten-rules/#rule-3-no-dynamic-memory-allocation-after-initialization\" title=\"Rule 3: No Dynamic Memory Allocation After Initialization\"\u003eRule 3: No Dynamic Memory Allocation After Initialization\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003eOriginal Intent:\u003c/strong\u003e Prevent memory exhaustion and fragmentation in systems without virtual memory.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eC#/.NET Applicability:\u003c/strong\u003e Fundamentally incompatible with .NET\u0026rsquo;s design philosophy.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"why-this-rule-doesnt-translate\"\u003e\u003ca href=\"/posts/dotnet-power-of-ten-rules/#why-this-rule-doesnt-translate\" title=\"Why This Rule Doesn\u0026rsquo;t Translate\"\u003eWhy This Rule Doesn\u0026rsquo;t Translate\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eThe .NET garbage collector exists precisely to enable safe dynamic allocation. Forbidding dynamic allocation in .NET is like forbidding breathing—every framework feature depends on it:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// Standard .NET patterns rely on GC\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003eresults\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003ehttpClient\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eGetStringAsync\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eurl\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003eparsed\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eJsonSerializer\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eDeserialize\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eMyData\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;(\u003c/span\u003e\u003cspan class=\"n\"\u003eresults\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003efiltered\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eparsed\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eItems\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eWhere\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ex\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003ex\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eIsActive\u003c/span\u003e\u003cspan class=\"p\"\u003e).\u003c/span\u003e\u003cspan class=\"n\"\u003eToList\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\n\n\n\u003ch3 id=\"the-modern-equivalent-minimize-allocations\"\u003e\u003ca href=\"/posts/dotnet-power-of-ten-rules/#the-modern-equivalent-minimize-allocations\" title=\"The Modern Equivalent: Minimize Allocations\"\u003eThe Modern Equivalent: Minimize Allocations\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eRather than avoiding allocation entirely, focus on reducing unnecessary allocations and GC pressure:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// Inefficient: allocates multiple intermediate strings\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kt\"\u003estring\u003c/span\u003e \u003cspan class=\"n\"\u003eresult\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003efor\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"n\"\u003ei\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"m\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"n\"\u003ei\u003c/span\u003e \u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e \u003cspan class=\"m\"\u003e1000\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"n\"\u003ei\u003c/span\u003e\u003cspan class=\"p\"\u003e++)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eresult\u003c/span\u003e \u003cspan class=\"p\"\u003e+=\u003c/span\u003e \u003cspan class=\"s\"\u003e$\u0026#34;Item {i}; \u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e  \u003cspan class=\"c1\"\u003e// Allocates new string each iteration\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// Efficient: single allocation, reusable buffer\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003esb\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003enew\u003c/span\u003e \u003cspan class=\"n\"\u003eStringBuilder\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ecapacity\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"m\"\u003e1000\u003c/span\u003e \u003cspan class=\"p\"\u003e*\u003c/span\u003e \u003cspan class=\"m\"\u003e15\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003efor\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"n\"\u003ei\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"m\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"n\"\u003ei\u003c/span\u003e \u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e \u003cspan class=\"m\"\u003e1000\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"n\"\u003ei\u003c/span\u003e\u003cspan class=\"p\"\u003e++)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003esb\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAppend\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;Item \u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003esb\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAppend\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ei\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003esb\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAppend\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;; \u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kt\"\u003estring\u003c/span\u003e \u003cspan class=\"n\"\u003eresult\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003esb\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eToString\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\n\n\n\u003ch3 id=\"stack-allocation-for-performance-critical-code\"\u003e\u003ca href=\"/posts/dotnet-power-of-ten-rules/#stack-allocation-for-performance-critical-code\" title=\"Stack Allocation for Performance-Critical Code\"\u003eStack Allocation for Performance-Critical Code\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eWhen allocation overhead matters, use \u003ccode\u003eSpan\u0026lt;T\u0026gt;\u003c/code\u003e with \u003ccode\u003estackalloc\u003c/code\u003e:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// Stack allocation for small, temporary buffers\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eSpan\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003ebuffer\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003estackalloc\u003c/span\u003e \u003cspan class=\"kt\"\u003eint\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"m\"\u003e256\u003c/span\u003e\u003cspan class=\"p\"\u003e];\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003efor\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"n\"\u003ei\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"m\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"n\"\u003ei\u003c/span\u003e \u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e \u003cspan class=\"n\"\u003ebuffer\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eLength\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"n\"\u003ei\u003c/span\u003e\u003cspan class=\"p\"\u003e++)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003ebuffer\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"n\"\u003ei\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eComputeValue\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ei\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eProcessData\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ebuffer\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cstrong\u003eCritical constraints:\u003c/strong\u003e Stack allocations come with three important limitations. First, analyzer rule \u003ca href=\"https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca2014\" target=\"_blank\" rel=\"noopener external noreferrer\"\u003eCA2014\u003c/a\u003e prevents using \u003ccode\u003estackalloc\u003c/code\u003e inside loops to avoid stack overflow. Second, keep allocations small (under 1KB recommended) since stack space is limited. Third, \u003ccode\u003eSpan\u0026lt;T\u0026gt;\u003c/code\u003e can\u0026rsquo;t escape method scope: attempting to return it produces compiler error \u003ca href=\"https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/compiler-messages/ref-safety-errors\" target=\"_blank\" rel=\"noopener external noreferrer\"\u003eCS8347\u003c/a\u003e. For data that needs to escape, use \u003ccode\u003eMemory\u0026lt;T\u0026gt;\u003c/code\u003e or arrays instead.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"object-pooling-for-hot-paths\"\u003e\u003ca href=\"/posts/dotnet-power-of-ten-rules/#object-pooling-for-hot-paths\" title=\"Object Pooling for Hot Paths\"\u003eObject Pooling for Hot Paths\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eFor high-throughput scenarios:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// Use ArrayPool for reusable buffers\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kt\"\u003ebyte\u003c/span\u003e\u003cspan class=\"p\"\u003e[]\u003c/span\u003e \u003cspan class=\"n\"\u003ebuffer\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eArrayPool\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"kt\"\u003ebyte\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;.\u003c/span\u003e\u003cspan class=\"n\"\u003eShared\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eRent\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"m\"\u003e4096\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003etry\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"n\"\u003ebytesRead\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003estream\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eReadAsync\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ebuffer\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAsMemory\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"m\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"m\"\u003e4096\u003c/span\u003e\u003cspan class=\"p\"\u003e));\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eProcessBytes\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ebuffer\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAsSpan\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"m\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003ebytesRead\u003c/span\u003e\u003cspan class=\"p\"\u003e));\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003efinally\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eArrayPool\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"kt\"\u003ebyte\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;.\u003c/span\u003e\u003cspan class=\"n\"\u003eShared\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eReturn\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ebuffer\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cstrong\u003eVerdict:\u003c/strong\u003e Don\u0026rsquo;t avoid allocation, manage it intelligently. Use \u003ccode\u003eSpan\u0026lt;T\u0026gt;\u003c/code\u003e/\u003ccode\u003eMemory\u0026lt;T\u0026gt;\u003c/code\u003e for performance-critical code, \u003ccode\u003eArrayPool\u0026lt;T\u0026gt;\u003c/code\u003e for reusable buffers, and profile before optimizing. I\u0026rsquo;ve watched teams waste months optimizing allocations that consumed 2% of execution time while ignoring the database query running on every keystroke. The GC is your friend, not your enemy—but measure before you fight it.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"rule-4-no-function-longer-than-one-printed-page-60-lines\"\u003e\u003ca href=\"/posts/dotnet-power-of-ten-rules/#rule-4-no-function-longer-than-one-printed-page-60-lines\" title=\"Rule 4: No Function Longer Than One Printed Page (~60 Lines)\"\u003eRule 4: No Function Longer Than One Printed Page (~60 Lines)\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003eOriginal Intent:\u003c/strong\u003e Keep functions digestible for human review and static analysis.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eC#/.NET Applicability:\u003c/strong\u003e Absolutely valid, possibly even more important.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"why-this-rule-still-matters\"\u003e\u003ca href=\"/posts/dotnet-power-of-ten-rules/#why-this-rule-still-matters\" title=\"Why This Rule Still Matters\"\u003eWhy This Rule Still Matters\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eIn 15 years across finance, healthcare, and industrial control systems, I\u0026rsquo;ve never once regretted breaking a long method into smaller pieces. I\u0026rsquo;ve regretted plenty of 500-line monsters—particularly the one in a reporting visualization that took three developers two weeks to debug because nobody could hold the entire state machine in their head:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// Bad: 250-line method doing everything\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kd\"\u003easync\u003c/span\u003e \u003cspan class=\"n\"\u003eTask\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eOrderResult\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003eProcessOrder\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eOrderRequest\u003c/span\u003e \u003cspan class=\"n\"\u003erequest\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e// Validate customer\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e// Check inventory\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e// Calculate pricing\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e// Apply discounts\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e// Process payment\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e// Update inventory\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e// Send notifications\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e// Log everything\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e// Handle 12 different error cases\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e// ... 200 more lines\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eModern C# actually makes this rule easier to enforce:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// Good: orchestration method with clear intent\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kd\"\u003easync\u003c/span\u003e \u003cspan class=\"n\"\u003eTask\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eOrderResult\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003eProcessOrder\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eOrderRequest\u003c/span\u003e \u003cspan class=\"n\"\u003erequest\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003ecustomer\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003eValidateCustomer\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003erequest\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eCustomerId\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003eavailability\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003eCheckInventory\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003erequest\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eItems\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(!\u003c/span\u003e\u003cspan class=\"n\"\u003eavailability\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAllAvailable\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003eOrderResult\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eOutOfStock\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eavailability\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eUnavailableItems\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003epricing\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eCalculatePricing\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003erequest\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eItems\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003ecustomer\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eTier\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003epayment\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003eProcessPayment\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ecustomer\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003epricing\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eTotal\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(!\u003c/span\u003e\u003cspan class=\"n\"\u003epayment\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eSucceeded\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003eOrderResult\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ePaymentFailed\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003epayment\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eReason\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003eUpdateInventory\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003erequest\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eItems\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003eSendConfirmation\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ecustomer\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003erequest\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003eOrderResult\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eSuccess\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003epayment\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eTransactionId\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\n\n\n\u003ch3 id=\"modern-enforcement-tools\"\u003e\u003ca href=\"/posts/dotnet-power-of-ten-rules/#modern-enforcement-tools\" title=\"Modern Enforcement Tools\"\u003eModern Enforcement Tools\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eUnlike 2006 C, modern tooling enforces this automatically. The \u003ca href=\"https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1505\" target=\"_blank\" rel=\"noopener external noreferrer\"\u003eCA1505 analyzer rule\u003c/a\u003e flags unmaintainable code based on cyclomatic complexity and maintainability index. Configure it in \u003ccode\u003e.editorconfig\u003c/code\u003e to treat violations as warnings, ensuring your team maintains digestible function sizes without manual review.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eLocal functions\u003c/strong\u003e reduce the need for small private methods:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"n\"\u003eIEnumerable\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eOrder\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003eGetRecentOrders\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eCustomer\u003c/span\u003e \u003cspan class=\"n\"\u003ecustomer\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003ecutoffDate\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eDateTime\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eUtcNow\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAddDays\u003c/span\u003e\u003cspan class=\"p\"\u003e(-\u003c/span\u003e\u003cspan class=\"m\"\u003e30\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003ecustomer\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eOrders\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eWhere\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eIsRecent\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eOrderByDescending\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eo\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003eo\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eDate\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kt\"\u003ebool\u003c/span\u003e \u003cspan class=\"n\"\u003eIsRecent\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eOrder\u003c/span\u003e \u003cspan class=\"n\"\u003eorder\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003eorder\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eDate\u003c/span\u003e \u003cspan class=\"p\"\u003e\u0026gt;=\u003c/span\u003e \u003cspan class=\"n\"\u003ecutoffDate\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cstrong\u003eVerdict:\u003c/strong\u003e If anything, aim for shorter than 60 lines. With expression-bodied members, local functions, and LINQ, there\u0026rsquo;s no excuse for bloated methods. Enable analyzers to enforce it.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"rule-5-assertion-density-of-two-per-function\"\u003e\u003ca href=\"/posts/dotnet-power-of-ten-rules/#rule-5-assertion-density-of-two-per-function\" title=\"Rule 5: Assertion Density of Two Per Function\"\u003eRule 5: Assertion Density of Two Per Function\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003eOriginal Intent:\u003c/strong\u003e Catch anomalous conditions early with runtime checks.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eC#/.NET Applicability:\u003c/strong\u003e Valid principle, but modern C# offers better mechanisms.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"the-modern-equivalent-multiple-layers-of-defense\"\u003e\u003ca href=\"/posts/dotnet-power-of-ten-rules/#the-modern-equivalent-multiple-layers-of-defense\" title=\"The Modern Equivalent: Multiple Layers of Defense\"\u003eThe Modern Equivalent: Multiple Layers of Defense\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eNASA wanted two runtime assertions per function. C# gives you something better—multiple enforcement layers, starting at compile time:\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e1. Compile-Time Checks: Nullable Reference Types\u003c/strong\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cp\"\u003e#nullable\u003c/span\u003e \u003cspan class=\"n\"\u003eenable\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eOrderProcessor\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e// Compiler enforces non-null at compile time\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"n\"\u003eOrderResult\u003c/span\u003e \u003cspan class=\"n\"\u003eProcess\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eOrder\u003c/span\u003e \u003cspan class=\"n\"\u003eorder\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eCustomer\u003c/span\u003e \u003cspan class=\"n\"\u003ecustomer\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"c1\"\u003e// No runtime null check needed - compiler guarantees non-null\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003etotal\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eorder\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eItems\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eSum\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ei\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003ei\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ePrice\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"c1\"\u003e// Nullable warns if customer.Email might be null\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003eSendReceipt\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ecustomer\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eEmail\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003etotal\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003eOrderResult\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eSuccess\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003eprivate\u003c/span\u003e \u003cspan class=\"k\"\u003evoid\u003c/span\u003e \u003cspan class=\"n\"\u003eSendReceipt\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003estring\u003c/span\u003e \u003cspan class=\"n\"\u003eemail\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"kt\"\u003edecimal\u003c/span\u003e \u003cspan class=\"n\"\u003eamount\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"c1\"\u003e// email is guaranteed non-null by caller contract\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cstrong\u003e2. Parameter Validation: Guard Clauses\u003c/strong\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"k\"\u003evoid\u003c/span\u003e \u003cspan class=\"n\"\u003eProcessPayment\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003edecimal\u003c/span\u003e \u003cspan class=\"n\"\u003eamount\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003ePaymentMethod\u003c/span\u003e \u003cspan class=\"n\"\u003emethod\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eArgumentNullException\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eThrowIfNull\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003emethod\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e  \u003cspan class=\"c1\"\u003e// .NET 6+\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eArgumentOutOfRangeException\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eThrowIfNegativeOrZero\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eamount\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e// Business logic here\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// Pre-.NET 6 equivalent\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"k\"\u003evoid\u003c/span\u003e \u003cspan class=\"n\"\u003eProcessPayment\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003edecimal\u003c/span\u003e \u003cspan class=\"n\"\u003eamount\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003ePaymentMethod\u003c/span\u003e \u003cspan class=\"n\"\u003emethod\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003emethod\u003c/span\u003e \u003cspan class=\"k\"\u003eis\u003c/span\u003e \u003cspan class=\"kc\"\u003enull\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003ethrow\u003c/span\u003e \u003cspan class=\"k\"\u003enew\u003c/span\u003e \u003cspan class=\"n\"\u003eArgumentNullException\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003enameof\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003emethod\u003c/span\u003e\u003cspan class=\"p\"\u003e));\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eamount\u003c/span\u003e \u003cspan class=\"p\"\u003e\u0026lt;=\u003c/span\u003e \u003cspan class=\"m\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003ethrow\u003c/span\u003e \u003cspan class=\"k\"\u003enew\u003c/span\u003e \u003cspan class=\"n\"\u003eArgumentOutOfRangeException\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003enameof\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eamount\u003c/span\u003e\u003cspan class=\"p\"\u003e),\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;Amount must be positive\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e// Business logic here\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cstrong\u003e3. Debug Assertions for Internal Invariants\u003c/strong\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eusing\u003c/span\u003e \u003cspan class=\"nn\"\u003eSystem.Diagnostics\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"k\"\u003evoid\u003c/span\u003e \u003cspan class=\"n\"\u003eUpdateBalance\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eAccount\u003c/span\u003e \u003cspan class=\"n\"\u003eaccount\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"kt\"\u003edecimal\u003c/span\u003e \u003cspan class=\"n\"\u003edelta\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eDebug\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAssert\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eaccount\u003c/span\u003e \u003cspan class=\"p\"\u003e!=\u003c/span\u003e \u003cspan class=\"kc\"\u003enull\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;Account should never be null here\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eDebug\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAssert\u003c/span\u003e\u003cspan class=\"p\"\u003e(!\u003c/span\u003e\u003cspan class=\"n\"\u003eaccount\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eIsClosed\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;Should not update closed accounts\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eaccount\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eBalance\u003c/span\u003e \u003cspan class=\"p\"\u003e+=\u003c/span\u003e \u003cspan class=\"n\"\u003edelta\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eDebug\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAssert\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eaccount\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eBalance\u003c/span\u003e \u003cspan class=\"p\"\u003e\u0026gt;=\u003c/span\u003e \u003cspan class=\"m\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;Balance should never go negative\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cstrong\u003eKey difference:\u003c/strong\u003e \u003ccode\u003eDebug.Assert\u003c/code\u003e is removed in Release builds, making it suitable for checking invariants during development without runtime cost. For mission-critical code, consider \u003ca href=\"https://learn.microsoft.com/en-us/dotnet/framework/debug-trace-profile/code-contracts\" target=\"_blank\" rel=\"noopener external noreferrer\"\u003eCode Contracts\u003c/a\u003e which provide formal preconditions, postconditions, and object invariants with static verification support.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"pattern-matching-for-exhaustiveness\"\u003e\u003ca href=\"/posts/dotnet-power-of-ten-rules/#pattern-matching-for-exhaustiveness\" title=\"Pattern Matching for Exhaustiveness\"\u003ePattern Matching for Exhaustiveness\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eModern C# can enforce completeness at compile time:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kt\"\u003estring\u003c/span\u003e \u003cspan class=\"n\"\u003eGetStatusMessage\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eOrderStatus\u003c/span\u003e \u003cspan class=\"n\"\u003estatus\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003estatus\u003c/span\u003e \u003cspan class=\"k\"\u003eswitch\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eOrderStatus\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ePending\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;Order is pending\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eOrderStatus\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eProcessing\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;Order is being processed\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eOrderStatus\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eShipped\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;Order has been shipped\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eOrderStatus\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eDelivered\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;Order delivered\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eOrderStatus\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eCancelled\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;Order cancelled\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e// Compiler error if any enum value is missing (with warnings enabled)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cstrong\u003eVerdict:\u003c/strong\u003e Use nullable reference types for compile-time null safety, guard clauses for parameter validation, and \u003ccode\u003eDebug.Assert\u003c/code\u003e for invariants. This gives you better than \u0026ldquo;two assertions per function\u0026rdquo;: you get enforcement at compile time where possible, and runtime checks where needed.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"rule-6-declare-data-at-smallest-possible-scope\"\u003e\u003ca href=\"/posts/dotnet-power-of-ten-rules/#rule-6-declare-data-at-smallest-possible-scope\" title=\"Rule 6: Declare Data at Smallest Possible Scope\"\u003eRule 6: Declare Data at Smallest Possible Scope\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003eOriginal Intent:\u003c/strong\u003e Minimize variable lifetime and potential misuse.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eC#/.NET Applicability:\u003c/strong\u003e Completely valid and reinforced by modern language features.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"modern-c-makes-this-easier\"\u003e\u003ca href=\"/posts/dotnet-power-of-ten-rules/#modern-c-makes-this-easier\" title=\"Modern C# Makes This Easier\"\u003eModern C# Makes This Easier\u003c/a\u003e\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// Bad: variables declared too early\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"k\"\u003evoid\u003c/span\u003e \u003cspan class=\"n\"\u003eProcessData\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e\u003cspan class=\"p\"\u003e[]\u003c/span\u003e \u003cspan class=\"n\"\u003edata\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"n\"\u003esum\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"m\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"n\"\u003ecount\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"m\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"n\"\u003eaverage\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"m\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"n\"\u003emax\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"m\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e// 50 lines later...\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003efor\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"n\"\u003ei\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"m\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"n\"\u003ei\u003c/span\u003e \u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e \u003cspan class=\"n\"\u003edata\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eLength\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"n\"\u003ei\u003c/span\u003e\u003cspan class=\"p\"\u003e++)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003esum\u003c/span\u003e \u003cspan class=\"p\"\u003e+=\u003c/span\u003e \u003cspan class=\"n\"\u003edata\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"n\"\u003ei\u003c/span\u003e\u003cspan class=\"p\"\u003e];\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003ecount\u003c/span\u003e\u003cspan class=\"p\"\u003e++;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eaverage\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003esum\u003c/span\u003e \u003cspan class=\"p\"\u003e/\u003c/span\u003e \u003cspan class=\"n\"\u003ecount\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e// Another 30 lines...\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003emax\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003edata\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eMax\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// Good: declare at point of use\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"k\"\u003evoid\u003c/span\u003e \u003cspan class=\"n\"\u003eProcessData\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e\u003cspan class=\"p\"\u003e[]\u003c/span\u003e \u003cspan class=\"n\"\u003edata\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e// ... other logic ...\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"n\"\u003esum\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"m\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eforeach\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"k\"\u003evalue\u003c/span\u003e \u003cspan class=\"k\"\u003ein\u003c/span\u003e \u003cspan class=\"n\"\u003edata\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003esum\u003c/span\u003e \u003cspan class=\"p\"\u003e+=\u003c/span\u003e \u003cspan class=\"k\"\u003evalue\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"n\"\u003eaverage\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003esum\u003c/span\u003e \u003cspan class=\"p\"\u003e/\u003c/span\u003e \u003cspan class=\"n\"\u003edata\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eLength\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e// ... other logic ...\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"n\"\u003emax\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003edata\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eMax\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\n\n\n\u003ch3 id=\"pattern-matching-limits-scope-automatically\"\u003e\u003ca href=\"/posts/dotnet-power-of-ten-rules/#pattern-matching-limits-scope-automatically\" title=\"Pattern Matching Limits Scope Automatically\"\u003ePattern Matching Limits Scope Automatically\u003c/a\u003e\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// Scope limited to when pattern matches\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eobj\u003c/span\u003e \u003cspan class=\"k\"\u003eis\u003c/span\u003e \u003cspan class=\"n\"\u003eCustomer\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"n\"\u003eIsActive\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kc\"\u003etrue\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e \u003cspan class=\"n\"\u003ecustomer\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e// \u0026#39;customer\u0026#39; only exists in this block\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eProcessCustomer\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ecustomer\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// \u0026#39;customer\u0026#39; doesn\u0026#39;t exist here\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// Switch expressions with declaration patterns\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003ediscount\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eorder\u003c/span\u003e \u003cspan class=\"k\"\u003eswitch\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"n\"\u003eTotal\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"m\"\u003e1000\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e \u003cspan class=\"n\"\u003elargeOrder\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003elargeOrder\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eTotal\u003c/span\u003e \u003cspan class=\"p\"\u003e*\u003c/span\u003e \u003cspan class=\"m\"\u003e0.1\u003c/span\u003e\u003cspan class=\"n\"\u003em\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"n\"\u003eCustomer\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eIsPremium\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kc\"\u003etrue\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e \u003cspan class=\"n\"\u003epremiumOrder\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003epremiumOrder\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eTotal\u003c/span\u003e \u003cspan class=\"p\"\u003e*\u003c/span\u003e \u003cspan class=\"m\"\u003e0.05\u003c/span\u003e\u003cspan class=\"n\"\u003em\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003e_\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"m\"\u003e0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e};\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\n\n\n\u003ch3 id=\"using-declarations-for-resource-management\"\u003e\u003ca href=\"/posts/dotnet-power-of-ten-rules/#using-declarations-for-resource-management\" title=\"Using Declarations for Resource Management\"\u003eUsing Declarations for Resource Management\u003c/a\u003e\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// Bad: resource scope too broad\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kd\"\u003easync\u003c/span\u003e \u003cspan class=\"n\"\u003eTask\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"kt\"\u003estring\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003eReadConfig\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003estream\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eFile\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eOpenRead\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;config.json\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003etry\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"c1\"\u003e// 100 lines of code\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003eReadFromStream\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003estream\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003efinally\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003estream\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eDispose\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// Good: using declaration limits scope\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kd\"\u003easync\u003c/span\u003e \u003cspan class=\"n\"\u003eTask\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"kt\"\u003estring\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003eReadConfig\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eusing\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003estream\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eFile\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eOpenRead\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;config.json\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e))\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003eReadFromStream\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003estream\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e// stream disposed here, not at method end\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// Even better: compact using declaration (C# 8+)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kd\"\u003easync\u003c/span\u003e \u003cspan class=\"n\"\u003eTask\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"kt\"\u003estring\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003eReadConfig\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eusing\u003c/span\u003e \u003cspan class=\"nn\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003estream\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eFile\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eOpenRead\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;config.json\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003eReadFromStream\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003estream\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e// stream disposed at end of method automatically\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cstrong\u003eVerdict:\u003c/strong\u003e More relevant than ever. Pattern matching limits scope automatically. Using declarations prevent resource leaks. Block-scoped variables can\u0026rsquo;t escape their intended lifetime. C# 10\u0026rsquo;s file-scoped namespaces extend this principle to namespace declarations—one less level of indentation, one less place for variables to hide.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"rule-7-check-return-values-and-parameter-validity\"\u003e\u003ca href=\"/posts/dotnet-power-of-ten-rules/#rule-7-check-return-values-and-parameter-validity\" title=\"Rule 7: Check Return Values and Parameter Validity\"\u003eRule 7: Check Return Values and Parameter Validity\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003eOriginal Intent:\u003c/strong\u003e Never ignore potential failures; validate all inputs.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eC#/.NET Applicability:\u003c/strong\u003e Absolutely critical, but mechanisms differ.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"return-value-checking-exceptions-vs-result-types\"\u003e\u003ca href=\"/posts/dotnet-power-of-ten-rules/#return-value-checking-exceptions-vs-result-types\" title=\"Return Value Checking: Exceptions vs. Result Types\"\u003eReturn Value Checking: Exceptions vs. Result Types\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003e.NET uses exceptions for error conditions, unlike C\u0026rsquo;s return codes:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// .NET idiomatic: exception-based\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"k\"\u003evoid\u003c/span\u003e \u003cspan class=\"n\"\u003eSaveCustomer\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eCustomer\u003c/span\u003e \u003cspan class=\"n\"\u003ecustomer\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eArgumentNullException\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eThrowIfNull\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ecustomer\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003etry\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003edatabase\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eSaveChanges\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e  \u003cspan class=\"c1\"\u003e// Throws on error\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003ecatch\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eDbUpdateException\u003c/span\u003e \u003cspan class=\"n\"\u003eex\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003elogger\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eLogError\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eex\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;Failed to save customer {CustomerId}\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003ecustomer\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eId\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003ethrow\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e  \u003cspan class=\"c1\"\u003e// Re-throw or wrap in domain exception\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eBut C#\u0026rsquo;s \u003ccode\u003eTry*\u003c/code\u003e pattern provides explicit success/failure:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// When failure is expected and not exceptional\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(!\u003c/span\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eTryParse\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003einput\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"k\"\u003eout\u003c/span\u003e \u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"k\"\u003evalue\u003c/span\u003e\u003cspan class=\"p\"\u003e))\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eConsole\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eWriteLine\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;Invalid number format\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(!\u003c/span\u003e\u003cspan class=\"n\"\u003einventory\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eTryReserve\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eproductId\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003equantity\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"k\"\u003eout\u003c/span\u003e \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003ereservation\u003c/span\u003e\u003cspan class=\"p\"\u003e))\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003eOrderResult\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eInsufficientStock\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\n\n\n\u003ch3 id=\"modern-pattern-result-types\"\u003e\u003ca href=\"/posts/dotnet-power-of-ten-rules/#modern-pattern-result-types\" title=\"Modern Pattern: Result Types\"\u003eModern Pattern: Result Types\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eWhen failure is a normal business outcome rather than an exceptional condition, exceptions feel wrong. Result types make success and failure explicit:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"k\"\u003erecord\u003c/span\u003e \u003cspan class=\"nc\"\u003eResult\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eT\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kt\"\u003ebool\u003c/span\u003e \u003cspan class=\"n\"\u003eSuccess\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"k\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003einit\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"n\"\u003eT\u003c/span\u003e\u003cspan class=\"p\"\u003e?\u003c/span\u003e \u003cspan class=\"n\"\u003eValue\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"k\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003einit\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kt\"\u003estring?\u003c/span\u003e \u003cspan class=\"n\"\u003eError\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"k\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003einit\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kd\"\u003estatic\u003c/span\u003e \u003cspan class=\"n\"\u003eResult\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eT\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003eOk\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eT\u003c/span\u003e \u003cspan class=\"k\"\u003evalue\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"k\"\u003enew\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"n\"\u003eSuccess\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eValue\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003evalue\u003c/span\u003e \u003cspan class=\"p\"\u003e};\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kd\"\u003estatic\u003c/span\u003e \u003cspan class=\"n\"\u003eResult\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eT\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003eFail\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003estring\u003c/span\u003e \u003cspan class=\"n\"\u003eerror\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"k\"\u003enew\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"n\"\u003eSuccess\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"kc\"\u003efalse\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eError\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eerror\u003c/span\u003e \u003cspan class=\"p\"\u003e};\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kd\"\u003easync\u003c/span\u003e \u003cspan class=\"n\"\u003eTask\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eResult\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eOrder\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003ePlaceOrder\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eOrderRequest\u003c/span\u003e \u003cspan class=\"n\"\u003erequest\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003evalidationResult\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eValidateRequest\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003erequest\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(!\u003c/span\u003e\u003cspan class=\"n\"\u003evalidationResult\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eSuccess\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003eResult\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eOrder\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;.\u003c/span\u003e\u003cspan class=\"n\"\u003eFail\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003evalidationResult\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eError\u003c/span\u003e\u003cspan class=\"p\"\u003e!);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003epaymentResult\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003eProcessPayment\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003erequest\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(!\u003c/span\u003e\u003cspan class=\"n\"\u003epaymentResult\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eSuccess\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003eResult\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eOrder\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;.\u003c/span\u003e\u003cspan class=\"n\"\u003eFail\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003epaymentResult\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eError\u003c/span\u003e\u003cspan class=\"p\"\u003e!);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003eResult\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eOrder\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;.\u003c/span\u003e\u003cspan class=\"n\"\u003eOk\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eCreateOrder\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003erequest\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003epaymentResult\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eValue\u003c/span\u003e\u003cspan class=\"p\"\u003e!));\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\n\n\n\u003ch3 id=\"parameter-validation-multiple-strategies\"\u003e\u003ca href=\"/posts/dotnet-power-of-ten-rules/#parameter-validation-multiple-strategies\" title=\"Parameter Validation: Multiple Strategies\"\u003eParameter Validation: Multiple Strategies\u003c/a\u003e\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eOrderService\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e// 1. Constructor validation\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"n\"\u003eOrderService\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eIOrderRepository\u003c/span\u003e \u003cspan class=\"n\"\u003erepository\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eILogger\u003c/span\u003e \u003cspan class=\"n\"\u003elogger\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003eArgumentNullException\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eThrowIfNull\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003erepository\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003eArgumentNullException\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eThrowIfNull\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003elogger\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003e_repository\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003erepository\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003e_logger\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003elogger\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e// 2. Method parameter validation\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"n\"\u003eOrder\u003c/span\u003e \u003cspan class=\"n\"\u003eCreateOrder\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eCustomer\u003c/span\u003e \u003cspan class=\"n\"\u003ecustomer\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eList\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eOrderItem\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003eitems\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003eArgumentNullException\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eThrowIfNull\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ecustomer\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003eArgumentNullException\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eThrowIfNull\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eitems\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eitems\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eCount\u003c/span\u003e \u003cspan class=\"p\"\u003e==\u003c/span\u003e \u003cspan class=\"m\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"k\"\u003ethrow\u003c/span\u003e \u003cspan class=\"k\"\u003enew\u003c/span\u003e \u003cspan class=\"n\"\u003eArgumentException\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;Order must contain at least one item\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003enameof\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eitems\u003c/span\u003e\u003cspan class=\"p\"\u003e));\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eitems\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAny\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ei\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003ei\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eQuantity\u003c/span\u003e \u003cspan class=\"p\"\u003e\u0026lt;=\u003c/span\u003e \u003cspan class=\"m\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e))\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"k\"\u003ethrow\u003c/span\u003e \u003cspan class=\"k\"\u003enew\u003c/span\u003e \u003cspan class=\"n\"\u003eArgumentException\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;All items must have positive quantity\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003enameof\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eitems\u003c/span\u003e\u003cspan class=\"p\"\u003e));\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"c1\"\u003e// Business logic\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\n\n\n\u003ch3 id=\"analyzer-support\"\u003e\u003ca href=\"/posts/dotnet-power-of-ten-rules/#analyzer-support\" title=\"Analyzer Support\"\u003eAnalyzer Support\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eRoslyn analyzers automatically detect unchecked return values. Enable \u003ca href=\"https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1806\" target=\"_blank\" rel=\"noopener external noreferrer\"\u003eCA1806\u003c/a\u003e to catch ignored method results and \u003ca href=\"https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0058\" target=\"_blank\" rel=\"noopener external noreferrer\"\u003eIDE0058\u003c/a\u003e to flag unused expression values. These analyzers ensure your team never silently discards important return information.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eVerdict:\u003c/strong\u003e Always validate parameters. Always handle exceptions or check return values. Use \u003ccode\u003eArgumentNullException.ThrowIfNull()\u003c/code\u003e for parameter checks, \u003ccode\u003eTry*\u003c/code\u003e methods for expected failures, and exceptions for exceptional conditions.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"rule-8-limited-preprocessor-use\"\u003e\u003ca href=\"/posts/dotnet-power-of-ten-rules/#rule-8-limited-preprocessor-use\" title=\"Rule 8: Limited Preprocessor Use\"\u003eRule 8: Limited Preprocessor Use\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003eOriginal Intent:\u003c/strong\u003e Avoid complex macros that obscure code meaning and hinder analysis.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eC#/.NET Applicability:\u003c/strong\u003e Largely irrelevant: C# preprocessor is far more constrained.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"what-c-doesnt-have-thank-goodness\"\u003e\u003ca href=\"/posts/dotnet-power-of-ten-rules/#what-c-doesnt-have-thank-goodness\" title=\"What C# Doesn\u0026rsquo;t Have (Thank Goodness)\"\u003eWhat C# Doesn\u0026rsquo;t Have (Thank Goodness)\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eC# preprocessor directives can\u0026rsquo;t:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eDefine function-like macros\u003c/li\u003e\n\u003cli\u003ePerform token pasting\u003c/li\u003e\n\u003cli\u003eCreate recursive macros\u003c/li\u003e\n\u003cli\u003eHide complex logic\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// This is NOT possible in C#:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// #define MAX(a, b) ((a) \u0026gt; (b) ? (a) : (b))   // No function-like macros\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// What C# DOES support:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cp\"\u003e#define\u003c/span\u003e \u003cspan class=\"n\"\u003eDEBUG_LOGGING\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cp\"\u003e#if\u003c/span\u003e \u003cspan class=\"n\"\u003eDEBUG_LOGGING\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eConsole\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eWriteLine\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;Debug information\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cp\"\u003e#endif\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\n\n\n\u003ch3 id=\"the-real-concern-conditional-compilation-abuse\"\u003e\u003ca href=\"/posts/dotnet-power-of-ten-rules/#the-real-concern-conditional-compilation-abuse\" title=\"The Real Concern: Conditional Compilation Abuse\"\u003eThe Real Concern: Conditional Compilation Abuse\u003c/a\u003e\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// Bad: excessive conditional compilation\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eConfigService\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kt\"\u003estring\u003c/span\u003e \u003cspan class=\"n\"\u003eGetConnectionString\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"cp\"\u003e#if\u003c/span\u003e \u003cspan class=\"n\"\u003ePRODUCTION\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;Production connection string\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"cp\"\u003e#elif\u003c/span\u003e \u003cspan class=\"n\"\u003eSTAGING\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;Staging connection string\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"cp\"\u003e#elif\u003c/span\u003e \u003cspan class=\"n\"\u003eDEVELOPMENT\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;Development connection string\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"cp\"\u003e#else\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;Local connection string\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"cp\"\u003e#endif\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// Good: configuration-based approach\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eConfigService\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003eprivate\u003c/span\u003e \u003cspan class=\"k\"\u003ereadonly\u003c/span\u003e \u003cspan class=\"n\"\u003eIConfiguration\u003c/span\u003e \u003cspan class=\"n\"\u003e_config\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"n\"\u003eConfigService\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eIConfiguration\u003c/span\u003e \u003cspan class=\"n\"\u003econfig\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003e_config\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003econfig\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kt\"\u003estring\u003c/span\u003e \u003cspan class=\"n\"\u003eGetConnectionString\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003e_config\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eGetConnectionString\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;DefaultConnection\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\n\n\n\u003ch3 id=\"legitimate-uses\"\u003e\u003ca href=\"/posts/dotnet-power-of-ten-rules/#legitimate-uses\" title=\"Legitimate Uses\"\u003eLegitimate Uses\u003c/a\u003e\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// Acceptable: Debug-only code\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"k\"\u003evoid\u003c/span\u003e \u003cspan class=\"n\"\u003eProcessData\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e\u003cspan class=\"p\"\u003e[]\u003c/span\u003e \u003cspan class=\"n\"\u003edata\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"cp\"\u003e#if\u003c/span\u003e \u003cspan class=\"n\"\u003eDEBUG\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eDebug\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAssert\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003edata\u003c/span\u003e \u003cspan class=\"p\"\u003e!=\u003c/span\u003e \u003cspan class=\"kc\"\u003enull\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eDebug\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAssert\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003edata\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eLength\u003c/span\u003e \u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"m\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"cp\"\u003e#endif\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e// Production code\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"n\"\u003esum\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"m\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eforeach\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"k\"\u003evalue\u003c/span\u003e \u003cspan class=\"k\"\u003ein\u003c/span\u003e \u003cspan class=\"n\"\u003edata\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003esum\u003c/span\u003e \u003cspan class=\"p\"\u003e+=\u003c/span\u003e \u003cspan class=\"k\"\u003evalue\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// Acceptable: Platform-specific code\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cp\"\u003e#if\u003c/span\u003e \u003cspan class=\"n\"\u003eWINDOWS\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"na\"\u003e    [DllImport(\u0026#34;user32.dll\u0026#34;)]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003eprivate\u003c/span\u003e \u003cspan class=\"kd\"\u003estatic\u003c/span\u003e \u003cspan class=\"kd\"\u003eextern\u003c/span\u003e \u003cspan class=\"kt\"\u003ebool\u003c/span\u003e \u003cspan class=\"n\"\u003eShowWindow\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eIntPtr\u003c/span\u003e \u003cspan class=\"n\"\u003ehWnd\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"n\"\u003enCmdShow\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cp\"\u003e#elif\u003c/span\u003e \u003cspan class=\"n\"\u003eLINUX\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"na\"\u003e    [DllImport(\u0026#34;libX11.so\u0026#34;)]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003eprivate\u003c/span\u003e \u003cspan class=\"kd\"\u003estatic\u003c/span\u003e \u003cspan class=\"kd\"\u003eextern\u003c/span\u003e \u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"n\"\u003eXOpenDisplay\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003estring\u003c/span\u003e \u003cspan class=\"n\"\u003edisplay_name\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cp\"\u003e#endif\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cstrong\u003eVerdict:\u003c/strong\u003e C#\u0026rsquo;s preprocessor is already minimal by design. Use it for debug-only code and platform-specific implementations. For everything else, use dependency injection and configuration.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"rule-9-restrict-pointer-use-max-one-level-of-dereferencing\"\u003e\u003ca href=\"/posts/dotnet-power-of-ten-rules/#rule-9-restrict-pointer-use-max-one-level-of-dereferencing\" title=\"Rule 9: Restrict Pointer Use (Max One Level of Dereferencing)\"\u003eRule 9: Restrict Pointer Use (Max One Level of Dereferencing)\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003eOriginal Intent:\u003c/strong\u003e Prevent complex pointer arithmetic and double-pointer confusion.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eC#/.NET Applicability:\u003c/strong\u003e Mostly irrelevant: managed code eliminates most pointer usage.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"the-net-alternative-references-are-not-pointers\"\u003e\u003ca href=\"/posts/dotnet-power-of-ten-rules/#the-net-alternative-references-are-not-pointers\" title=\"The .NET Alternative: References Are Not Pointers\"\u003eThe .NET Alternative: References Are Not Pointers\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eIn managed C#, you work with references, not pointers:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// Safe by default - no pointer arithmetic\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eCustomer\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kt\"\u003estring\u003c/span\u003e \u003cspan class=\"n\"\u003eName\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"k\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003eset\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// Reference semantics, but no pointer manipulation\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eCustomer\u003c/span\u003e \u003cspan class=\"n\"\u003ecustomer\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003enew\u003c/span\u003e \u003cspan class=\"n\"\u003eCustomer\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"n\"\u003eName\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;John\u0026#34;\u003c/span\u003e \u003cspan class=\"p\"\u003e};\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eCustomer\u003c/span\u003e \u003cspan class=\"n\"\u003esame\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003ecustomer\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e  \u003cspan class=\"c1\"\u003e// same reference, not a copy\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003esame\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eName\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;Jane\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eConsole\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eWriteLine\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ecustomer\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eName\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e  \u003cspan class=\"c1\"\u003e// Outputs: Jane\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\n\n\n\u003ch3 id=\"unsafe-code-when-you-actually-need-pointers\"\u003e\u003ca href=\"/posts/dotnet-power-of-ten-rules/#unsafe-code-when-you-actually-need-pointers\" title=\"Unsafe Code: When You Actually Need Pointers\"\u003eUnsafe Code: When You Actually Need Pointers\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eRare, but sometimes unavoidable for interop or extreme performance:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// Performance-critical unsafe code - use sparingly\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003eunsafe\u003c/span\u003e \u003cspan class=\"k\"\u003evoid\u003c/span\u003e \u003cspan class=\"n\"\u003eProcessBuffer\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003ebyte\u003c/span\u003e\u003cspan class=\"p\"\u003e[]\u003c/span\u003e \u003cspan class=\"n\"\u003edata\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003efixed\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003ebyte\u003c/span\u003e\u003cspan class=\"p\"\u003e*\u003c/span\u003e \u003cspan class=\"n\"\u003eptr\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003edata\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"c1\"\u003e// One level of indirection - NASA would approve\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"kt\"\u003ebyte\u003c/span\u003e\u003cspan class=\"p\"\u003e*\u003c/span\u003e \u003cspan class=\"n\"\u003ecurrent\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eptr\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003efor\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"n\"\u003ei\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"m\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"n\"\u003ei\u003c/span\u003e \u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e \u003cspan class=\"n\"\u003edata\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eLength\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"n\"\u003ei\u003c/span\u003e\u003cspan class=\"p\"\u003e++)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"p\"\u003e*\u003c/span\u003e\u003cspan class=\"n\"\u003ecurrent\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003ebyte\u003c/span\u003e\u003cspan class=\"p\"\u003e)(*\u003c/span\u003e\u003cspan class=\"n\"\u003ecurrent\u003c/span\u003e \u003cspan class=\"p\"\u003e*\u003c/span\u003e \u003cspan class=\"m\"\u003e2\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"n\"\u003ecurrent\u003c/span\u003e\u003cspan class=\"p\"\u003e++;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\n\n\n\u003ch3 id=\"modern-safe-alternative-span\"\u003e\u003ca href=\"/posts/dotnet-power-of-ten-rules/#modern-safe-alternative-span\" title=\"Modern Safe Alternative: Span\"\u003eModern Safe Alternative: Span\u003cT\u003e\u003c/a\u003e\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// Safe, zero-allocation, pointer-like performance\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003evoid\u003c/span\u003e \u003cspan class=\"n\"\u003eProcessBuffer\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eSpan\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"kt\"\u003ebyte\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003edata\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003efor\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"n\"\u003ei\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"m\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"n\"\u003ei\u003c/span\u003e \u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e \u003cspan class=\"n\"\u003edata\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eLength\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"n\"\u003ei\u003c/span\u003e\u003cspan class=\"p\"\u003e++)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003edata\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"n\"\u003ei\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003ebyte\u003c/span\u003e\u003cspan class=\"p\"\u003e)(\u003c/span\u003e\u003cspan class=\"n\"\u003edata\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"n\"\u003ei\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e \u003cspan class=\"p\"\u003e*\u003c/span\u003e \u003cspan class=\"m\"\u003e2\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// Or even better:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003evoid\u003c/span\u003e \u003cspan class=\"n\"\u003eProcessBuffer\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eSpan\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"kt\"\u003ebyte\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003edata\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eforeach\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"k\"\u003eref\u003c/span\u003e \u003cspan class=\"kt\"\u003ebyte\u003c/span\u003e \u003cspan class=\"k\"\u003evalue\u003c/span\u003e \u003cspan class=\"k\"\u003ein\u003c/span\u003e \u003cspan class=\"n\"\u003edata\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003evalue\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003ebyte\u003c/span\u003e\u003cspan class=\"p\"\u003e)(\u003c/span\u003e\u003cspan class=\"k\"\u003evalue\u003c/span\u003e \u003cspan class=\"p\"\u003e*\u003c/span\u003e \u003cspan class=\"m\"\u003e2\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\n\n\n\u003ch3 id=\"refinout-managed-pointer-like-semantics\"\u003e\u003ca href=\"/posts/dotnet-power-of-ten-rules/#refinout-managed-pointer-like-semantics\" title=\"ref/in/out: Managed \u0026ldquo;Pointer-Like\u0026rdquo; Semantics\"\u003eref/in/out: Managed \u0026ldquo;Pointer-Like\u0026rdquo; Semantics\u003c/a\u003e\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// Pass by reference without pointers\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"k\"\u003evoid\u003c/span\u003e \u003cspan class=\"n\"\u003eUpdateCustomer\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"k\"\u003eref\u003c/span\u003e \u003cspan class=\"n\"\u003eCustomer\u003c/span\u003e \u003cspan class=\"n\"\u003ecustomer\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003ecustomer\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003enew\u003c/span\u003e \u003cspan class=\"n\"\u003eCustomer\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"n\"\u003eName\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;Updated\u0026#34;\u003c/span\u003e \u003cspan class=\"p\"\u003e};\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// Read-only reference (no copying large structs)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kt\"\u003edecimal\u003c/span\u003e \u003cspan class=\"n\"\u003eCalculateTotal\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"k\"\u003ein\u003c/span\u003e \u003cspan class=\"n\"\u003eLargeStruct\u003c/span\u003e \u003cspan class=\"n\"\u003edata\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003edata\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eValue1\u003c/span\u003e \u003cspan class=\"p\"\u003e+\u003c/span\u003e \u003cspan class=\"n\"\u003edata\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eValue2\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e  \u003cspan class=\"c1\"\u003e// No defensive copy\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// Output parameter\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kt\"\u003ebool\u003c/span\u003e \u003cspan class=\"n\"\u003eTryGetCustomer\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"n\"\u003eid\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"k\"\u003eout\u003c/span\u003e \u003cspan class=\"n\"\u003eCustomer\u003c/span\u003e \u003cspan class=\"n\"\u003ecustomer\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e// ...\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cstrong\u003eVerdict:\u003c/strong\u003e You rarely need unsafe code in modern C#. Use \u003ccode\u003eSpan\u0026lt;T\u0026gt;\u003c/code\u003e/\u003ccode\u003eMemory\u0026lt;T\u0026gt;\u003c/code\u003e for performance-critical buffer manipulation. Use \u003ccode\u003eref\u003c/code\u003e/\u003ccode\u003ein\u003c/code\u003e/\u003ccode\u003eout\u003c/code\u003e for reference semantics. Reserve \u003ccode\u003eunsafe\u003c/code\u003e for true interop scenarios.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"rule-10-enable-all-compiler-warnings-and-use-static-analysis\"\u003e\u003ca href=\"/posts/dotnet-power-of-ten-rules/#rule-10-enable-all-compiler-warnings-and-use-static-analysis\" title=\"Rule 10: Enable All Compiler Warnings and Use Static Analysis\"\u003eRule 10: Enable All Compiler Warnings and Use Static Analysis\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003eOriginal Intent:\u003c/strong\u003e Catch bugs early through automated checking.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eC#/.NET Applicability:\u003c/strong\u003e Not just valid: vastly more powerful than 2006 C tooling.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"modern-tooling-is-incomparably-better\"\u003e\u003ca href=\"/posts/dotnet-power-of-ten-rules/#modern-tooling-is-incomparably-better\" title=\"Modern Tooling Is Incomparably Better\"\u003eModern Tooling Is Incomparably Better\u003c/a\u003e\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-xml\" data-lang=\"xml\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c\"\u003e\u0026lt;!-- Essential .csproj settings --\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003e\u0026lt;PropertyGroup\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c\"\u003e\u0026lt;!-- Treat all warnings as errors --\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nt\"\u003e\u0026lt;TreatWarningsAsErrors\u0026gt;\u003c/span\u003etrue\u003cspan class=\"nt\"\u003e\u0026lt;/TreatWarningsAsErrors\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c\"\u003e\u0026lt;!-- Enable nullable reference types --\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nt\"\u003e\u0026lt;Nullable\u0026gt;\u003c/span\u003eenable\u003cspan class=\"nt\"\u003e\u0026lt;/Nullable\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c\"\u003e\u0026lt;!-- Enable latest code analysis --\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nt\"\u003e\u0026lt;AnalysisLevel\u0026gt;\u003c/span\u003elatest\u003cspan class=\"nt\"\u003e\u0026lt;/AnalysisLevel\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nt\"\u003e\u0026lt;EnforceCodeStyleInBuild\u0026gt;\u003c/span\u003etrue\u003cspan class=\"nt\"\u003e\u0026lt;/EnforceCodeStyleInBuild\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c\"\u003e\u0026lt;!-- Enable specific analyzer categories --\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nt\"\u003e\u0026lt;AnalysisMode\u0026gt;\u003c/span\u003eAll\u003cspan class=\"nt\"\u003e\u0026lt;/AnalysisMode\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003e\u0026lt;/PropertyGroup\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\n\n\n\u003ch3 id=\"roslyn-analyzers-static-analysis-built-in\"\u003e\u003ca href=\"/posts/dotnet-power-of-ten-rules/#roslyn-analyzers-static-analysis-built-in\" title=\"Roslyn Analyzers: Static Analysis Built In\"\u003eRoslyn Analyzers: Static Analysis Built In\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eUnlike 2006 C compilers, \u003ca href=\"https://learn.microsoft.com/en-us/visualstudio/code-quality/roslyn-analyzers-overview\" target=\"_blank\" rel=\"noopener external noreferrer\"\u003eRoslyn analyzers\u003c/a\u003e provide deep semantic analysis. Rules like \u003ca href=\"https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca2000\" target=\"_blank\" rel=\"noopener external noreferrer\"\u003eCA2000\u003c/a\u003e catch undisposed resources, \u003ca href=\"https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1806\" target=\"_blank\" rel=\"noopener external noreferrer\"\u003eCA1806\u003c/a\u003e flags ignored return values, and \u003ca href=\"https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1062\" target=\"_blank\" rel=\"noopener external noreferrer\"\u003eCA1062\u003c/a\u003e enforces parameter validation: all without running your code. This compile-time safety net would have been science fiction to NASA\u0026rsquo;s 2006 C developers.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"editorconfig-for-team-standards\"\u003e\u003ca href=\"/posts/dotnet-power-of-ten-rules/#editorconfig-for-team-standards\" title=\"EditorConfig for Team Standards\"\u003eEditorConfig for Team Standards\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003e\u003ca href=\"https://editorconfig.org/\" target=\"_blank\" rel=\"noopener external noreferrer\"\u003eEditorConfig files\u003c/a\u003e let you enforce code style and quality rules across your team. Beyond basic formatting, you can control diagnostic severity for specific analyzer rules: turning \u003ca href=\"https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1062\" target=\"_blank\" rel=\"noopener external noreferrer\"\u003eCA1062\u003c/a\u003e (validate arguments) into build errors while suppressing overly strict rules like \u003ca href=\"https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1303\" target=\"_blank\" rel=\"noopener external noreferrer\"\u003eCA1303\u003c/a\u003e (localized strings). This ensures consistent standards without lengthy code review discussions about style preferences.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"multiple-layers-of-analysis\"\u003e\u003ca href=\"/posts/dotnet-power-of-ten-rules/#multiple-layers-of-analysis\" title=\"Multiple Layers of Analysis\"\u003eMultiple Layers of Analysis\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eModern .NET provides defense in depth: compiler warnings catch syntax issues, \u003ca href=\"https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/overview\" target=\"_blank\" rel=\"noopener external noreferrer\"\u003eRoslyn analyzers\u003c/a\u003e enforce code quality (\u003ca href=\"https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/\" target=\"_blank\" rel=\"noopener external noreferrer\"\u003eCA* rules\u003c/a\u003e) and style (\u003ca href=\"https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/\" target=\"_blank\" rel=\"noopener external noreferrer\"\u003eIDE* rules\u003c/a\u003e). Extend this with third-party analyzers like \u003ca href=\"https://security-code-scan.github.io/\" target=\"_blank\" rel=\"noopener external noreferrer\"\u003eSecurityCodeScan\u003c/a\u003e for security vulnerabilities, \u003ca href=\"https://github.com/semihokur/AsyncFixer\" target=\"_blank\" rel=\"noopener external noreferrer\"\u003eAsyncFixer\u003c/a\u003e for async/await pitfalls, or \u003ca href=\"https://www.nuget.org/packages/Microsoft.VisualStudio.Threading.Analyzers/\" target=\"_blank\" rel=\"noopener external noreferrer\"\u003eMicrosoft.VisualStudio.Threading.Analyzers\u003c/a\u003e for threading issues. For enterprise scenarios, \u003ca href=\"https://www.sonarsource.com/products/sonarqube/\" target=\"_blank\" rel=\"noopener external noreferrer\"\u003eSonarQube\u003c/a\u003e and \u003ca href=\"https://docs.github.com/en/code-security/code-scanning/introduction-to-code-scanning/about-code-scanning-with-codeql\" target=\"_blank\" rel=\"noopener external noreferrer\"\u003eGitHub Advanced Security\u003c/a\u003e with CodeQL provide continuous security scanning.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"automated-code-review\"\u003e\u003ca href=\"/posts/dotnet-power-of-ten-rules/#automated-code-review\" title=\"Automated Code Review\"\u003eAutomated Code Review\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eIntegrate analysis into CI/CD pipelines with \u003ca href=\"https://docs.github.com/en/actions\" target=\"_blank\" rel=\"noopener external noreferrer\"\u003eGitHub Actions\u003c/a\u003e, \u003ca href=\"https://learn.microsoft.com/en-us/azure/devops/pipelines/\" target=\"_blank\" rel=\"noopener external noreferrer\"\u003eAzure Pipelines\u003c/a\u003e, or similar platforms. Configure builds to treat warnings as errors (\u003ccode\u003e/p:TreatWarningsAsErrors=true\u003c/code\u003e) so quality issues block merges automatically. This transforms static analysis from optional developer tooling into enforced team standards.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eVerdict:\u003c/strong\u003e This is the one rule where C# developers have it absurdly better than 2006 C developers. I\u0026rsquo;ve reviewed codebases where enabling \u003ccode\u003eTreatWarningsAsErrors\u003c/code\u003e found 47 bugs in 30 seconds—bugs that had been sitting there for months. Enable everything: compiler warnings, Roslyn analyzers, nullable reference types. Use \u003ccode\u003e.editorconfig\u003c/code\u003e to enforce team standards. Automate it in CI/CD so quality gates can\u0026rsquo;t be ignored when the deadline looms. There\u0026rsquo;s no excuse not to.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"summary-translation-guide\"\u003e\u003ca href=\"/posts/dotnet-power-of-ten-rules/#summary-translation-guide\" title=\"Summary: Translation Guide\"\u003eSummary: Translation Guide\u003c/a\u003e\u003c/h2\u003e\n\u003ctable\u003e\n\t\u003cthead\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003cth\u003ePower of Ten Rule\u003c/th\u003e\n\t\t\t\t\t\u003cth\u003eC#/.NET Status\u003c/th\u003e\n\t\t\t\t\t\u003cth\u003eModern Equivalent\u003c/th\u003e\n\t\t\t\u003c/tr\u003e\n\t\u003c/thead\u003e\n\t\u003ctbody\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e1. Simple control flow\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003ePartially valid\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003eAvoid \u003ccode\u003egoto\u003c/code\u003e; recursion OK with care\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e2. Bounded loops\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003eValid\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003eUse \u003ccode\u003eforeach\u003c/code\u003e, LINQ \u003ccode\u003eTake()\u003c/code\u003e, \u003ccode\u003eCancellationToken\u003c/code\u003e\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e3. No dynamic allocation\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003eNot applicable\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003eMinimize allocations with \u003ccode\u003eSpan\u0026lt;T\u0026gt;\u003c/code\u003e, \u003ca href=\"https://learn.microsoft.com/en-us/dotnet/api/system.buffers.arraypool-1\" target=\"_blank\" rel=\"noopener external noreferrer\"\u003e\u003ccode\u003eArrayPool\u0026lt;T\u0026gt;\u003c/code\u003e\u003c/a\u003e\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e4. Max 60 lines per function\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e\u003cstrong\u003eAbsolutely valid\u003c/strong\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003eEnable \u003ca href=\"https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1505\" target=\"_blank\" rel=\"noopener external noreferrer\"\u003eCA1505\u003c/a\u003e, use local functions, extract methods\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e5. Two assertions per function\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003eValid principle\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ca href=\"https://learn.microsoft.com/en-us/dotnet/csharp/nullable-references\" target=\"_blank\" rel=\"noopener external noreferrer\"\u003eNullable types\u003c/a\u003e + guard clauses + \u003ca href=\"https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.debug.assert\" target=\"_blank\" rel=\"noopener external noreferrer\"\u003e\u003ccode\u003eDebug.Assert\u003c/code\u003e\u003c/a\u003e\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e6. Minimal variable scope\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e\u003cstrong\u003eAbsolutely valid\u003c/strong\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003ePattern matching, using declarations, block scope\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e7. Check all returns\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e\u003cstrong\u003eAbsolutely valid\u003c/strong\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003eValidate parameters, handle exceptions, use \u003ccode\u003eTry*\u003c/code\u003e pattern\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e8. Limited preprocessor\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003eMostly irrelevant\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003eC# preprocessor already constrained; use DI for config\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e9. Restrict pointers\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003eMostly irrelevant\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003eUse \u003ca href=\"https://learn.microsoft.com/en-us/dotnet/api/system.span-1\" target=\"_blank\" rel=\"noopener external noreferrer\"\u003e\u003ccode\u003eSpan\u0026lt;T\u0026gt;\u003c/code\u003e\u003c/a\u003e, \u003ccode\u003eref\u003c/code\u003e/\u003ccode\u003ein\u003c/code\u003e/\u003ccode\u003eout\u003c/code\u003e; reserve \u003ccode\u003eunsafe\u003c/code\u003e for interop\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e10. All warnings + static analysis\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e\u003cstrong\u003eEmphatically valid\u003c/strong\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003eEnable \u003ca href=\"https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/overview\" target=\"_blank\" rel=\"noopener external noreferrer\"\u003eall analyzers\u003c/a\u003e, nullable types, treat warnings as errors\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\u003c/tbody\u003e\n\u003c/table\u003e\n\n\n\n\n\u003ch2 id=\"the-verdict-timeless-principles-modern-implementation\"\u003e\u003ca href=\"/posts/dotnet-power-of-ten-rules/#the-verdict-timeless-principles-modern-implementation\" title=\"The Verdict: Timeless Principles, Modern Implementation\"\u003eThe Verdict: Timeless Principles, Modern Implementation\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eFour rules (4, 6, 7, and 10) transfer directly with superior tooling. Function length limits, minimal scope, return value checking, static analysis—all work better in 2025 than 2006 C. Three rules (3, 8, 9) become largely irrelevant because .NET\u0026rsquo;s managed runtime provides better abstractions than manual memory management or preprocessor macros. The remaining rules (1, 2, 5) require contextual interpretation. Their principles remain sound, but modern C#\u0026rsquo;s language features and runtime safety nets fundamentally change implementation.\u003c/p\u003e\n\u003cp\u003eGerard Holzmann\u0026rsquo;s rules weren\u0026rsquo;t about C syntax. They encoded deeper principles—predictability, analyzability, defensive programming—that transcend any specific language. What\u0026rsquo;s different in 2025? Modern C# gives you powerful tools to enforce these principles without bare-metal constraints. \u003ca href=\"https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/overview\" target=\"_blank\" rel=\"noopener external noreferrer\"\u003eRoslyn analyzers\u003c/a\u003e catch bugs at compile time that required runtime assertions in embedded C. \u003ca href=\"https://learn.microsoft.com/en-us/dotnet/csharp/nullable-references\" target=\"_blank\" rel=\"noopener external noreferrer\"\u003eNullable reference types\u003c/a\u003e prevent null dereferences before code runs. \u003ca href=\"https://learn.microsoft.com/en-us/dotnet/api/system.span-1\" target=\"_blank\" rel=\"noopener external noreferrer\"\u003e\u003ccode\u003eSpan\u0026lt;T\u0026gt;\u003c/code\u003e\u003c/a\u003e delivers pointer-like performance with array-like safety.\u003c/p\u003e\n\u003cp\u003eFor safety-critical C# code—medical devices, financial systems, industrial control—absolutely adopt these principles. Enable every analyzer. Enforce short functions. Validate everything. Treat warnings as errors. But don\u0026rsquo;t cargo-cult embedded C constraints just because NASA used them. Embrace the GC when it makes sense, use modern abstractions that eliminate entire bug categories, and let the type system catch errors at compile time instead of in production. Your code will be safer, more maintainable, and more correct. Which is what Gerard Holzmann wanted all along.\u003c/p\u003e\n","date_modified":"2026-05-26T10:22:03+02:00","date_published":"2025-12-10T16:45:00+01:00","id":"https://daily-devops.net/posts/dotnet-power-of-ten-rules/","language":"en","summary":"Holzmann's safety-critical coding rules hit harder in modern C#: Roslyn analyzers, nullable types, and the type system enforce what C only wished.\n","tags":["csharp","dotnet","bestpractices","codequality","softwareengineering"],"title":"Power of Ten Rules: More Relevant Than Ever for .NET","url":"https://daily-devops.net/posts/dotnet-power-of-ten-rules/"},{"authors":[{"name":"Martin Stühmer","url":"https://daily-devops.net/authors/martin/"}],"content_html":"\u003cp\u003eCode metrics have become a standard feature in modern development environments, yet their implementation and interpretation often leave much to be desired. While Visual Studio and .NET provide comprehensive code metrics analysis, the way these metrics are configured, presented, and (more critically) acted upon reveals a fundamental disconnect between measurement and meaningful improvement.\u003c/p\u003e\n\u003cp\u003eWhat code metrics actually measure, how to configure them properly, and (more importantly) why blindly following thresholds without understanding context is, frankly, a recipe for misguided refactoring efforts that waste your team\u0026rsquo;s time and actively damage your codebase.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"understanding-code-metrics-what-are-we-actually-measuring\"\u003e\u003ca href=\"/posts/code-metrics-configuration/#understanding-code-metrics-what-are-we-actually-measuring\" title=\"Understanding Code Metrics: What Are We Actually Measuring?\"\u003eUnderstanding Code Metrics: What Are We Actually Measuring?\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eVisual Studio provides several key code metrics, each designed to quantify different aspects of code complexity and maintainability. Understanding what these numbers actually represent is essential before you start making decisions based on them.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"maintainability-index-mi\"\u003e\u003ca href=\"/posts/code-metrics-configuration/#maintainability-index-mi\" title=\"Maintainability Index (MI)\"\u003eMaintainability Index (MI)\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eThe \u003cstrong\u003eMaintainability Index\u003c/strong\u003e is a composite metric ranging from 0 to 100, where higher values supposedly indicate better maintainability. Microsoft\u0026rsquo;s thresholds suggest:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003eGreen (20 to 100)\u003c/strong\u003e: Good maintainability\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eYellow (10 to 19)\u003c/strong\u003e: Moderate maintainability\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eRed (0 to 9)\u003c/strong\u003e: Poor maintainability\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eThe formula considers cyclomatic complexity, lines of code, and computational complexity. However, this single number masks important nuances. A method with a high maintainability index might still be poorly designed if it violates single responsibility principles or lacks proper abstraction. Conversely, a legitimately complex algorithm might score poorly despite being optimally implemented.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"cyclomatic-complexity\"\u003e\u003ca href=\"/posts/code-metrics-configuration/#cyclomatic-complexity\" title=\"Cyclomatic Complexity\"\u003eCyclomatic Complexity\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eThis metric counts the number of linearly independent paths through a method\u0026rsquo;s code. Each \u003ccode\u003eif\u003c/code\u003e, \u003ccode\u003ewhile\u003c/code\u003e, \u003ccode\u003efor\u003c/code\u003e, \u003ccode\u003ecase\u003c/code\u003e, and logical operator (\u003ccode\u003e\u0026amp;\u0026amp;\u003c/code\u003e, \u003ccode\u003e||\u003c/code\u003e) increases the count. The conventional wisdom suggests:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003e1 to 10\u003c/strong\u003e: Simple, low risk\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e11 to 20\u003c/strong\u003e: Moderate complexity\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e21 to 50\u003c/strong\u003e: High complexity\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e50+\u003c/strong\u003e: Untestable, critical risk\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eWhile cyclomatic complexity provides valuable insights into testability, it can be seriously misleading. A well-structured method with clear guard clauses might score higher than a convoluted method with fewer branches but objectively worse logical flow. The metric measures branches, not clarity.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"lines-of-code-loc\"\u003e\u003ca href=\"/posts/code-metrics-configuration/#lines-of-code-loc\" title=\"Lines of Code (LOC)\"\u003eLines of Code (LOC)\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eThis counts executable lines, excluding comments and blank lines. While straightforward, LOC is perhaps the most misunderstood metric. \u003cstrong\u003eA high line count doesn\u0026rsquo;t automatically indicate poor quality\u003c/strong\u003e. It might represent thorough error handling, comprehensive validation, or simply necessary business logic. Splitting a 200-line method into ten 20-line methods doesn\u0026rsquo;t magically improve anything if those ten methods are tightly coupled and need to be understood together anyway.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"depth-of-inheritance\"\u003e\u003ca href=\"/posts/code-metrics-configuration/#depth-of-inheritance\" title=\"Depth of Inheritance\"\u003eDepth of Inheritance\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eThis measures how many classes are in the inheritance hierarchy above a type. Deep inheritance trees can indicate over-engineering, but shallow hierarchies aren\u0026rsquo;t automatically better. The appropriate depth depends entirely on the domain model and design patterns being employed. Blindly flattening inheritance hierarchies to satisfy a metric threshold often results in awkward composition patterns or duplicated logic.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"class-coupling\"\u003e\u003ca href=\"/posts/code-metrics-configuration/#class-coupling\" title=\"Class Coupling\"\u003eClass Coupling\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eThis counts the number of unique types a class references. High coupling suggests tight dependencies and reduced modularity, but some coupling is inevitable and even desirable when working with framework types or established patterns. A web controller that references request models, response models, service interfaces, and result types will naturally have higher coupling, and that\u0026rsquo;s perfectly fine.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"the-critical-flaw-arbitrary-thresholds-without-context\"\u003e\u003ca href=\"/posts/code-metrics-configuration/#the-critical-flaw-arbitrary-thresholds-without-context\" title=\"The Critical Flaw: Arbitrary Thresholds Without Context\"\u003eThe Critical Flaw: Arbitrary Thresholds Without Context\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eThis is where the industry consistently gets it wrong. \u003cstrong\u003eApplying universal thresholds to code metrics fundamentally ignores the reality that different code serves different purposes\u003c/strong\u003e. The notion that a cyclomatic complexity of 10 is universally \u0026ldquo;good\u0026rdquo; while 11 is suddenly \u0026ldquo;problematic\u0026rdquo; is, quite honestly, nonsense. It\u0026rsquo;s the software equivalent of declaring that all methods must be exactly 15 lines long because someone once read that in a blog post.\u003c/p\u003e\n\u003cp\u003eConsider a service orchestrator that validates input, checks permissions, coordinates multiple services, handles errors, and logs operations. Such a method might legitimately have a cyclomatic complexity of 15 to 20 while being perfectly maintainable if it\u0026rsquo;s well-structured with clear sections and appropriate abstractions. In 2023, I watched a team spend two full weeks refactoring a beautifully clear order processing coordinator, splitting it into 18 micro-methods scattered across four files, all because SonarQube flagged it red. The result? Nobody on the team could follow the execution flow anymore without constantly jumping between files. The cyclomatic complexity went down. The actual maintainability went down with it.\u003c/p\u003e\n\u003cp\u003eConversely, a method with a cyclomatic complexity of 5 could be an absolute maintenance nightmare if it contains obscure bit manipulation, poorly named variables, or nested ternary operators that mask its true intent. I\u0026rsquo;ve seen plenty of \u0026ldquo;low complexity\u0026rdquo; code that nobody dares touch because figuring out what it actually does requires a whiteboard and strong coffee.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eThe metric is a signal, not a verdict.\u003c/strong\u003e Treating threshold violations as automated refactoring triggers leads to cargo cult programming. You end up splitting methods not because it actually improves design, but because a tool says a number is too high. If you\u0026rsquo;re refactoring solely to satisfy a metric, you\u0026rsquo;re doing it wrong. Full stop.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"configuring-code-metrics-analysis-in-net\"\u003e\u003ca href=\"/posts/code-metrics-configuration/#configuring-code-metrics-analysis-in-net\" title=\"Configuring Code Metrics Analysis in .NET\"\u003eConfiguring Code Metrics Analysis in .NET\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eDespite these significant limitations, code metrics remain useful when properly configured and (this is crucial) interpreted with actual human judgment. The key is setting them up as discussion triggers, not as automated quality gates. Here\u0026rsquo;s how to configure them effectively without falling into the trap of metric-driven madness.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"enabling-code-metrics-in-visual-studio\"\u003e\u003ca href=\"/posts/code-metrics-configuration/#enabling-code-metrics-in-visual-studio\" title=\"Enabling Code Metrics in Visual Studio\"\u003eEnabling Code Metrics in Visual Studio\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eCode metrics can be calculated for entire solutions, projects, or individual code files. Visual Studio makes this relatively straightforward, even if the UI hasn\u0026rsquo;t been meaningfully updated since roughly 2010.\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e\n\u003cp\u003e\u003cstrong\u003eSolution or Project Level\u003c/strong\u003e: Right-click on the solution or project in Solution Explorer, then select \u003cstrong\u003eAnalyze and Code Cleanup\u003c/strong\u003e followed by \u003cstrong\u003eCalculate Code Metrics\u003c/strong\u003e. This gives you a basic overview, though frankly the results window is a masterclass in wasted screen real estate.\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003cstrong\u003eEditorConfig Configuration\u003c/strong\u003e: For more meaningful control, use \u003ccode\u003e.editorconfig\u003c/code\u003e to configure specific metric thresholds:\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ol\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ini\" data-lang=\"ini\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Code metrics configuration\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003e[*.cs]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Cyclomatic complexity warning threshold\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"na\"\u003edotnet_diagnostic.CA1502.severity\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s\"\u003ewarning\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"na\"\u003edotnet_code_quality.CA1502.cyclomatic_complexity\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s\"\u003e25\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Maintainability index warning threshold\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"na\"\u003edotnet_diagnostic.CA1501.severity\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s\"\u003ewarning\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"na\"\u003edotnet_code_quality.CA1501.maintainability_index\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s\"\u003e15\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Maximum lines of code per method\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"na\"\u003edotnet_diagnostic.CA1505.severity\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s\"\u003esuggestion\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"na\"\u003edotnet_code_quality.CA1505.lines_of_code\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s\"\u003e100\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\n\n\n\u003ch3 id=\"the-ca1509-rule-understanding-invalid-configuration\"\u003e\u003ca href=\"/posts/code-metrics-configuration/#the-ca1509-rule-understanding-invalid-configuration\" title=\"The CA1509 Rule: Understanding Invalid Configuration\"\u003eThe CA1509 Rule: Understanding Invalid Configuration\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eMicrosoft\u0026rsquo;s documentation on \u003cstrong\u003eCA1509\u003c/strong\u003e addresses a specific but genuinely important issue: \u003cstrong\u003einvalid analyzer configuration values\u003c/strong\u003e. This rule fires when you\u0026rsquo;ve misconfigured code analysis settings, typically by:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eProviding non-numeric values where numbers are expected\u003c/li\u003e\n\u003cli\u003eUsing values outside acceptable ranges\u003c/li\u003e\n\u003cli\u003eSpecifying invalid enumeration values\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eExample of \u003cstrong\u003einvalid configuration\u003c/strong\u003e (that will silently fail in older tooling):\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ini\" data-lang=\"ini\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# WRONG: Non-numeric value\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"na\"\u003edotnet_code_quality.CA1502.cyclomatic_complexity\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s\"\u003ehigh\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# WRONG: Value out of range\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"na\"\u003edotnet_code_quality.CA1502.cyclomatic_complexity\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s\"\u003e-5\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# WRONG: Invalid severity level\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"na\"\u003edotnet_diagnostic.CA1502.severity\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s\"\u003ecritical\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eCorrect configuration:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ini\" data-lang=\"ini\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# CORRECT: Numeric value in valid range\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"na\"\u003edotnet_code_quality.CA1502.cyclomatic_complexity\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s\"\u003e25\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# CORRECT: Valid severity level\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"na\"\u003edotnet_diagnostic.CA1502.severity\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s\"\u003ewarning\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe CA1509 rule exists precisely because \u003cstrong\u003esilent failures in configuration can lead to dangerously false confidence\u003c/strong\u003e. You might genuinely believe you\u0026rsquo;ve enforced strict complexity limits when, in reality, your misconfiguration has effectively disabled the checks entirely. In a previous role, our team operated for three months under the assumption that our code quality gates were being enforced. They weren\u0026rsquo;t. A single typo (\u003ccode\u003ewarnin\u003c/code\u003e instead of \u003ccode\u003ewarning\u003c/code\u003e) in the \u003ccode\u003e.editorconfig\u003c/code\u003e had neutered the entire setup. Nobody noticed until a code review caught something that should have been flagged automatically.\u003c/p\u003e\n\u003cp\u003eThis is particularly insidious in team environments where configuration files are committed to source control. A typo or misunderstanding propagates across the entire team, systematically undermining code quality initiatives without anyone noticing until much later. Usually someone eventually asks \u0026ldquo;Why isn\u0026rsquo;t this rule triggering?\u0026rdquo; and then you discover that your quality process has been theatre for weeks or months.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"project-level-configuration\"\u003e\u003ca href=\"/posts/code-metrics-configuration/#project-level-configuration\" title=\"Project-Level Configuration\"\u003eProject-Level Configuration\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eFor comprehensive control, configure code metrics in your \u003ccode\u003e.csproj\u003c/code\u003e or \u003ccode\u003eDirectory.Build.props\u003c/code\u003e file:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-xml\" data-lang=\"xml\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003e\u0026lt;PropertyGroup\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"c\"\u003e\u0026lt;!-- Enable all code analysis rules --\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"nt\"\u003e\u0026lt;EnableNETAnalyzers\u0026gt;\u003c/span\u003etrue\u003cspan class=\"nt\"\u003e\u0026lt;/EnableNETAnalyzers\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"nt\"\u003e\u0026lt;AnalysisLevel\u0026gt;\u003c/span\u003elatest\u003cspan class=\"nt\"\u003e\u0026lt;/AnalysisLevel\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"nt\"\u003e\u0026lt;EnforceCodeStyleInBuild\u0026gt;\u003c/span\u003etrue\u003cspan class=\"nt\"\u003e\u0026lt;/EnforceCodeStyleInBuild\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"c\"\u003e\u0026lt;!-- Treat specific metrics violations as errors in CI/CD --\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"nt\"\u003e\u0026lt;WarningsAsErrors\u0026gt;\u003c/span\u003eCA1502;CA1505\u003cspan class=\"nt\"\u003e\u0026lt;/WarningsAsErrors\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003e\u0026lt;/PropertyGroup\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003e\u0026lt;ItemGroup\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"c\"\u003e\u0026lt;!-- Add code analysis package for additional metrics --\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"nt\"\u003e\u0026lt;PackageReference\u003c/span\u003e \u003cspan class=\"na\"\u003eInclude=\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;Microsoft.CodeAnalysis.NetAnalyzers\u0026#34;\u003c/span\u003e \u003cspan class=\"na\"\u003eVersion=\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;8.0.0\u0026#34;\u003c/span\u003e \u003cspan class=\"nt\"\u003e/\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003e\u0026lt;/ItemGroup\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis configuration ensures that:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eCode analysis runs during every build (not just when you remember to click \u0026ldquo;Calculate Metrics\u0026rdquo;)\u003c/li\u003e\n\u003cli\u003eSpecific violations break the build in CI/CD environments (preventing \u0026ldquo;I\u0026rsquo;ll fix it later\u0026rdquo; syndrome)\u003c/li\u003e\n\u003cli\u003eTeams maintain consistent standards across all machines (no more \u0026ldquo;it works on my machine\u0026rdquo; with different analyzer settings)\u003c/li\u003e\n\u003c/ul\u003e\n\n\n\n\n\u003ch2 id=\"real-world-examples-when-metrics-mislead\"\u003e\u003ca href=\"/posts/code-metrics-configuration/#real-world-examples-when-metrics-mislead\" title=\"Real-World Examples: When Metrics Mislead\"\u003eReal-World Examples: When Metrics Mislead\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eHere are some actual scenarios from production codebases where metrics painted an incomplete (or outright misleading) picture. These are real examples I\u0026rsquo;ve encountered, though I\u0026rsquo;ve simplified them slightly for clarity.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"example-1-the-high-complexity-coordinator\"\u003e\u003ca href=\"/posts/code-metrics-configuration/#example-1-the-high-complexity-coordinator\" title=\"Example 1: The High-Complexity Coordinator\"\u003eExample 1: The High-Complexity Coordinator\u003c/a\u003e\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kd\"\u003easync\u003c/span\u003e \u003cspan class=\"n\"\u003eTask\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eOrderResult\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003eProcessOrderAsync\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eOrderRequest\u003c/span\u003e \u003cspan class=\"n\"\u003erequest\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e// Cyclomatic Complexity: 18\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003erequest\u003c/span\u003e \u003cspan class=\"p\"\u003e==\u003c/span\u003e \u003cspan class=\"kc\"\u003enull\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003ethrow\u003c/span\u003e \u003cspan class=\"k\"\u003enew\u003c/span\u003e \u003cspan class=\"n\"\u003eArgumentNullException\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003enameof\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003erequest\u003c/span\u003e\u003cspan class=\"p\"\u003e));\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(!\u003c/span\u003e\u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003e_authService\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eValidateUserAsync\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003erequest\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eUserId\u003c/span\u003e\u003cspan class=\"p\"\u003e))\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003eOrderResult\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eUnauthorized\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003erequest\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eItems\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eCount\u003c/span\u003e \u003cspan class=\"p\"\u003e==\u003c/span\u003e \u003cspan class=\"m\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003eOrderResult\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eInvalid\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;No items in order\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003einventory\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003e_inventoryService\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eCheckAvailabilityAsync\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003erequest\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eItems\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(!\u003c/span\u003e\u003cspan class=\"n\"\u003einventory\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAllAvailable\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003eOrderResult\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eOutOfStock\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003einventory\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eUnavailableItems\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003epayment\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003e_paymentService\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eProcessPaymentAsync\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003erequest\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ePayment\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(!\u003c/span\u003e\u003cspan class=\"n\"\u003epayment\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eSuccess\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003eOrderResult\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ePaymentFailed\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003epayment\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eReason\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003erequest\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eRequiresShipping\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003eshipping\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003e_shippingService\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eCalculateShippingAsync\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003erequest\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAddress\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(!\u003c/span\u003e\u003cspan class=\"n\"\u003eshipping\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eCanDeliver\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003eOrderResult\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eShippingUnavailable\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003eorder\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003e_orderRepository\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eCreateOrderAsync\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003erequest\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003epayment\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003einventory\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003e_notificationService\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eSendOrderConfirmationAsync\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eorder\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003eOrderResult\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eSuccess\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eorder\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eId\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cstrong\u003eMetric\u003c/strong\u003e: Cyclomatic complexity of 18\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eTool Recommendation\u003c/strong\u003e: Split this method immediately into smaller methods to reduce complexity.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eReality\u003c/strong\u003e: This is a well-structured orchestration method with clear, sequential steps. Each condition represents a legitimate business rule. The flow is obvious: validate, check inventory, process payment, arrange shipping, create order. Splitting this would scatter related logic across multiple methods without improving understandability. In fact, it would make it worse because you\u0026rsquo;d need to trace through multiple method calls to understand what should be a single logical operation.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"example-2-the-deceptively-simple-method\"\u003e\u003ca href=\"/posts/code-metrics-configuration/#example-2-the-deceptively-simple-method\" title=\"Example 2: The Deceptively Simple Method\"\u003eExample 2: The Deceptively Simple Method\u003c/a\u003e\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"n\"\u003eCalculateDiscount\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eCustomer\u003c/span\u003e \u003cspan class=\"n\"\u003ecustomer\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eOrder\u003c/span\u003e \u003cspan class=\"n\"\u003eorder\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e// Cyclomatic Complexity: 3\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ecustomer\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eTier\u003c/span\u003e \u003cspan class=\"p\"\u003e==\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;Gold\u0026#34;\u003c/span\u003e \u003cspan class=\"p\"\u003e\u0026amp;\u0026amp;\u003c/span\u003e \u003cspan class=\"n\"\u003eorder\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eTotal\u003c/span\u003e \u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"m\"\u003e1000\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e?\u003c/span\u003e \u003cspan class=\"m\"\u003e20\u003c/span\u003e \u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e           \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ecustomer\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eTier\u003c/span\u003e \u003cspan class=\"p\"\u003e==\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;Silver\u0026#34;\u003c/span\u003e \u003cspan class=\"p\"\u003e\u0026amp;\u0026amp;\u003c/span\u003e \u003cspan class=\"n\"\u003eorder\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eTotal\u003c/span\u003e \u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"m\"\u003e500\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e?\u003c/span\u003e \u003cspan class=\"m\"\u003e10\u003c/span\u003e \u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"m\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cstrong\u003eMetric\u003c/strong\u003e: Cyclomatic complexity of 3\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eTool Recommendation\u003c/strong\u003e: Acceptable, no action needed\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eReality\u003c/strong\u003e: This nested ternary operator is significantly harder to understand than its complexity suggests. The metric doesn\u0026rsquo;t capture cognitive load. This should be refactored into explicit if/else blocks or (better) a strategy pattern, despite having \u0026ldquo;acceptable\u0026rdquo; metrics. Low complexity doesn\u0026rsquo;t automatically mean readable code.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"example-3-the-configuration-validation\"\u003e\u003ca href=\"/posts/code-metrics-configuration/#example-3-the-configuration-validation\" title=\"Example 3: The Configuration Validation\"\u003eExample 3: The Configuration Validation\u003c/a\u003e\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kt\"\u003ebool\u003c/span\u003e \u003cspan class=\"n\"\u003eValidateConfiguration\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eAppConfiguration\u003c/span\u003e \u003cspan class=\"n\"\u003econfig\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e// Cyclomatic Complexity: 25\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003estring\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eIsNullOrEmpty\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003econfig\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eDatabaseConnection\u003c/span\u003e\u003cspan class=\"p\"\u003e))\u003c/span\u003e \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"kc\"\u003efalse\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003estring\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eIsNullOrEmpty\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003econfig\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eApiKey\u003c/span\u003e\u003cspan class=\"p\"\u003e))\u003c/span\u003e \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"kc\"\u003efalse\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003econfig\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eTimeout\u003c/span\u003e \u003cspan class=\"p\"\u003e\u0026lt;=\u003c/span\u003e \u003cspan class=\"m\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"kc\"\u003efalse\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003econfig\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eMaxRetries\u003c/span\u003e \u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e \u003cspan class=\"m\"\u003e0\u003c/span\u003e \u003cspan class=\"p\"\u003e||\u003c/span\u003e \u003cspan class=\"n\"\u003econfig\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eMaxRetries\u003c/span\u003e \u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"m\"\u003e10\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"kc\"\u003efalse\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003estring\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eIsNullOrEmpty\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003econfig\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eServiceUrl\u003c/span\u003e\u003cspan class=\"p\"\u003e))\u003c/span\u003e \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"kc\"\u003efalse\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(!\u003c/span\u003e\u003cspan class=\"n\"\u003eUri\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eTryCreate\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003econfig\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eServiceUrl\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eUriKind\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAbsolute\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"k\"\u003eout\u003c/span\u003e \u003cspan class=\"n\"\u003e_\u003c/span\u003e\u003cspan class=\"p\"\u003e))\u003c/span\u003e \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"kc\"\u003efalse\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003econfig\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eCacheSize\u003c/span\u003e \u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e \u003cspan class=\"m\"\u003e0\u003c/span\u003e \u003cspan class=\"p\"\u003e||\u003c/span\u003e \u003cspan class=\"n\"\u003econfig\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eCacheSize\u003c/span\u003e \u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"m\"\u003e10000\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"kc\"\u003efalse\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003estring\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eIsNullOrEmpty\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003econfig\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eLogPath\u003c/span\u003e\u003cspan class=\"p\"\u003e))\u003c/span\u003e \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"kc\"\u003efalse\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003econfig\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eWorkers\u003c/span\u003e \u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e \u003cspan class=\"m\"\u003e1\u003c/span\u003e \u003cspan class=\"p\"\u003e||\u003c/span\u003e \u003cspan class=\"n\"\u003econfig\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eWorkers\u003c/span\u003e \u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"m\"\u003e100\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"kc\"\u003efalse\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003estring\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eIsNullOrEmpty\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003econfig\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eEncryptionKey\u003c/span\u003e\u003cspan class=\"p\"\u003e))\u003c/span\u003e \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"kc\"\u003efalse\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003econfig\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eEncryptionKey\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eLength\u003c/span\u003e \u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e \u003cspan class=\"m\"\u003e16\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"kc\"\u003efalse\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e// ... and so on\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cstrong\u003eMetric\u003c/strong\u003e: Cyclomatic complexity of 25+\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eTool Recommendation\u003c/strong\u003e: CRITICAL! Refactor immediately! Code smell detected!\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eReality\u003c/strong\u003e: This is a guard clause pattern performing straightforward validation. Each check is independent and clear. While this could potentially be refactored to use a validation framework like FluentValidation, the current implementation is perfectly maintainable. Anyone can read this method and immediately understand what\u0026rsquo;s being validated. \u003cstrong\u003eThe high complexity reflects the number of configuration options, not poor design.\u003c/strong\u003e Refactoring this just to lower a number would likely make it more complex to understand, not less.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"best-practices-for-code-metrics-configuration\"\u003e\u003ca href=\"/posts/code-metrics-configuration/#best-practices-for-code-metrics-configuration\" title=\"Best Practices for Code Metrics Configuration\"\u003eBest Practices for Code Metrics Configuration\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eBased on years of working with code metrics across various projects (and watching teams both succeed and fail spectacularly at using them), here are practical recommendations:\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"1-set-contextual-thresholds\"\u003e\u003ca href=\"/posts/code-metrics-configuration/#1-set-contextual-thresholds\" title=\"1. Set Contextual Thresholds\"\u003e1. Set Contextual Thresholds\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eDon\u0026rsquo;t use Microsoft\u0026rsquo;s default thresholds blindly. They were probably set by someone who never maintained a real-world enterprise application. Analyze your actual codebase and set realistic limits based on what you\u0026rsquo;re building:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ini\" data-lang=\"ini\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003e[*.cs]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# More lenient for coordinators and facades\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"na\"\u003edotnet_code_quality.CA1502.cyclomatic_complexity\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s\"\u003e25\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Stricter for business logic\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003e[**/Domain/**.cs]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"na\"\u003edotnet_code_quality.CA1502.cyclomatic_complexity\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s\"\u003e15\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Most lenient for generated code\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003e[**/Generated/**.cs]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"na\"\u003edotnet_diagnostic.CA1502.severity\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s\"\u003enone\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\n\n\n\u003ch3 id=\"2-combine-metrics-with-code-reviews\"\u003e\u003ca href=\"/posts/code-metrics-configuration/#2-combine-metrics-with-code-reviews\" title=\"2. Combine Metrics with Code Reviews\"\u003e2. Combine Metrics with Code Reviews\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eMetrics are \u003cstrong\u003eindicators for discussion\u003c/strong\u003e, not automatic refactoring triggers. This cannot be emphasized enough. When a metric threshold is exceeded:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003eReview the code with the team (not just accept what the tool says)\u003c/li\u003e\n\u003cli\u003eDiscuss whether the complexity is essential or accidental\u003c/li\u003e\n\u003cli\u003eConsider readability and maintainability alongside the numbers\u003c/li\u003e\n\u003cli\u003eRefactor only when there\u0026rsquo;s genuine consensus that it improves the code\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eIf someone suggests refactoring purely because \u0026ldquo;the metric is red,\u0026rdquo; push back. Ask them to explain what specifically is hard to understand or maintain. If they can\u0026rsquo;t articulate a concrete problem beyond \u0026ldquo;the number is high,\u0026rdquo; the refactoring probably isn\u0026rsquo;t worth doing.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"3-track-trends-not-absolutes\"\u003e\u003ca href=\"/posts/code-metrics-configuration/#3-track-trends-not-absolutes\" title=\"3. Track Trends, Not Absolutes\"\u003e3. Track Trends, Not Absolutes\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eFocus on whether metrics are improving or degrading over time, not on hitting specific numbers. A codebase where average complexity is gradually decreasing is healthier than one where you\u0026rsquo;ve arbitrarily set thresholds that everyone routinely suppresses:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-xml\" data-lang=\"xml\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c\"\u003e\u0026lt;!-- Ratchet approach: prevent regression --\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003e\u0026lt;PropertyGroup\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"nt\"\u003e\u0026lt;MaxAllowedCyclomaticComplexity\u0026gt;\u003c/span\u003e25\u003cspan class=\"nt\"\u003e\u0026lt;/MaxAllowedCyclomaticComplexity\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"c\"\u003e\u0026lt;!-- Gradually decrease this threshold over time --\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003e\u0026lt;/PropertyGroup\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\n\n\n\u003ch3 id=\"4-validate-your-configuration\"\u003e\u003ca href=\"/posts/code-metrics-configuration/#4-validate-your-configuration\" title=\"4. Validate Your Configuration\"\u003e4. Validate Your Configuration\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eEnsure your \u003ccode\u003e.editorconfig\u003c/code\u003e and project settings are actually valid and doing what you think they\u0026rsquo;re doing:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Build with warnings as errors to catch configuration issues\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003edotnet build /p:TreatWarningsAsErrors\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"nb\"\u003etrue\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis will cause CA1509 violations (invalid configuration) to break the build, preventing those silent failures that undermine your entire quality process.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"5-document-exceptions\"\u003e\u003ca href=\"/posts/code-metrics-configuration/#5-document-exceptions\" title=\"5. Document Exceptions\"\u003e5. Document Exceptions\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eWhen you intentionally exceed thresholds (and you will), document why. Future developers (including yourself in six months) will thank you:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// Cyclomatic complexity: 22\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// JUSTIFICATION: This coordinator method orchestrates the entire checkout process.\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// Splitting it would scatter cohesive logic and reduce maintainability.\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// Reviewed: 2024-12-15, approved by architecture team.\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"na\"\u003e[SuppressMessage(\u0026#34;Microsoft.Maintainability\u0026#34;, \u0026#34;CA1502:AvoidExcessiveComplexity\u0026#34;,\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"na\"\u003e    Justification = \u0026#34;Orchestration method with clear sequential steps\u0026#34;)]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kd\"\u003easync\u003c/span\u003e \u003cspan class=\"n\"\u003eTask\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eCheckoutResult\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003eProcessCheckoutAsync\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eCheckoutRequest\u003c/span\u003e \u003cspan class=\"n\"\u003erequest\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e// Implementation\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\n\n\n\u003ch2 id=\"the-bigger-picture-metrics-as-tools-not-goals\"\u003e\u003ca href=\"/posts/code-metrics-configuration/#the-bigger-picture-metrics-as-tools-not-goals\" title=\"The Bigger Picture: Metrics as Tools, Not Goals\"\u003eThe Bigger Picture: Metrics as Tools, Not Goals\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eThe fundamental issue with code metrics isn\u0026rsquo;t the measurements themselves. It\u0026rsquo;s how we respond to them. \u003cstrong\u003eOptimizing for metrics rather than maintainability is a textbook case of Goodhart\u0026rsquo;s Law\u003c/strong\u003e: \u0026ldquo;When a measure becomes a target, it ceases to be a good measure.\u0026rdquo;\u003c/p\u003e\n\u003cp\u003eI\u0026rsquo;ve watched teams obsess over reducing cyclomatic complexity, creating elaborate abstractions and indirection that make the code objectively harder to understand, all to satisfy a tool\u0026rsquo;s threshold. I\u0026rsquo;ve seen developers split perfectly cohesive methods into multiple smaller methods that require jumping between five different files to understand the flow, all because a metric said \u0026ldquo;this is too complex.\u0026rdquo; The metric improved. The code got worse. Nobody seemed to notice the contradiction.\u003c/p\u003e\n\u003cp\u003eIn one particularly memorable case, a developer created a 200-line method that consisted entirely of calls to other private methods in the same class. Each of those methods was 10 to 15 lines and accessed the same five instance fields. The cyclomatic complexity of each individual method was beautiful. The overall design was a maintenance nightmare. But hey, the metrics dashboard was green, so management was happy.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eCode metrics should inform judgment, not replace it.\u003c/strong\u003e They highlight areas that deserve scrutiny, but the decision to refactor must be based on whether the change genuinely improves the codebase. If you find yourself refactoring code that was already clear and maintainable just to lower a number, stop. You\u0026rsquo;re making things worse while convincing yourself you\u0026rsquo;re making them better.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"configuration-checklist-for-production-systems\"\u003e\u003ca href=\"/posts/code-metrics-configuration/#configuration-checklist-for-production-systems\" title=\"Configuration Checklist for Production Systems\"\u003eConfiguration Checklist for Production Systems\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eWhen setting up code metrics for a production system, ensure you:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003eEnable all relevant analyzers\u003c/strong\u003e via \u003ccode\u003eEnableNETAnalyzers\u003c/code\u003e and \u003ccode\u003eAnalysisLevel\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eConfigure thresholds based on your codebase\u003c/strong\u003e, not arbitrary industry standards\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eValidate configuration\u003c/strong\u003e by treating warnings as errors during CI/CD builds\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eDocument your thresholds and rationale\u003c/strong\u003e in a \u003ccode\u003eCODE_METRICS.md\u003c/code\u003e file\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eReview violations as a team\u003c/strong\u003e before enforcing automatic build failures\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eCreate exemptions for special cases\u003c/strong\u003e (generated code, third-party code, test data builders)\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eMonitor trends over time\u003c/strong\u003e rather than focusing on absolute values\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eIntegrate with pull request checks\u003c/strong\u003e to catch regressions early\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eProvide training\u003c/strong\u003e to help developers understand what the metrics mean\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eRevisit thresholds regularly\u003c/strong\u003e as the codebase and team mature\u003c/li\u003e\n\u003c/ul\u003e\n\n\n\n\n\u003ch2 id=\"final-thoughts-measure-what-matters\"\u003e\u003ca href=\"/posts/code-metrics-configuration/#final-thoughts-measure-what-matters\" title=\"Final Thoughts: Measure What Matters\"\u003eFinal Thoughts: Measure What Matters\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eCode metrics are valuable tools when used with appropriate skepticism and contextual awareness. They can highlight potential issues, guide code reviews, and track quality trends over time. But they are \u003cstrong\u003ediagnostic tools, not prescriptive rules\u003c/strong\u003e. They tell you where to look, not what to do.\u003c/p\u003e\n\u003cp\u003eThe CA1509 rule\u0026rsquo;s existence (a rule about configuring rules correctly) is almost poetic in its meta-commentary on the complexity of modern code analysis. We\u0026rsquo;ve built elaborate systems to measure code quality, then needed additional rules to ensure we\u0026rsquo;re measuring correctly, all while the fundamental question remains: \u003cstrong\u003eAre we building software that solves real problems effectively?\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eConfigure your code metrics thoughtfully. Understand what they measure and (equally important) what they don\u0026rsquo;t measure. Use them to start conversations, not end them. And above all, \u003cstrong\u003eremember that the goal is maintainable, working software, not perfectly scoring metrics.\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eBecause at the end of the day, your users don\u0026rsquo;t care about your cyclomatic complexity. They care whether your software works reliably, performs well, and can be enhanced to meet their evolving needs. Code metrics can help you achieve that, but only if you resist the temptation to treat them as the goal itself.\u003c/p\u003e\n\u003cp\u003eIf you\u0026rsquo;re a developer who\u0026rsquo;s been blindly following metric thresholds, consider this your wake-up call. If you\u0026rsquo;re a team lead enforcing arbitrary complexity limits without understanding context, you\u0026rsquo;re actively harming your codebase while congratulating yourself on maintaining \u0026ldquo;quality standards.\u0026rdquo; The numbers matter, but they don\u0026rsquo;t matter more than clear, maintainable code that actually works.\u003c/p\u003e","date_modified":"2026-05-26T10:22:03+02:00","date_published":"2025-11-18T17:00:00+01:00","id":"https://daily-devops.net/posts/code-metrics-configuration/","language":"en","summary":"A critical look at .NET and Visual Studio code metrics, their configuration, and why context matters infinitely more than arbitrary thresholds.","tags":["codequality","bestpractices","csharp","dotnet","visualstudio","softwareengineering"],"title":"Code Metrics and Configuration: Beyond the Numbers Game","url":"https://daily-devops.net/posts/code-metrics-configuration/"},{"authors":[{"name":"Martin Stühmer","url":"https://daily-devops.net/authors/martin/"}],"content_html":"\u003cp\u003e\u003cstrong\u003eMicrosoft wants you to believe .NET 10 is boring. They\u0026rsquo;re right — and that\u0026rsquo;s the best news we\u0026rsquo;ve had in years.\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eAfter the aggressive pace of .NET 6 through 9, Microsoft has shipped something different: a Long-Term Support release that doesn\u0026rsquo;t try to reinvent the platform. No experimental APIs. No architectural pivots. Just \u003cstrong\u003eruntime improvements, compiler optimizations, and tooling refinements\u003c/strong\u003e that production systems actually need.\u003c/p\u003e\n\u003cp\u003e.NET 10 extends support through \u003cstrong\u003eNovember 2028\u003c/strong\u003e — three full years of stability. For teams still recovering from the .NET 8 to .NET 9 migration cycle, that timeline feels like relief.\u003c/p\u003e\n\u003cp\u003eBut let\u0026rsquo;s be clear: this isn\u0026rsquo;t innovation theater. It\u0026rsquo;s \u003cstrong\u003eengineering maturity\u003c/strong\u003e. And if you\u0026rsquo;ve been chasing framework updates instead of shipping features, this LTS window is your chance to catch up.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"performance-the-jit-compiler-finally-earned-its-keep\"\u003e\u003ca href=\"/posts/dotnet-10-released/#performance-the-jit-compiler-finally-earned-its-keep\" title=\"Performance: The JIT Compiler Finally Earned Its Keep\"\u003ePerformance: The JIT Compiler Finally Earned Its Keep\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eLet\u0026rsquo;s address the elephant in the room: \u003cstrong\u003e.NET has always promised performance\u003c/strong\u003e. Every release brings benchmarks showing 10-30% improvements. And every time, production systems see\u0026hellip; \u003cem\u003e5-7%\u003c/em\u003e if you\u0026rsquo;re lucky, meaningful only in tightly controlled scenarios.\u003c/p\u003e\n\u003cp\u003e.NET 10 changes that pattern, not through magic, but through \u003cstrong\u003esurgical optimizations\u003c/strong\u003e that compound across real workloads.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"what-actually-improved\"\u003e\u003ca href=\"/posts/dotnet-10-released/#what-actually-improved\" title=\"What Actually Improved\"\u003eWhat Actually Improved\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eThe JIT compiler now performs \u003cstrong\u003ephysical promotion\u003c/strong\u003e of struct members — meaning fewer memory indirections and tighter cache locality. It inlines array interface calls more aggressively and applies \u003cstrong\u003eadvanced loop vectorization\u003c/strong\u003e using AVX 10.2 instructions where supported.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eTranslated:\u003c/strong\u003e your hot paths get faster without code changes.\u003c/p\u003e\n\u003cp\u003eHere\u0026rsquo;s a minimal example showing the new \u003ccode\u003eSpan\u0026lt;T\u0026gt;\u003c/code\u003e conversion improvements in C# 14:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// C# 13: Manual conversion required\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eReadOnlySpan\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"kt\"\u003echar\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003eoldWay\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003emyString\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAsSpan\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// C# 14: Implicit conversion from string\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eReadOnlySpan\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"kt\"\u003echar\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003enewWay\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003emyString\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eSubtle? Yes. But in tight loops processing text-heavy workloads, these small reductions in allocations add up.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"the-reality-check\"\u003e\u003ca href=\"/posts/dotnet-10-released/#the-reality-check\" title=\"The Reality Check\"\u003eThe Reality Check\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eDon\u0026rsquo;t expect miracles. If your API is slow because of database round-trips or inefficient queries, .NET 10 won\u0026rsquo;t fix that. But if you\u0026rsquo;re running compute-heavy services — data transformations, real-time analytics, batch processing — you\u0026rsquo;ll notice \u003cstrong\u003esmoother CPU usage and fewer GC pauses\u003c/strong\u003e.\u003c/p\u003e\n\u003cp\u003eWhen I migrated a few services from .NET 8 to .NET 9 last year, we measured around 7% throughput improvement on I/O-bound APIs and nearly 12% on CPU-intensive background workers. .NET 10 builds on that foundation with more predictable memory behavior and less GC jitter.\u003c/p\u003e\n\u003cp\u003eThe performance story here isn\u0026rsquo;t \u003cem\u003etwice as fast\u003c/em\u003e — it\u0026rsquo;s \u003cstrong\u003econsistently fast under load\u003c/strong\u003e. And in production, that consistency is worth more than benchmark theater.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"sdk-stability-where-net-9-stumbled-net-10-delivers\"\u003e\u003ca href=\"/posts/dotnet-10-released/#sdk-stability-where-net-9-stumbled-net-10-delivers\" title=\"SDK Stability: Where .NET 9 Stumbled, .NET 10 Delivers\"\u003eSDK Stability: Where .NET 9 Stumbled, .NET 10 Delivers\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eHere\u0026rsquo;s an uncomfortable truth: \u003cstrong\u003e.NET 9\u0026rsquo;s SDK had rough edges\u003c/strong\u003e. Workload resolution issues, inconsistent behavior between CI and local builds, and breaking changes in \u003ccode\u003edotnet publish\u003c/code\u003e that caught teams mid-sprint.\u003c/p\u003e\n\u003cp\u003eIf you migrated to .NET 9 early, you know what I\u0026rsquo;m talking about. We hit workload mismatch errors twice during our .NET 8 → .NET 9 migration — once in CI, once in our containerized deployments. The fix involved explicit \u003ccode\u003e--self-contained\u003c/code\u003e flags and careful SDK version pinning.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"what-net-10-fixed\"\u003e\u003ca href=\"/posts/dotnet-10-released/#what-net-10-fixed\" title=\"What .NET 10 Fixed\"\u003eWhat .NET 10 Fixed\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eMicrosoft addressed the fragility. The SDK now:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003eResolves workloads deterministically\u003c/strong\u003e across environments\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eFingerprints static assets automatically\u003c/strong\u003e, eliminating cache invalidation guesswork\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eAligns container publishing\u003c/strong\u003e with the rest of the toolchain (no more surprise base image mismatches)\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eThat last point matters if you\u0026rsquo;re using \u003ccode\u003edotnet publish\u003c/code\u003e to generate container images directly. In .NET 9, it worked — until it didn\u0026rsquo;t, and then you spent an afternoon debugging why your Dockerfile suddenly produced different layers.\u003c/p\u003e\n\u003cp\u003e.NET 10 makes the build process \u003cstrong\u003eboring again\u003c/strong\u003e. And boring is what you want in CI/CD.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"the-hidden-win-roslyn-analyzers-play-nice\"\u003e\u003ca href=\"/posts/dotnet-10-released/#the-hidden-win-roslyn-analyzers-play-nice\" title=\"The Hidden Win: Roslyn Analyzers Play Nice\"\u003eThe Hidden Win: Roslyn Analyzers Play Nice\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003e\u003cem\u003eOne overlooked improvement:\u003c/em\u003e Roslyn analyzers no longer slow down incremental builds as aggressively. If your project has 15+ analyzers enabled (you should), you\u0026rsquo;ll notice faster edit-compile-test cycles.\u003c/p\u003e\n\u003cp\u003eIt\u0026rsquo;s not revolutionary. But when you\u0026rsquo;re running that loop 50 times a day, the seconds add up.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"c-14-practical-improvements-not-syntax-experiments\"\u003e\u003ca href=\"/posts/dotnet-10-released/#c-14-practical-improvements-not-syntax-experiments\" title=\"C# 14: Practical Improvements, Not Syntax Experiments\"\u003eC# 14: Practical Improvements, Not Syntax Experiments\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eC# 14 ships with .NET 10, and the language team made a smart choice: \u003cstrong\u003eno experimental features\u003c/strong\u003e. Instead, they focused on filling gaps that developers work around daily.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"field-backed-properties\"\u003e\u003ca href=\"/posts/dotnet-10-released/#field-backed-properties\" title=\"Field-Backed Properties\"\u003eField-Backed Properties\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003ePreviously, auto-properties couldn\u0026rsquo;t expose their backing fields. Now they can:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eConfiguration\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kt\"\u003estring\u003c/span\u003e \u003cspan class=\"n\"\u003eApiKey\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"k\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003eset\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e// C# 14: Access the backing field directly\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"k\"\u003evoid\u003c/span\u003e \u003cspan class=\"n\"\u003eClearSensitiveData\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003efield\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"kc\"\u003enull\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"c1\"\u003e// \u0026#39;field\u0026#39; keyword references backing field\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eSmall change, but it eliminates the need for manual backing fields when you need direct access.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"linq-finally-gets-joins\"\u003e\u003ca href=\"/posts/dotnet-10-released/#linq-finally-gets-joins\" title=\"LINQ Finally Gets Joins\"\u003eLINQ Finally Gets Joins\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eThis one should\u0026rsquo;ve happened years ago. LINQ now supports \u003ccode\u003eLeftJoin()\u003c/code\u003e and \u003ccode\u003eRightJoin()\u003c/code\u003e without extension method hacks:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003eresult\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003ecustomers\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eLeftJoin\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eorders\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003ec\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003ec\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eId\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eo\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003eo\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eCustomerId\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ec\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eo\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"k\"\u003enew\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"n\"\u003ec\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eo\u003c/span\u003e \u003cspan class=\"p\"\u003e})\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eWhere\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ex\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003ex\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eo\u003c/span\u003e \u003cspan class=\"p\"\u003e==\u003c/span\u003e \u003cspan class=\"kc\"\u003enull\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"c1\"\u003e// Customers without orders\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eSelect\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ex\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003ex\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ec\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIf you\u0026rsquo;ve written \u003ccode\u003eGroupJoin().SelectMany()\u003c/code\u003e gymnastics to fake left joins, you know why this matters.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"whats-still-missing\"\u003e\u003ca href=\"/posts/dotnet-10-released/#whats-still-missing\" title=\"What\u0026rsquo;s Still Missing\"\u003eWhat\u0026rsquo;s Still Missing\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eNo async LINQ. No discriminated unions. No pipeline operators.\u003c/p\u003e\n\u003cp\u003eSome will call that conservative. I call it \u003cstrong\u003ediscipline\u003c/strong\u003e. C# 14 doesn\u0026rsquo;t rewrite the language — it sharpens the tools we already use.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"migration-easier-than-net-9-but-not-trivial\"\u003e\u003ca href=\"/posts/dotnet-10-released/#migration-easier-than-net-9-but-not-trivial\" title=\"Migration: Easier Than .NET 9, But Not Trivial\"\u003eMigration: Easier Than .NET 9, But Not Trivial\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eIf you\u0026rsquo;re coming from .NET 8, the upgrade path is straightforward. If you\u0026rsquo;re on .NET 9, it\u0026rsquo;s almost invisible. But \u0026ldquo;almost\u0026rdquo; still requires validation.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"the-breaking-changes-youll-actually-hit\"\u003e\u003ca href=\"/posts/dotnet-10-released/#the-breaking-changes-youll-actually-hit\" title=\"The Breaking Changes You\u0026rsquo;ll Actually Hit\"\u003eThe Breaking Changes You\u0026rsquo;ll Actually Hit\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eMicrosoft lists 47 breaking changes. Most won\u0026rsquo;t affect you. These will:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e\u003cstrong\u003eASP.NET Core middleware order enforcement\u003c/strong\u003e — if you relied on loose ordering, expect build warnings (and potential runtime surprises).\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eEntity Framework Core query translation changes\u003c/strong\u003e — some LINQ queries that compiled in EF 8 now require client-side evaluation.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eJsonSerializer default behavior shifts\u003c/strong\u003e — particularly around null-handling and type discriminators in polymorphic scenarios.\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eNone of these are blockers. But they will surface during integration testing if you skip unit coverage.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"what-we-learned-from-net-8--net-9\"\u003e\u003ca href=\"/posts/dotnet-10-released/#what-we-learned-from-net-8--net-9\" title=\"What We Learned from .NET 8 → .NET 9\"\u003eWhat We Learned from .NET 8 → .NET 9\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eWhen we upgraded last year, we followed this pattern:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e\u003cstrong\u003eRun your existing test suite first\u003c/strong\u003e — fix flaky tests before migrating. You don\u0026rsquo;t want to debug framework issues and test issues simultaneously.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eUpgrade dependencies in isolation\u003c/strong\u003e — update NuGet packages one layer at a time (infrastructure, then domain, then API surface).\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eDeploy to a staging clone first\u003c/strong\u003e — not staging itself, but a true production clone with real load patterns.\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eThe third step caught two issues we missed locally: a JSON serialization edge case and a gRPC deadline timeout that behaved differently under sustained load.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"the-upgrade-checklist\"\u003e\u003ca href=\"/posts/dotnet-10-released/#the-upgrade-checklist\" title=\"The Upgrade Checklist\"\u003eThe Upgrade Checklist\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eBefore you start:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eConfirm all third-party libraries support .NET 10 (check NuGet compatibility)\u003c/li\u003e\n\u003cli\u003eUpdate your CI/CD pipeline SDK references\u003c/li\u003e\n\u003cli\u003eReview your \u003ccode\u003eglobal.json\u003c/code\u003e and lock the SDK version explicitly\u003c/li\u003e\n\u003cli\u003eValidate Docker base images if you\u0026rsquo;re containerized (\u003ccode\u003emcr.microsoft.com/dotnet/aspnet:10.0\u003c/code\u003e)\u003c/li\u003e\n\u003cli\u003eAudit custom Roslyn analyzers — some may not support C# 14 yet\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eRun your full test suite. Then run it again with diagnostics enabled. If you see warnings about obsolete APIs, address them now — they\u0026rsquo;ll become errors in .NET 11.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"when-to-migrate\"\u003e\u003ca href=\"/posts/dotnet-10-released/#when-to-migrate\" title=\"When to Migrate\"\u003eWhen to Migrate\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eIf you\u0026rsquo;re on .NET 6 (LTS support ended \u003cstrong\u003eNovember 2024\u003c/strong\u003e), you\u0026rsquo;re already late. Move to .NET 10 directly.\u003c/p\u003e\n\u003cp\u003eIf you\u0026rsquo;re on .NET 8 (LTS ending \u003cstrong\u003eNovember 2026\u003c/strong\u003e), you have time — but the sooner you migrate, the longer you benefit from performance improvements in production.\u003c/p\u003e\n\u003cp\u003eIf you\u0026rsquo;re on .NET 9 (STS ending \u003cstrong\u003eNovember 2026\u003c/strong\u003e), migrate during your next sprints. Feel lucky, you might just find a hidden gem in the upgrade. The effort is minimal, and you gain three years of support instead of eighteen months.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"what-comes-next-the-platform-we-deserved-all-along\"\u003e\u003ca href=\"/posts/dotnet-10-released/#what-comes-next-the-platform-we-deserved-all-along\" title=\"What Comes Next: The Platform We Deserved All Along\"\u003eWhat Comes Next: The Platform We Deserved All Along\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003e.NET 10 represents something rare in software: \u003cstrong\u003ea mature platform that stopped chasing trends and started honoring its commitments\u003c/strong\u003e.\u003c/p\u003e\n\u003cp\u003eThree years of LTS support means three years where your focus shifts from framework updates to product delivery. Where your CI pipelines stabilize instead of breaking every six months. Where runtime behavior becomes predictable enough that 3 AM production incidents become less frequent.\u003c/p\u003e\n\u003cp\u003eThis isn\u0026rsquo;t the end of .NET\u0026rsquo;s evolution. It\u0026rsquo;s the foundation for what comes after.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"the-bigger-picture\"\u003e\u003ca href=\"/posts/dotnet-10-released/#the-bigger-picture\" title=\"The Bigger Picture\"\u003eThe Bigger Picture\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eWith .NET 10 stable and locked in for the next three years, Microsoft can now take risks elsewhere — in Aspire, in Blazor United, in native AOT, in AI integrations — without destabilizing the core runtime. That separation between \u003cstrong\u003estable platform\u003c/strong\u003e and \u003cstrong\u003eexperimental tooling\u003c/strong\u003e is exactly what the ecosystem needs.\u003c/p\u003e\n\u003cp\u003eIf .NET 10 feels boring, it\u0026rsquo;s because boring is what production systems need. Excitement belongs in features, not in frameworks.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"the-opportunity\"\u003e\u003ca href=\"/posts/dotnet-10-released/#the-opportunity\" title=\"The Opportunity\"\u003eThe Opportunity\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eFor teams still on .NET Framework, this is your target for a rebuild and reconsider your strategy over the past few years. You have done something really really wrong, and have to pay the price of delayed modernization.\u003c/p\u003e\n\u003cp\u003eFor teams on .NET 6 or 8, this is your stabilization window. And for teams already on .NET 9, this is your chance to lock in the improvements without the upgrade treadmill.\u003c/p\u003e\n\u003cp\u003e.NET 10 won\u0026rsquo;t fix your architecture. It won\u0026rsquo;t eliminate your technical debt. It won\u0026rsquo;t make bad code good. But it will give you a runtime that \u003cstrong\u003eperforms predictably, builds consistently, and stays supported long enough to matter\u003c/strong\u003e. And in a world where frameworks change faster than products ship, that\u0026rsquo;s not just valuable.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eThat\u0026rsquo;s exactly what we needed,\u003c/strong\u003e and I\u0026rsquo;m really looking forward to building on top of it for years to come.\u003c/p\u003e\n","date_modified":"2026-05-26T10:22:03+02:00","date_published":"2025-11-13T18:00:00+01:00","id":"https://daily-devops.net/posts/dotnet-10-released/","language":"en","summary":".NET 10 ships JIT physical promotion, AVX 10.2 loop vectorization, and C# 14 with LTS support through November 2028. Boring is finally the feature.\n","tags":["architecture","dotnet","csharp","codequality","microsoft","performance"],"title":".NET 10: Boring by Design, Reliable by Default\n","url":"https://daily-devops.net/posts/dotnet-10-released/"},{"authors":[{"name":"Martin Stühmer","url":"https://daily-devops.net/authors/martin/"}],"content_html":"\u003cp\u003eYour string operations are killing your API. You just haven\u0026rsquo;t measured it yet.\u003c/p\u003e\n\u003cp\u003eWhile you\u0026rsquo;re busy optimizing database queries and adding cache layers, thousands of string searches per second are quietly eating your CPU budget. The problem isn\u0026rsquo;t visible in your APM dashboard because it\u0026rsquo;s distributed across every request. But it\u0026rsquo;s there. Compounding. Scaling linearly with load.\u003c/p\u003e\n\u003cp\u003eI discovered this the hard way when a log processing API started choking under production traffic. The bottleneck? String validation and sanitization. The fix? A .NET 8 feature that delivered a \u003cstrong\u003e5x performance improvement\u003c/strong\u003e and let us shut down servers instead of adding them. And it\u0026rsquo;s gotten even better in .NET 9 and 10.\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003eSearchValues\u0026lt;T\u0026gt;\u003c/code\u003e isn\u0026rsquo;t a nice-to-have optimization. It\u0026rsquo;s the difference between infrastructure costs that scale with your success versus infrastructure costs that scale with your inefficiency.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"the-real-problem-string-operations-dont-scale-like-you-think\"\u003e\u003ca href=\"/posts/searchvalues-saved-us-from-scaling-hell/#the-real-problem-string-operations-dont-scale-like-you-think\" title=\"The Real Problem: String Operations Don\u0026rsquo;t Scale Like You Think\"\u003eThe Real Problem: String Operations Don\u0026rsquo;t Scale Like You Think\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eHere\u0026rsquo;s what nobody tells you about string operations: they scale linearly with load. Double the traffic? Double the CPU usage. Add more features that parse strings? Multiply the pain.\u003c/p\u003e\n\u003cp\u003eTraditional approaches using \u003ccode\u003estring.IndexOfAny()\u003c/code\u003e or custom loops work fine when you\u0026rsquo;re processing dozens of requests per second. They fail silently when you\u0026rsquo;re processing thousands. No exceptions. No errors. Just slow, expensive CPU burn that compounds into massive infrastructure waste.\u003c/p\u003e\n\u003cp\u003eConsider a real scenario: a log aggregation service processing application logs in real-time. Each log entry needs sanitization, sensitive data detection, and validation before storage. That\u0026rsquo;s multiple string operations per log entry. Thousands of log entries per second. Millions of string operations per minute.\u003c/p\u003e\n\u003cp\u003eWith traditional methods, you\u0026rsquo;re leaving performance on the table. Modern CPUs have SIMD instructions that can process multiple characters simultaneously, but \u003ccode\u003eIndexOfAny()\u003c/code\u003e doesn\u0026rsquo;t use them consistently. It\u0026rsquo;s a generic solution for a problem that benefits from specialization.\u003c/p\u003e\n\u003cp\u003eThat\u0026rsquo;s where \u003ccode\u003eSearchValues\u0026lt;T\u0026gt;\u003c/code\u003e comes in. It\u0026rsquo;s a frozen, immutable set of values that .NET analyzes once at creation time, then optimizes specifically for your search pattern using the fastest algorithm available, whether that\u0026rsquo;s SIMD vectorization, bitmap lookups, or other strategies depending on your value set. The difference isn\u0026rsquo;t marginal. It\u0026rsquo;s transformational.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"what-makes-searchvaluest-different\"\u003e\u003ca href=\"/posts/searchvalues-saved-us-from-scaling-hell/#what-makes-searchvaluest-different\" title=\"What Makes SearchValues\u0026lt;T\u0026gt; Different?\"\u003eWhat Makes \u003ccode\u003eSearchValues\u0026lt;T\u0026gt;\u003c/code\u003e Different?\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003e\u003ccode\u003eSearchValues\u0026lt;T\u0026gt;\u003c/code\u003e was introduced in .NET 8. Most developers still haven\u0026rsquo;t heard of it. That\u0026rsquo;s a mistake that\u0026rsquo;s costing infrastructure budget.\u003c/p\u003e\n\u003cp\u003eWhen you create a \u003ccode\u003eSearchValues\u0026lt;T\u0026gt;\u003c/code\u003e instance, .NET analyzes your value set \u003cstrong\u003eonce\u003c/strong\u003e and selects the most efficient search algorithm. SIMD vectorization when possible. Bitmap lookups for dense character sets. Optimized branching for sparse sets. The runtime chooses. You don\u0026rsquo;t.\u003c/p\u003e\n\u003cp\u003eYou pay the analysis cost once at startup. Then you reuse that optimized instance across millions of operations. That\u0026rsquo;s the trade-off: slightly more expensive creation, dramatically cheaper execution.\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eTLDR; Pure performance win.\u003c/p\u003e\n\u003c/blockquote\u003e\n\n\n\n\n\u003ch2 id=\"net-9-and-net-10-getting-even-faster\"\u003e\u003ca href=\"/posts/searchvalues-saved-us-from-scaling-hell/#net-9-and-net-10-getting-even-faster\" title=\".NET 9 and .NET 10: Getting Even Faster\"\u003e.NET 9 and .NET 10: Getting Even Faster\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003e\u003ccode\u003eSearchValues\u0026lt;T\u0026gt;\u003c/code\u003e didn\u0026rsquo;t stop at .NET 8. Microsoft kept pushing performance further with each release, expanding both capabilities and raw speed.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"net-9-multi-substring-search\"\u003e\u003ca href=\"/posts/searchvalues-saved-us-from-scaling-hell/#net-9-multi-substring-search\" title=\".NET 9: Multi-Substring Search\"\u003e.NET 9: Multi-Substring Search\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003e.NET 8 gave us \u003ccode\u003eSearchValues\u0026lt;char\u0026gt;\u003c/code\u003e and \u003ccode\u003eSearchValues\u0026lt;byte\u0026gt;\u003c/code\u003e for character-level searches. .NET 9 expands this with \u003ccode\u003eSearchValues\u0026lt;string\u0026gt;\u003c/code\u003e for searching multiple substrings within a string.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eBefore (Regex):\u003c/strong\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// Looking for \u0026#34;error\u0026#34;, \u0026#34;warning\u0026#34;, or \u0026#34;critical\u0026#34; (case-insensitive)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003eregex\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003enew\u003c/span\u003e \u003cspan class=\"n\"\u003eRegex\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;(?i)error|warning|critical\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eRegexOptions\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eCompiled\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kt\"\u003ebool\u003c/span\u003e \u003cspan class=\"n\"\u003efound\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eregex\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eIsMatch\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003elogMessage\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cstrong\u003eAfter (SearchValues):\u003c/strong\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003eprivate\u003c/span\u003e \u003cspan class=\"kd\"\u003estatic\u003c/span\u003e \u003cspan class=\"k\"\u003ereadonly\u003c/span\u003e \u003cspan class=\"n\"\u003eSearchValues\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"kt\"\u003estring\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003eLogKeywords\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eSearchValues\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eCreate\u003c/span\u003e\u003cspan class=\"p\"\u003e([\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;error\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;warning\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;critical\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e],\u003c/span\u003e \u003cspan class=\"n\"\u003eStringComparison\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eOrdinalIgnoreCase\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kt\"\u003ebool\u003c/span\u003e \u003cspan class=\"n\"\u003efound\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003elogMessage\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAsSpan\u003c/span\u003e\u003cspan class=\"p\"\u003e().\u003c/span\u003e\u003cspan class=\"n\"\u003eContainsAny\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eLogKeywords\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe Regex compiler in .NET 9 \u003cstrong\u003euses this automatically\u003c/strong\u003e when it detects multi-substring patterns. Call it directly to skip regex parsing overhead entirely.\u003c/p\u003e\n\u003cp\u003eMulti-substring searches in log analysis became 3-4x faster in .NET 9. Patterns that were \u0026ldquo;too expensive\u0026rdquo; for every log entry suddenly became viable for real-time alerting.\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eTLDR; .NET 9 is even faster for multi-substring searches.\u003c/p\u003e\n\u003c/blockquote\u003e\n\n\n\n\n\u003ch3 id=\"net-10-hardware-level-optimizations\"\u003e\u003ca href=\"/posts/searchvalues-saved-us-from-scaling-hell/#net-10-hardware-level-optimizations\" title=\".NET 10: Hardware-Level Optimizations\"\u003e.NET 10: Hardware-Level Optimizations\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003e.NET 10 takes the optimization further by targeting CPU instruction sets directly. On AVX-512, it replaces two instructions with a single \u003ccode\u003ePermuteVar64x8x2\u003c/code\u003e, cutting CPU cycles in half. ARM64 gets cheaper \u003ccode\u003eUnzipEven\u003c/code\u003e instructions, delivering better performance on AWS Graviton and Azure Ampere instances. Case-insensitive searches benefit from extended fast-path logic that reduces validation overhead.\u003c/p\u003e\n\u003cp\u003eIf you\u0026rsquo;re running .NET 9 and seeing good results, .NET 10 makes the same operations \u003cstrong\u003e10-20% faster\u003c/strong\u003e without code changes. Just upgrade the runtime.\u003c/p\u003e\n\u003cp\u003eFor heavy text processing workloads, that 10-20% compounds across millions of operations. It\u0026rsquo;s the difference between needing an extra worker node versus staying within capacity.\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eTLDR; Once again, .NET 10 is faster.\u003c/p\u003e\n\u003c/blockquote\u003e\n\n\n\n\n\u003ch2 id=\"production-numbers\"\u003e\u003ca href=\"/posts/searchvalues-saved-us-from-scaling-hell/#production-numbers\" title=\"Production Numbers\"\u003eProduction Numbers\u003c/a\u003e\u003c/h2\u003e\n\u003cblockquote\u003e\n\u003cp\u003e\u003cstrong\u003eBenchmarks lie. Production doesn\u0026rsquo;t.\u003c/strong\u003e\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003eIn a real log processing pipeline handling production traffic, 10,000 log entries that previously took ~450ms now complete in ~85ms. That\u0026rsquo;s \u003cstrong\u003e5.3x faster on the same hardware\u003c/strong\u003e. Not \u003cem\u003e\u003cstrong\u003e20% faster\u003c/strong\u003e\u003c/em\u003e optimization theater. That\u0026rsquo;s handling five times more throughput without adding a single server. Infrastructure costs going down while traffic going up.\u003c/p\u003e\n\u003cp\u003eThis isn\u0026rsquo;t theoretical performance gains on a conference slide deck. This is the difference between scaling horizontally by adding servers versus scaling efficiently by using what you have. Between infrastructure costs that increase with growth versus costs that stay flat. Between firefighting capacity problems versus preventing them.\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eThe business impact is direct: \u003cstrong\u003efewer servers, lower costs, same (or better) performance\u003c/strong\u003e.\u003c/p\u003e\n\u003c/blockquote\u003e\n\n\n\n\n\u003ch2 id=\"log-sanitization-without-the-performance-tax\"\u003e\u003ca href=\"/posts/searchvalues-saved-us-from-scaling-hell/#log-sanitization-without-the-performance-tax\" title=\"Log Sanitization Without the Performance Tax\"\u003eLog Sanitization Without the Performance Tax\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eLogging should be cheap and invisible. Write to stdout, let your log shipper handle it, done. Reality is messier.\u003c/p\u003e\n\u003cp\u003eLog sanitization is expensive. Stripping sensitive data and control characters before storage becomes a bottleneck at scale. And every production system does it because compliance demands it. Regulations like GDPR, PCI-DSS, and HIPAA all require sanitizing logs.\u003c/p\u003e\n\u003cbr /\u003e\n\u003cp\u003eHere\u0026rsquo;s a simplified example of log sanitization that removes dangerous characters and redacts sensitive data patterns:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eusing\u003c/span\u003e \u003cspan class=\"nn\"\u003eSystem\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eusing\u003c/span\u003e \u003cspan class=\"nn\"\u003eSystem.Buffers\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eusing\u003c/span\u003e \u003cspan class=\"nn\"\u003eSystem.Text\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eLogSanitizer\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e// Characters that could indicate injection attempts or corrupt data\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003eprivate\u003c/span\u003e \u003cspan class=\"kd\"\u003estatic\u003c/span\u003e \u003cspan class=\"k\"\u003ereadonly\u003c/span\u003e \u003cspan class=\"n\"\u003eSearchValues\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"kt\"\u003echar\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003eDangerousCharacters\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003eSearchValues\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eCreate\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;\\0\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\x08\\x0B\\x0C\\x0E\\x0F\u0026#34;\u003c/span\u003e \u003cspan class=\"p\"\u003e+\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                           \u003cspan class=\"s\"\u003e\u0026#34;\\x10\\x11\\x12\\x13\\x14\\x15\\x16\\x17\\x18\\x19\\x1A\\x1B\\x1C\\x1D\\x1E\\x1F\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e// Patterns that often precede sensitive data in logs\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003eprivate\u003c/span\u003e \u003cspan class=\"kd\"\u003estatic\u003c/span\u003e \u003cspan class=\"k\"\u003ereadonly\u003c/span\u003e \u003cspan class=\"n\"\u003eSearchValues\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"kt\"\u003echar\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003eSensitiveMarkers\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003eSearchValues\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eCreate\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;=:@\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kd\"\u003estatic\u003c/span\u003e \u003cspan class=\"kt\"\u003estring\u003c/span\u003e \u003cspan class=\"n\"\u003eSanitizeLogEntry\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003estring\u003c/span\u003e \u003cspan class=\"n\"\u003elogEntry\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003eReadOnlySpan\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"kt\"\u003echar\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003espan\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003elogEntry\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAsSpan\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"c1\"\u003e// Fast path: if no dangerous characters, return original\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(!\u003c/span\u003e\u003cspan class=\"n\"\u003espan\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eContainsAny\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eDangerousCharacters\u003c/span\u003e\u003cspan class=\"p\"\u003e))\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003eProcessSensitiveData\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003elogEntry\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"c1\"\u003e// Slow path: rebuild without dangerous characters\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003ebuilder\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003enew\u003c/span\u003e \u003cspan class=\"n\"\u003eStringBuilder\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003elogEntry\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eLength\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003eforeach\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003echar\u003c/span\u003e \u003cspan class=\"n\"\u003ec\u003c/span\u003e \u003cspan class=\"k\"\u003ein\u003c/span\u003e \u003cspan class=\"n\"\u003espan\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(!\u003c/span\u003e\u003cspan class=\"n\"\u003eDangerousCharacters\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eContains\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ec\u003c/span\u003e\u003cspan class=\"p\"\u003e))\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"n\"\u003ebuilder\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAppend\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ec\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003eProcessSensitiveData\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ebuilder\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eToString\u003c/span\u003e\u003cspan class=\"p\"\u003e());\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003eprivate\u003c/span\u003e \u003cspan class=\"kd\"\u003estatic\u003c/span\u003e \u003cspan class=\"kt\"\u003estring\u003c/span\u003e \u003cspan class=\"n\"\u003eProcessSensitiveData\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003estring\u003c/span\u003e \u003cspan class=\"n\"\u003elog\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"c1\"\u003e// Find patterns like \u0026#34;password=\u0026#34;, \u0026#34;token:\u0026#34;, \u0026#34;apiKey@\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003eReadOnlySpan\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"kt\"\u003echar\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003espan\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003elog\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAsSpan\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"n\"\u003emarkerIndex\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003espan\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eIndexOfAny\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eSensitiveMarkers\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003emarkerIndex\u003c/span\u003e \u003cspan class=\"p\"\u003e==\u003c/span\u003e \u003cspan class=\"p\"\u003e-\u003c/span\u003e\u003cspan class=\"m\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003elog\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"c1\"\u003e// Redact the next 20 characters after sensitive markers\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003eresult\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003enew\u003c/span\u003e \u003cspan class=\"n\"\u003eStringBuilder\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003elog\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eLength\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003eresult\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAppend\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003espan\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eSlice\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"m\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003emarkerIndex\u003c/span\u003e \u003cspan class=\"p\"\u003e+\u003c/span\u003e \u003cspan class=\"m\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e));\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003eresult\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAppend\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;[REDACTED]\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003eresult\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eToString\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cstrong\u003eThe Impact:\u003c/strong\u003e 87% reduction in sanitization time. Not in a benchmark. In production. Under real load.\u003c/p\u003e\n\u003cp\u003eThe fast path (clean logs) allocates \u003cstrong\u003ezero heap memory\u003c/strong\u003e. Garbage collector stays asleep. Sub-millisecond performance for 10KB log entries is the norm.\u003c/p\u003e\n\u003cp\u003eAcross millions of logs daily, that\u0026rsquo;s measurable infrastructure savings. We \u003cstrong\u003eshut down log processing workers\u003c/strong\u003e instead of adding them. Real money saved.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"what-you-need-to-know\"\u003e\u003ca href=\"/posts/searchvalues-saved-us-from-scaling-hell/#what-you-need-to-know\" title=\"What You Need to Know\"\u003eWhat You Need to Know\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eMicrosoft\u0026rsquo;s \u003ca href=\"https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1870\" target=\"_blank\" rel=\"noopener external noreferrer\"\u003eCA1870 analyzer\u003c/a\u003e throws warnings when it detects string searching that could use \u003ca href=\"https://learn.microsoft.com/en-us/dotnet/api/system.buffers.searchvalues-1\" target=\"_blank\" rel=\"noopener external noreferrer\"\u003e\u003ccode\u003eSearchValues\u0026lt;T\u0026gt;\u003c/code\u003e\u003c/a\u003e. The warning appears on code like \u003ccode\u003etext.IndexOfAny(new[] { ',', ';', '|' })\u003c/code\u003e and suggests converting to \u003ccode\u003eSearchValues\u0026lt;T\u0026gt;\u003c/code\u003e. The fix is straightforward: create a static readonly \u003ccode\u003eSearchValues\u0026lt;char\u0026gt; Delimiters = SearchValues.Create([',', ';', '|'])\u003c/code\u003e and use \u003ccode\u003etext.AsSpan().IndexOfAny(Delimiters)\u003c/code\u003e. The analyzer is helpful once you know when to act on it.\u003c/p\u003e\n\u003cp\u003eThe most critical mistake is creating instances in loops or hot paths. \u003ccode\u003eSearchValues\u0026lt;T\u0026gt;\u003c/code\u003e is powerful but easy to misuse. The analysis work happens at creation time, which is expensive. The lookup cost is cheap. This means you need to store instances as static readonly fields. Pay the creation cost once at startup, then benefit from fast lookups across millions of operations. Creating \u003ccode\u003eSearchValues\u0026lt;T\u0026gt;\u003c/code\u003e repeatedly destroys the performance advantage you\u0026rsquo;re trying to gain.\u003c/p\u003e\n\u003cp\u003ePremature optimization is still evil, but there\u0026rsquo;s a clear pattern where \u003ccode\u003eSearchValues\u0026lt;T\u0026gt;\u003c/code\u003e delivers real value. The sweet spot is searching for \u003cstrong\u003e3+ different characters\u003c/strong\u003e or values in code that executes \u003cstrong\u003ehundreds to millions of times\u003c/strong\u003e, specifically in hot paths, per-request logic, or tight loops. Variable-length input where you can\u0026rsquo;t predict string size amplifies the benefits. Modern CPUs with SIMD support (most hardware from the last 5-7 years) show the biggest gains, often 5-10x faster. For searching 1-2 characters, just use \u003ccode\u003eIndexOf\u003c/code\u003e directly. It\u0026rsquo;s simpler and performs similarly. Skip \u003ccode\u003eSearchValues\u0026lt;T\u0026gt;\u003c/code\u003e for code that runs once at startup, error handling paths that rarely execute, tiny fixed-length strings, or when you\u0026rsquo;re already bottlenecked on I/O operations. The overhead isn\u0026rsquo;t worth it for infrequent operations.\u003c/p\u003e\n\u003cp\u003eThe real test is production, not benchmarks. I\u0026rsquo;ve seen 2x to 10x improvements depending on workload, but your results will vary based on several factors. Character set size matters, where larger sets (10+ characters) benefit more from vectorization than small sets (2-3 characters). String length amplifies the speedup since more characters are processed. Search frequency compounds the benefits over time. CPU architecture plays a role too, with modern SIMD-capable processors showing 5-10x gains while older CPUs show 2-3x improvements. Don\u0026rsquo;t trust microbenchmarks that optimize for cache locality and predictable branching that production never has. Run load tests with production-like data. Measure wall-clock time and monitor allocations, not just throughput.\u003c/p\u003e\n\u003cp\u003ePerformance improvements in production aren\u0026rsquo;t synthetic benchmark gains. They\u0026rsquo;re real, measurable, budget-impacting improvements. Zero-allocation fast paths reduce GC pressure, which compounds in long-running services. Security validators that ran 3-5x faster meant user-facing latency reduction that customers noticed. ETL pipelines that completed 4x faster let us \u003cstrong\u003edecommission servers\u003c/strong\u003e instead of adding them. Not \u0026ldquo;up to\u0026rdquo; savings in a marketing slide. Actual, budgeted, we-turned-off-these-instances savings.\u003c/p\u003e\n\u003cp\u003eNot every use case benefits equally. Profile first, optimize second. Small character sets (2-3 characters) show minimal gains. Large character sets (10+ characters) deliver significant wins. Find your workload\u0026rsquo;s threshold. The creation cost is real, so single searches don\u0026rsquo;t benefit. This optimization is for repeated operations in high-frequency code paths.\u003c/p\u003e\n\u003cp\u003eIn high-throughput APIs and data pipelines, \u003ccode\u003eSearchValues\u0026lt;T\u0026gt;\u003c/code\u003e delivered one of the highest ROI optimizations relative to effort. Minimal code changes. No architectural changes. No dependency updates or compatibility breaks. Measurable immediate gains. But ROI requires return. If you\u0026rsquo;re not processing thousands of operations per second, this won\u0026rsquo;t move the needle. Measure your use case and save energy for problems that actually matter.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"the-bottom-line\"\u003e\u003ca href=\"/posts/searchvalues-saved-us-from-scaling-hell/#the-bottom-line\" title=\"The Bottom Line\"\u003eThe Bottom Line\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003e\u003ccode\u003eSearchValues\u0026lt;T\u0026gt;\u003c/code\u003e delivers measurable business value when applied correctly, but becomes noise when applied incorrectly. The difference lies in recognizing the pattern: high-frequency operations searching for multiple characters in variable-length input. This pattern shows up in log processing, input validation, CSV parsing, security filtering, and file path sanitization, essentially anywhere you\u0026rsquo;re repeatedly searching for multiple values in strings or spans where the size varies.\u003c/p\u003e\n\u003cp\u003eThe methodology is straightforward, though discipline matters. Start by measuring and profiling your hot paths to identify actual bottlenecks instead of guessing where problems might be. Apply the optimization selectively, focusing on repeated operations with 3+ characters in performance-critical code paths. Then validate the impact through benchmarking with realistic data under realistic load, not synthetic microbenchmarks that optimize for conditions production never sees. Treat CA1870 warnings as a starting point for investigation, not a mandate for action. Your profiler understands your workload better than any static analyzer ever could.\u003c/p\u003e\n\u003cp\u003eThe log sanitization example from earlier demonstrates the core pattern in action: high frequency, variable input, multiple search targets. This same combination appears across input validation, data parsing, security filtering, and content sanitization throughout production systems. When you find this pattern in your codebase, apply \u003ccode\u003eSearchValues\u0026lt;T\u0026gt;\u003c/code\u003e and measure what changes. When it works, it transforms performance in ways that show up directly in infrastructure costs. When it doesn\u0026rsquo;t deliver results, you\u0026rsquo;ve invested an hour learning something valuable about your workload\u0026rsquo;s actual characteristics.\u003c/p\u003e\n\u003cp\u003eThe impact is asymmetric by design. Your infrastructure budget notices the difference immediately through fewer servers, lower costs, and better resource utilization. Your customers notice nothing, which is exactly the point of effective performance optimization. Good performance work is invisible to users while being highly visible in cost structure and operational metrics. One less bottleneck when scaling. One fewer server to provision. One more problem solved before it escalates into a crisis that demands emergency intervention.\u003c/p\u003e\n","date_modified":"2026-05-26T10:22:03+02:00","date_published":"2025-10-20T17:30:00+01:00","id":"https://daily-devops.net/posts/searchvalues-saved-us-from-scaling-hell/","language":"en","summary":"How SearchValues in .NET 8-10 delivered 5x faster string operations, reduced infrastructure costs, and evolved with multi-substring optimization.","tags":["performance","bestpractices","codequality","csharp","dotnet","hidden-gems"],"title":"How SearchValues Saved Us From Scaling Hell","url":"https://daily-devops.net/posts/searchvalues-saved-us-from-scaling-hell/"},{"authors":[{"name":"Martin Stühmer","url":"https://daily-devops.net/authors/martin/"}],"content_html":"\u003cblockquote\u003e\n\u003cp\u003e\u003cem\u003eEveryone preaches Clean Code. Few deliver it. Even fewer can explain the purpose behind it.\u003c/em\u003e\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003eClean Code has become a buzzword in software development. It promises clarity, maintainability, and professionalism. Yet, in many projects, especially within the .NET ecosystem, the pursuit of Clean Code has devolved into a superficial exercise — a checklist of patterns and practices that often obscures rather than reveals intent.\u003c/p\u003e\n\u003cp\u003eWhat began as a philosophy of craftsmanship has become a slogan. Across the software industry, entire companies promote themselves as \u0026ldquo;Clean Code\u0026rdquo; experts. They quote principles, host workshops, and promise maintainable systems built on solid engineering ethics. But when you take over one of their projects, the illusion often breaks quickly.\u003c/p\u003e\n\u003cp\u003eBehind the neat folder structures and the spotless naming conventions, you find the opposite of maintainability: deep abstraction hierarchies, duplicated logic, and decisions made to look professional rather than to last. The surface is clean, but the foundation is fragile. Clean Code, in these environments, has turned from a discipline into a decoration.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"when-clean-turns-into-clutter\"\u003e\u003ca href=\"/posts/clean-code-lip-service-not-a-standard/#when-clean-turns-into-clutter\" title=\"When Clean Turns Into Clutter\"\u003eWhen Clean Turns Into Clutter\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eThe intent behind Clean Code is noble. Readability, simplicity, and maintainability have always been pillars of good software. Yet, in many .NET projects, the application of these ideas drifts into over-engineering.\u003c/p\u003e\n\u003cp\u003eDevelopers eager to demonstrate \u0026ldquo;good design\u0026rdquo; create layers of repositories, services, and managers that add distance rather than clarity. Patterns are applied mechanically instead of meaningfully. C# makes such designs easy to express, but without discipline, they create noise instead of structure.\u003c/p\u003e\n\u003cp\u003eIn effective systems, every layer exists for a reason. It isolates complexity or stabilizes a contract. In misguided ones, layers multiply because someone once said \u0026ldquo;that\u0026rsquo;s how clean code should look.\u0026rdquo; The result is the opposite of clarity: a maze of abstractions where simplicity should have lived.\u003c/p\u003e\n\u003cp\u003eClean Code was never about purity. It was about communication, code that speaks its purpose clearly and succinctly.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"how-to-recognize-a-clean-code-disaster\"\u003e\u003ca href=\"/posts/clean-code-lip-service-not-a-standard/#how-to-recognize-a-clean-code-disaster\" title=\"How to Recognize a Clean Code Disaster\"\u003eHow to Recognize a Clean Code Disaster\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eYou don\u0026rsquo;t have to read the code to know when a Clean Code project has failed. Product Owners, Scrum Masters, and technical managers can identify the warning signs long before the architecture diagram gives it away.\u003c/p\u003e\n\u003cp\u003eWhen development velocity drops without visible cause, you are likely seeing the impact of unnecessary complexity. Teams spend more time understanding the structure than implementing logic. Planning sessions get longer, and \u0026ldquo;small\u0026rdquo; changes suddenly take entire sprints.\u003c/p\u003e\n\u003cp\u003eWhen developers start discussing patterns, interfaces, or naming more than business outcomes, philosophy has overtaken purpose. That shift from solving problems to defending design purity is the hallmark of a Clean Code disaster.\u003c/p\u003e\n\u003cp\u003eIf onboarding new team members feels like teaching theology instead of engineering, you are no longer running a project — you are managing a doctrine.\u003c/p\u003e\n\u003cp\u003eThese projects are easy to recognize: they look perfect in review slides, but nobody can confidently add a new feature. Clean Code has become an excuse for paralysis.\u003c/p\u003e\n\u003cp\u003eWondering how to handle all kinds of technical debt? You might find inspiration in my articles \u003cstrong\u003e\u003ca href=\"https://daily-devops.net/posts/illuminate-technical-debt/\"\u003eIlluminate Technical Debt\u003c/a\u003e\u003c/strong\u003e or \u003cstrong\u003e\u003ca href=\"https://daily-devops.net/posts/tale-of-forgotten-pennies-and-lost-dollars/\"\u003eA Tale of Forgotten Pennies and Lost Dollars\u003c/a\u003e\u003c/strong\u003e.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"the-subjectivity-trap\"\u003e\u003ca href=\"/posts/clean-code-lip-service-not-a-standard/#the-subjectivity-trap\" title=\"The Subjectivity Trap\"\u003eThe Subjectivity Trap\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eClean Code\u0026rsquo;s biggest flaw is its subjectivity. What one developer considers elegant, another sees as excessive. Without shared standards, teams drift toward inconsistency. Over time, that inconsistency turns into entropy.\u003c/p\u003e\n\u003cp\u003eThis is where the .NET ecosystem provides real strength — if teams use it.\u003c/p\u003e\n\u003cp\u003eMicrosoft\u0026rsquo;s official \u003cstrong\u003e\u003ca href=\"https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/coding-style/coding-conventions\" target=\"_blank\" rel=\"noopener external noreferrer\"\u003eC# Coding Conventions\u003c/a\u003e\u003c/strong\u003e offer consistent guidance that prevents personal interpretation from dominating code style. They cover essential ground: meaningful naming, predictable indentation, placement of braces, and clear method intent. They sound simple, but simplicity is precisely the point — clarity begins with habit.\u003c/p\u003e\n\u003cp\u003eBeyond syntax, the \u003cstrong\u003e\u003ca href=\"https://learn.microsoft.com/en-us/dotnet/standard/design-guidelines/\" target=\"_blank\" rel=\"noopener external noreferrer\"\u003eFramework Design Guidelines\u003c/a\u003e\u003c/strong\u003e by Krzysztof Cwalina and Brad Abrams extend these ideas into design maturity. They encourage minimal public exposure, predictable method naming, immutable data where feasible, and the separation of domain and infrastructure concerns. These aren\u0026rsquo;t arbitrary conventions; they\u0026rsquo;re principles proven through the evolution of .NET itself.\u003c/p\u003e\n\u003cp\u003eComplementary to that, tools such as \u003cstrong\u003e.editorconfig\u003c/strong\u003e and \u003cstrong\u003eRoslyn Analyzers\u003c/strong\u003e allow you to codify these rules directly into your build pipeline. They turn subjective ideals into enforceable practice — removing \u0026ldquo;it looks cleaner\u0026rdquo; from every review conversation.\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e\u003cem\u003eReal Clean Code doesn\u0026rsquo;t rely on taste. It relies on consistency.\u003c/em\u003e\u003c/p\u003e\n\u003c/blockquote\u003e\n\n\n\n\n\u003ch2 id=\"the-clean-code-business-model\"\u003e\u003ca href=\"/posts/clean-code-lip-service-not-a-standard/#the-clean-code-business-model\" title=\"The Clean Code Business Model\"\u003eThe Clean Code Business Model\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eMany consulting firms have learned to commercialize the language of Clean Code. They brand it as proof of engineering excellence and build delivery models around it. Unfortunately, much of this is theater.\u003c/p\u003e\n\u003cp\u003eThese firms often deliver code that passes inspection — it compiles neatly, adheres to style rules, and satisfies every static analysis tool — yet still lacks coherence. When you extend it, you discover how rigid it really is. Each minor change requires revisiting abstractions that were meant to protect flexibility. The system becomes elegant but immobile.\u003c/p\u003e\n\u003cp\u003eThis happens because the focus shifts from \u003cem\u003eevolution\u003c/em\u003e to \u003cem\u003epresentation\u003c/em\u003e. The goal is to appear clean, not to stay changeable. The product is technically compliant but practically suffocating. Clean Code, stripped of pragmatism, turns into an architectural straitjacket.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"when-clean-code-becomes-a-liability\"\u003e\u003ca href=\"/posts/clean-code-lip-service-not-a-standard/#when-clean-code-becomes-a-liability\" title=\"When Clean Code Becomes a Liability\"\u003eWhen Clean Code Becomes a Liability\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eSoftware engineering always operates under constraint. Budgets, deadlines, and shifting priorities dictate reality. Clean Code, when treated as a moral requirement instead of a practical discipline, often ignores those constraints.\u003c/p\u003e\n\u003cp\u003eEvery abstraction, every refactor, every additional layer has a cost. When those costs go unacknowledged, the project accumulates \u003cstrong\u003estructural debt\u003c/strong\u003e, code that is technically ideal but functionally rigid. It cannot evolve without risk.\u003c/p\u003e\n\u003cp\u003eThe irony is sharp: the same projects that advertise \u0026ldquo;Clean Code\u0026rdquo; often become the hardest to maintain. They have confused clarity with complexity, principles with efficiency.\u003c/p\u003e\n\u003cp\u003eWhen code is written for the slide deck instead of the sprint, it becomes a \u003cem\u003eliability\u003c/em\u003e, not an asset.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"practical-integrity-and-sustainable-clarity\"\u003e\u003ca href=\"/posts/clean-code-lip-service-not-a-standard/#practical-integrity-and-sustainable-clarity\" title=\"Practical Integrity and Sustainable Clarity\"\u003ePractical Integrity and Sustainable Clarity\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eReal Clean Code is grounded in restraint. It means writing C# that is understandable, testable, and predictable, without turning simplicity into ceremony.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"apply-patterns-with-purpose\"\u003e\u003ca href=\"/posts/clean-code-lip-service-not-a-standard/#apply-patterns-with-purpose\" title=\"Apply Patterns with Purpose\"\u003eApply Patterns with Purpose\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eDependency injection, for example, should be used to support modularity and testing, not to decorate trivial classes. Asynchronous code should express intent clearly — methods named \u003cstrong\u003e\u003ccode\u003eGetAsync\u003c/code\u003e\u003c/strong\u003e should do exactly what they promise — and mixing synchronous and asynchronous patterns should be avoided. State should be explicit, and side effects should be visible.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"follow-framework-conventions\"\u003e\u003ca href=\"/posts/clean-code-lip-service-not-a-standard/#follow-framework-conventions\" title=\"Follow Framework Conventions\"\u003eFollow Framework Conventions\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eGood C# code follows the spirit of the platform. It leverages the framework\u0026rsquo;s conventions rather than fighting them. The \u003cstrong\u003e\u003ca href=\"https://learn.microsoft.com/en-us/dotnet/standard/design-guidelines/\" target=\"_blank\" rel=\"noopener external noreferrer\"\u003eDesign guidelines for developing class libraries\u003c/a\u003e\u003c/strong\u003e explicitly recommend favoring readability, minimizing surprise, and maintaining a predictable object model. Following them doesn\u0026rsquo;t just improve code; it builds trust between developers who may never meet but must share the same repository.\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eReadable code is not the goal; it is the byproduct of deliberate design choices that make collaboration sustainable.\u003c/p\u003e\n\u003c/blockquote\u003e\n\n\n\n\n\u003ch2 id=\"conclusion-clean-code-as-practice-not-theater\"\u003e\u003ca href=\"/posts/clean-code-lip-service-not-a-standard/#conclusion-clean-code-as-practice-not-theater\" title=\"Conclusion: Clean Code as Practice, Not Theater\"\u003eConclusion: Clean Code as Practice, Not Theater\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eClean Code is not the destination. It is the baseline — a way of showing that you care about what comes next.\u003c/p\u003e\n\u003cp\u003eTrue engineering excellence begins where Clean Code ends: in architecture that aligns with context, in systems that evolve gracefully, and in decisions that respect both business goals and human comprehension.\u003c/p\u003e\n\u003cp\u003eCompanies that sell Clean Code as a brand often leave behind systems that cannot grow. They confuse purity with professionalism and structure with sustainability.\u003c/p\u003e\n\u003cp\u003eGood software is written for people as much as for machines.\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eMy motto: \u003cstrong\u003eStick to the framework.\u003c/strong\u003e\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003eIn .NET, that means trusting the conventions, libraries, and design wisdom refined over decades rather than chasing ideological perfection.\u003c/p\u003e\n\u003cp\u003eClean Code, when practiced honestly, is not theater. It is a quiet act of respect, respect for the craft, for the product, and for the next developer who must live with your decisions.\u003c/p\u003e\n","date_modified":"2026-02-13T11:27:21+01:00","date_published":"2025-10-16T13:00:00+02:00","id":"https://daily-devops.net/posts/clean-code-lip-service-not-a-standard/","language":"en","summary":"How misunderstood Clean Code ideals harm .NET systems. Learn to recognize code quality failures and apply C# best practices for maintainable software.","tags":["csharp","dotnet","technicaldebt","softwareengineering","bestpractices","codequality"],"title":"Clean Code: A Lip Service, Not a Standard\n","url":"https://daily-devops.net/posts/clean-code-lip-service-not-a-standard/"},{"authors":[{"name":"Martin Stühmer","url":"https://daily-devops.net/authors/martin/"}],"content_html":"\u003cp\u003eIn recent weeks, I had the opportunity to support a project explicitly built around Domain Driven Design (DDD) and Domain Driven Development principles. On the surface, this project appeared highly sophisticated, leveraging trendy abstractions and contemporary buzzwords. Yet, as I dove deeper, it quickly became clear that essential development fundamentals were being neglected.\u003c/p\u003e\n\u003cp\u003eDespite its polished exterior, the project had a weak approach to managing technical debt, resulting in significant productivity losses and unnecessary team friction. Built-in analyzers—specifically crafted for .NET—were often disregarded or explicitly disabled. Instead, the team leaned on external tools plagued with false positives, adding complexity rather than clarity.\u003c/p\u003e\n\u003cp\u003eThis scenario prompts a critical question: Why do we, as software professionals, insist on complicating things unnecessarily? Why ignore integrated, purpose-built tools in favor of unreliable external ones? It’s time we refocus on the basics beneath the buzzwords, ensuring sustainable, high-quality development practices.\u003c/p\u003e\n\u003cp\u003eWhen I raised these concerns constructively, the response was discouraging silence and apparent indifference. Sadly, this scenario isn’t rare. Too often, commitment to quality gets overridden by louder voices pushing us to \u0026ldquo;just get things done.\u0026rdquo;\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"maintaining-quality--tools-and-techniques\"\u003e\u003ca href=\"/posts/buzzword-driven-development/#maintaining-quality--tools-and-techniques\" title=\"Maintaining Quality – Tools and Techniques\"\u003eMaintaining Quality – Tools and Techniques\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eSoftware quality is foundational, not optional. Keeping standards high and technical debt low begins with the right tools—especially integrated analyzers in .NET projects.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"why-integrated-analyzers-matter\"\u003e\u003ca href=\"/posts/buzzword-driven-development/#why-integrated-analyzers-matter\" title=\"Why Integrated Analyzers Matter\"\u003eWhy Integrated Analyzers Matter\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eIntegrated analyzers provide immediate, actionable feedback directly in your IDE, reducing disruptions and enhancing productivity. They catch bugs early, enforce coding standards, and ensure consistency. Unlike external analyzers, built-in tools are specifically optimized for .NET, minimizing inaccuracies and false positives.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"essential-net-analyzers\"\u003e\u003ca href=\"/posts/buzzword-driven-development/#essential-net-analyzers\" title=\"Essential .NET Analyzers\"\u003eEssential .NET Analyzers\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eHere are four key analyzers that every .NET project should use:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003eMicrosoft.CodeAnalysis.NetAnalyzers\u003c/strong\u003e (included by default)\n\u003cul\u003e\n\u003cli\u003eCatches common bugs like memory leaks\u003c/li\u003e\n\u003cli\u003eEnforces naming conventions\u003c/li\u003e\n\u003cli\u003eIdentifies security issues\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eMicrosoft.VisualStudio.Threading.Analyzers\u003c/strong\u003e\n\u003cul\u003e\n\u003cli\u003ePrevents async/await deadlocks\u003c/li\u003e\n\u003cli\u003eEnsures proper threading patterns\u003c/li\u003e\n\u003cli\u003eEssential for any project using async code\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eRoslynator.Analyzers\u003c/strong\u003e\n\u003cul\u003e\n\u003cli\u003eImproves code readability\u003c/li\u003e\n\u003cli\u003eSuggests better coding patterns\u003c/li\u003e\n\u003cli\u003eHelps maintain consistent style\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eMeziantou.Analyzer\u003c/strong\u003e\n\u003cul\u003e\n\u003cli\u003eFinds performance issues in LINQ queries\u003c/li\u003e\n\u003cli\u003eIdentifies outdated API usage\u003c/li\u003e\n\u003cli\u003eCatches resource management problems\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003cblockquote\u003e\n\u003cp\u003e\u003cstrong\u003eRemember:\u003c/strong\u003e Every warning has a purpose. Don\u0026rsquo;t ignore them—configure them thoughtfully.\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003eWhile some warnings may initially seem trivial or frustrating, each one signals a genuine, underlying concern. Thankfully, project settings provide flexibility to balance rigor and practicality, ensuring valuable warnings don’t get buried beneath noise.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"project-settings-that-matter\"\u003e\u003ca href=\"/posts/buzzword-driven-development/#project-settings-that-matter\" title=\"Project Settings That Matter\"\u003eProject Settings That Matter\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eAnalyzers alone aren\u0026rsquo;t enough. Your project settings must enforce quality standards:\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eKey Settings:\u003c/strong\u003e\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ccode\u003eTreatWarningsAsErrors = true\u003c/code\u003e → Fixes warnings immediately\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eWarningLevel = 4\u003c/code\u003e → Maximum compiler checks\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eAnalysisLevel = latest\u003c/code\u003e → Uses newest quality rules\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e\u003cstrong\u003eStrategic Configuration:\u003c/strong\u003e\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eUse \u003ccode\u003eNoWarn\u003c/code\u003e to suppress specific, non-critical warnings\u003c/li\u003e\n\u003cli\u003eUse \u003ccode\u003eWarningsAsErrors\u003c/code\u003e to make specific warnings critical\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eQuality requires discipline. Don\u0026rsquo;t submit pull requests with hundreds of warnings.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"ai-code-assistants--allies-or-amplifiers-of-ignorance\"\u003e\u003ca href=\"/posts/buzzword-driven-development/#ai-code-assistants--allies-or-amplifiers-of-ignorance\" title=\"AI Code Assistants – Allies or Amplifiers of Ignorance?\"\u003eAI Code Assistants – Allies or Amplifiers of Ignorance?\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eWhat happens when we neglect the basics? Will advanced AI code assistants rescue us, or merely magnify our negligence? AI assistants such as GitHub Copilot or Visual Studio IntelliCode are powerful, but without foundational understanding, they risk perpetuating poor practices. AI should augment our expertise, not substitute for it.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"the-double-edged-sword-of-ai-assistance\"\u003e\u003ca href=\"/posts/buzzword-driven-development/#the-double-edged-sword-of-ai-assistance\" title=\"The Double-Edged Sword of AI Assistance\"\u003eThe Double-Edged Sword of AI Assistance\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eAI code assistants excel at pattern recognition and can significantly boost productivity when used correctly. However, they also present unique challenges:\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eThe Good:\u003c/strong\u003e\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003eRapid Prototyping\u003c/strong\u003e: AI can quickly generate boilerplate code, allowing developers to focus on business logic\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eLearning Accelerator\u003c/strong\u003e: Exposes developers to new patterns and libraries they might not have discovered otherwise\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eConsistency\u003c/strong\u003e: Helps maintain coding patterns across team members\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e\u003cstrong\u003eThe Problematic:\u003c/strong\u003e\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003eFalse Confidence\u003c/strong\u003e: Developers may trust AI-generated code without understanding its implications\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003ePattern Perpetuation\u003c/strong\u003e: AI learns from existing codebases, potentially amplifying bad practices if they\u0026rsquo;re prevalent in training data\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eContext Blindness\u003c/strong\u003e: AI lacks understanding of specific project constraints, architectural decisions, or business requirements\u003c/li\u003e\n\u003c/ul\u003e\n\n\n\n\n\u003ch3 id=\"a-simple-example-ai-vs-analyzers\"\u003e\u003ca href=\"/posts/buzzword-driven-development/#a-simple-example-ai-vs-analyzers\" title=\"A Simple Example: AI vs. Analyzers\"\u003eA Simple Example: AI vs. Analyzers\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eConsider this AI-suggested code:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// Looks fine, but has problems\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kd\"\u003easync\u003c/span\u003e \u003cspan class=\"n\"\u003eTask\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"kt\"\u003estring\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003eGetDataAsync\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003eresult\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003ehttpClient\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eGetStringAsync\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eurl\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003eresult\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eToUpper\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cstrong\u003eProblems the analyzer would catch:\u003c/strong\u003e\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eMissing cancellation support\u003c/li\u003e\n\u003cli\u003eNo \u003ccode\u003eConfigureAwait(false)\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003eCulture-unaware string operation\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e\u003cstrong\u003eHere\u0026rsquo;s a cleaner approach (though still room for improvement):\u003c/strong\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// Clean, analyzer-compliant code\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kd\"\u003easync\u003c/span\u003e \u003cspan class=\"n\"\u003eTask\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"kt\"\u003estring\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003eGetDataAsync\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eCancellationToken\u003c/span\u003e \u003cspan class=\"n\"\u003ecancellationToken\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003edefault\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003eresult\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003ehttpClient\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eGetStringAsync\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eurl\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003ecancellationToken\u003c/span\u003e\u003cspan class=\"p\"\u003e).\u003c/span\u003e\u003cspan class=\"n\"\u003eConfigureAwait\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kc\"\u003efalse\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003eresult\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eToUpperInvariant\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe analyzer saves you from subtle issues and potential headaches that could cause production problems.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"using-ai-responsibly\"\u003e\u003ca href=\"/posts/buzzword-driven-development/#using-ai-responsibly\" title=\"Using AI Responsibly\"\u003eUsing AI Responsibly\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eAI can certainly help with quick boilerplate generation, learning new patterns, and maintaining consistency across your codebase. However, you need to watch out for the tendency to blindly trust AI suggestions, copying bad patterns from training data, or missing project-specific context that only human developers understand.\u003c/p\u003e\n\u003cp\u003eThe key is treating AI-generated code like any junior developer\u0026rsquo;s work—review it thoroughly before integration. Keep your analyzers enabled because they serve as an excellent safety net that catches AI mistakes. Most importantly, make sure you understand the code before using it, and use AI as a learning tool rather than a replacement for critical thinking.\u003c/p\u003e\n\u003cp\u003eThink of analyzers as your safety net when using AI assistance. They provide the quality guardrails that ensure AI-generated code meets your project\u0026rsquo;s standards, catching subtle issues that might otherwise slip through into production.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"the-bottom-line\"\u003e\u003ca href=\"/posts/buzzword-driven-development/#the-bottom-line\" title=\"The Bottom Line\"\u003eThe Bottom Line\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eDon\u0026rsquo;t let trendy buzzwords distract you from the basics. Good software development isn\u0026rsquo;t about adopting the latest methodology or framework—it\u0026rsquo;s about mastering fundamental practices that have proven their worth over time.\u003c/p\u003e\n\u003cp\u003eThe foundation of quality code starts with proper analyzers that catch problems early in the development cycle. These tools, specifically designed for .NET, provide immediate feedback and prevent common mistakes before they reach production. Combined with smart project settings that enforce quality standards, they create an environment where excellence becomes the default, not the exception.\u003c/p\u003e\n\u003cp\u003eWhen we add AI assistants to this mix, they become powerful allies rather than potential sources of technical debt. With analyzer safety nets in place, we can leverage AI\u0026rsquo;s speed and pattern recognition while maintaining the quality standards our profession demands.\u003c/p\u003e\n\u003cp\u003eMaster these fundamentals first. Everything else—whether it\u0026rsquo;s Domain Driven Design, microservices, or the next big thing—is just noise without a solid foundation. Quality isn\u0026rsquo;t optional; it\u0026rsquo;s our professional responsibility to the teams we work with and the users who depend on our software.\u003c/p\u003e\n","date_modified":"2026-05-26T10:22:03+02:00","date_published":"2025-07-23T17:00:00+02:00","id":"https://daily-devops.net/posts/buzzword-driven-development/","language":"en","summary":"Why fundamental .NET software quality must never be sacrificed for trendy buzzwords, including recommended analyzers, settings, and practices.","tags":["ai-code-assistant","bestpractices","codequality","csharp","dotnet","nuget","softwareengineering","technicaldebt"],"title":"Buzzword-Driven Development vs. Fundamental Software Quality","url":"https://daily-devops.net/posts/buzzword-driven-development/"},{"authors":[{"name":"Martin Stühmer","url":"https://daily-devops.net/authors/martin/"}],"content_html":"\u003cp\u003eIn the world of software development, there’s a recurring tension between \u003cstrong\u003ediscipline and improvisation\u003c/strong\u003e. Somewhere along that spectrum lies a phenomenon increasingly referred to as \u003cstrong\u003eVibe Coding\u003c/strong\u003e. The term evokes a style of development where engineers follow intuition and momentum rather than formal plans, processes, or design patterns.\u003c/p\u003e\n\u003cp\u003eIt’s fast, fluid, and occasionally brilliant. But is it sustainable in a .NET-based enterprise context?\u003c/p\u003e\n\u003cp\u003eLet’s examine the merits and pitfalls of Vibe Coding, with concrete examples from the .NET environment—and a proposal for when and how to use it.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"what-is-vibe-coding\"\u003e\u003ca href=\"/posts/vibe-coding-isnt-wrong-its-unfinished/#what-is-vibe-coding\" title=\"What Is Vibe Coding?\"\u003eWhat Is Vibe Coding?\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003eVibe Coding\u003c/strong\u003e refers to a spontaneous, improvisational approach to development. Instead of beginning with architecture diagrams or layered design, developers jump directly into writing code, letting their ideas evolve as they go. It’s often associated with prototyping, hackathons, or exploratory spikes.\u003c/p\u003e\n\u003cp\u003eIn .NET, this might mean spinning up an API in 15 minutes using \u003cstrong\u003eASP.NET Core Minimal APIs\u003c/strong\u003e, building UI experiments in \u003cstrong\u003eBlazor\u003c/strong\u003e, or testing LINQ expressions directly in \u003cstrong\u003eLINQPad\u003c/strong\u003e. The approach is highly creative—but it lacks formal structure.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"when-vibe-coding-accelerates-development\"\u003e\u003ca href=\"/posts/vibe-coding-isnt-wrong-its-unfinished/#when-vibe-coding-accelerates-development\" title=\"When Vibe Coding Accelerates Development\"\u003eWhen Vibe Coding Accelerates Development\u003c/a\u003e\u003c/h2\u003e\n\n\n\n\n\u003ch3 id=\"1-prototyping-apis-with-minimal-overhead\"\u003e\u003ca href=\"/posts/vibe-coding-isnt-wrong-its-unfinished/#1-prototyping-apis-with-minimal-overhead\" title=\"1. Prototyping APIs with Minimal Overhead\"\u003e1. Prototyping APIs with Minimal Overhead\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eThe \u003ccode\u003eMinimal API\u003c/code\u003e template introduced in .NET 6 is practically designed for vibe-driven exploration:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003ebuilder\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eWebApplication\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eCreateBuilder\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eargs\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003eapp\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003ebuilder\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eBuild\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eapp\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eMapGet\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;/status\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"p\"\u003e()\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003eResults\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eOk\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;Healthy\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e));\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eapp\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eRun\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eFor internal tools, demos, or early-stage ideation, this approach is efficient and expressive. It enables rapid iteration without over-engineering.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"2-rapid-ui-exploration-with-blazor\"\u003e\u003ca href=\"/posts/vibe-coding-isnt-wrong-its-unfinished/#2-rapid-ui-exploration-with-blazor\" title=\"2. Rapid UI Exploration with Blazor\"\u003e2. Rapid UI Exploration with Blazor\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eFront-end behavior often benefits from real-time experimentation. With Blazor (Server or WASM), developers can explore interactions, layouts, or component communication with minimal ceremony:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-html\" data-lang=\"html\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"nt\"\u003ebutton\u003c/span\u003e \u003cspan class=\"err\"\u003e@\u003c/span\u003e\u003cspan class=\"na\"\u003eonclick\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;Toggle\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003eClick me\u003cspan class=\"p\"\u003e\u0026lt;/\u003c/span\u003e\u003cspan class=\"nt\"\u003ebutton\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"nt\"\u003ep\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e@(isVisible ? \u0026#34;Hello!\u0026#34; : \u0026#34;\u0026#34;)\u003cspan class=\"p\"\u003e\u0026lt;/\u003c/span\u003e\u003cspan class=\"nt\"\u003ep\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis kind of feedback loop fosters creativity and engagement—essential when validating UI concepts.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"3-scripting-and-querying-with-linqpad\"\u003e\u003ca href=\"/posts/vibe-coding-isnt-wrong-its-unfinished/#3-scripting-and-querying-with-linqpad\" title=\"3. Scripting and Querying with LINQPad\"\u003e3. Scripting and Querying with LINQPad\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eTools like \u003ca href=\"https://www.linqpad.net/\" target=\"_blank\" rel=\"noopener external noreferrer\"\u003eLINQPad\u003c/a\u003e and \u003ccode\u003edotnet-script\u003c/code\u003e offer .NET developers a sandbox for testing LINQ queries, EF Core interactions, or complex logic in isolation—ideal for exploring new libraries or debugging issues without committing code to the main solution.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"where-vibe-coding-falls-short\"\u003e\u003ca href=\"/posts/vibe-coding-isnt-wrong-its-unfinished/#where-vibe-coding-falls-short\" title=\"Where Vibe Coding Falls Short\"\u003eWhere Vibe Coding Falls Short\u003c/a\u003e\u003c/h2\u003e\n\n\n\n\n\u003ch3 id=\"1-lack-of-architectural-foundations\"\u003e\u003ca href=\"/posts/vibe-coding-isnt-wrong-its-unfinished/#1-lack-of-architectural-foundations\" title=\"1. Lack of Architectural Foundations\"\u003e1. Lack of Architectural Foundations\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eA typical symptom of overextended Vibe Coding is \u003cstrong\u003eaccidental monoliths\u003c/strong\u003e. Consider a Minimal API that grows unchecked:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eapp\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eMapPost\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;/checkout\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"kd\"\u003easync\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eOrderRequest\u003c/span\u003e \u003cspan class=\"n\"\u003erequest\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003edb\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003enew\u003c/span\u003e \u003cspan class=\"n\"\u003eSqlConnection\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;...\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e// Data access, validation, business rules, and notifications—all in one handler.\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e});\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWhat begins as a prototype quickly becomes difficult to test, extend, or scale. Critical concepts like \u003cstrong\u003eseparation of concerns\u003c/strong\u003e, \u003cstrong\u003edependency injection\u003c/strong\u003e, and \u003cstrong\u003eSOLID principles\u003c/strong\u003e are often sidelined.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"2-no-formal-testing-strategy\"\u003e\u003ca href=\"/posts/vibe-coding-isnt-wrong-its-unfinished/#2-no-formal-testing-strategy\" title=\"2. No Formal Testing Strategy\"\u003e2. No Formal Testing Strategy\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eVibe Coding frequently leads to \u0026ldquo;just try it and see\u0026rdquo; logic. But in professional environments, we need:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eUnit tests with \u003ccode\u003exUnit\u003c/code\u003e or \u003ccode\u003eNUnit\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003eMocks with \u003ccode\u003eMoq\u003c/code\u003e or \u003ccode\u003eFakeItEasy\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003eTestable interfaces and inversion of control\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eWithout tests, teams rely on manual verification or fragile assumptions—both of which impair reliability and CI/CD readiness.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"3-technical-debt-accumulation\"\u003e\u003ca href=\"/posts/vibe-coding-isnt-wrong-its-unfinished/#3-technical-debt-accumulation\" title=\"3. Technical Debt Accumulation\"\u003e3. Technical Debt Accumulation\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003ePerhaps the most critical long-term risk is the \u003cstrong\u003eunmanaged accumulation of technical debt\u003c/strong\u003e. In .NET systems, this often manifests as:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eTight coupling between controllers and data access\u003c/li\u003e\n\u003cli\u003eHardcoded configuration logic\u003c/li\u003e\n\u003cli\u003eBusiness rules embedded directly in API endpoints\u003c/li\u003e\n\u003cli\u003eLack of documentation, test coverage, or separation of layers\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eWhat starts as quick progress soon creates \u003cstrong\u003emaintenance drag\u003c/strong\u003e: each change becomes riskier, onboarding new developers becomes harder, and long-term scalability suffers. Left unchecked, such debt can outweigh the initial productivity gains of vibe-driven work.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"a-professional-compromise-from-vibes-to-value\"\u003e\u003ca href=\"/posts/vibe-coding-isnt-wrong-its-unfinished/#a-professional-compromise-from-vibes-to-value\" title=\"A Professional Compromise: From Vibes to Value\"\u003eA Professional Compromise: From Vibes to Value\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eVibe Coding can play a \u003cstrong\u003evaluable role at the right phase of a project\u003c/strong\u003e. The key is knowing when to \u003cstrong\u003epivot from exploration to engineering\u003c/strong\u003e.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"suggested-progression\"\u003e\u003ca href=\"/posts/vibe-coding-isnt-wrong-its-unfinished/#suggested-progression\" title=\"Suggested Progression\"\u003eSuggested Progression\u003c/a\u003e\u003c/h3\u003e\n\u003ctable\u003e\n\t\u003cthead\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003cth\u003ePhase\u003c/th\u003e\n\t\t\t\t\t\u003cth\u003eApproach\u003c/th\u003e\n\t\t\t\u003c/tr\u003e\n\t\u003c/thead\u003e\n\t\u003ctbody\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003eIdeation\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003eVibe Coding with Minimal APIs or Blazor\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003eValidation\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003eAdd test harnesses, refactor into layers\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003eScaling\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003eIntroduce Clean Architecture, CI/CD, observability\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003eMaintenance\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003eDocument decisions, enforce standards\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\u003c/tbody\u003e\n\u003c/table\u003e\n\u003cp\u003eThe .NET platform is particularly well-suited to this transition:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ccode\u003eIHostBuilder\u003c/code\u003e and \u003ccode\u003eIServiceCollection\u003c/code\u003e offer clean extensibility.\u003c/li\u003e\n\u003cli\u003eProjects can evolve toward \u003cstrong\u003eClean Architecture\u003c/strong\u003e, with layering and dependency inversion.\u003c/li\u003e\n\u003cli\u003eTesting frameworks, analyzers, and tooling integrate smoothly into existing pipelines (Azure DevOps, GitHub Actions, etc.).\u003c/li\u003e\n\u003c/ul\u003e\n\n\n\n\n\u003ch2 id=\"conclusion\"\u003e\u003ca href=\"/posts/vibe-coding-isnt-wrong-its-unfinished/#conclusion\" title=\"Conclusion\"\u003eConclusion\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003eVibe Coding isn’t wrong—it’s unfinished.\u003c/strong\u003e — It’s a useful tool in the developer’s toolbox, especially for exploration, experimentation, and early validation. But in the context of long-lived .NET solutions, it must be tempered with structure, clarity, and discipline.\u003c/p\u003e\n\u003cp\u003eUse the vibe to build momentum.\nThen build the foundation that lasts—without the burden of unplanned debt.\u003c/p\u003e","date_modified":"2026-05-26T10:22:03+02:00","date_published":"2025-05-07T12:00:00+02:00","id":"https://daily-devops.net/posts/vibe-coding-isnt-wrong-its-unfinished/","language":"en","summary":"Explore the balance between intuitive coding and structured development in .NET, examining when vibe coding helps and when it hinders project success.","tags":["softwareengineering","bestpractices","codequality","csharp","dotnet","technicaldebt","testing"],"title":"Vibe Coding in .NET: Creative Catalyst or Maintenance Risk?","url":"https://daily-devops.net/posts/vibe-coding-isnt-wrong-its-unfinished/"},{"authors":[{"name":"Martin Stühmer","url":"https://daily-devops.net/authors/martin/"}],"content_html":"\u003cp\u003eWhen we activated static code analysis for the first time in one of my last projects, the overwhelming number of warnings exceeded expectations and highlighted gaps in the code. Without making any changes, the project already had a \u003cstrong\u003esignificant number of warnings\u003c/strong\u003e. After activating additional analyzers and updating some configurations, this number \u003cstrong\u003etemporarily increased dramatically\u003c/strong\u003e.\u003c/p\u003e\n\u003cp\u003eThe high number of warnings was initially daunting, but we saw it as an opportunity to significantly improve our code quality. At first glance, it seemed easier to suppress or ignore these warnings. But as I often remind my team, \u003cstrong\u003e\u0026ldquo;The code you create is a valuable legacy, so it\u0026rsquo;s important to build it carefully.\u0026rdquo;\u003c/strong\u003e Ignoring warnings today creates obstacles for future developers—and that could very well include you six months down the line.\u003c/p\u003e\n\u003cp\u003eThis experience reinforced the importance of managing warnings and errors systematically. Let me share some of the lessons we learned, the strategies we used to tame those 60,000 warnings, and how you can apply these techniques to your own projects.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"from-chaos-to-clarity-why-warnings-matter\"\u003e\u003ca href=\"/posts/managing-errors-warnings-and-configurations/#from-chaos-to-clarity-why-warnings-matter\" title=\"From Chaos to Clarity: Why Warnings Matter\"\u003eFrom Chaos to Clarity: Why Warnings Matter\u003c/a\u003e\u003c/h2\u003e\n\n\n\n\n\u003ch3 id=\"the-cost-of-ignoring-warnings\"\u003e\u003ca href=\"/posts/managing-errors-warnings-and-configurations/#the-cost-of-ignoring-warnings\" title=\"The Cost of Ignoring Warnings\"\u003eThe Cost of Ignoring Warnings\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eWarnings signal potential issues, alerting us to things that might go wrong. Ignoring these warnings can lead to subtle bugs, poor maintainability, and wasted time during debugging. When a project accumulates thousands of warnings, it creates \u003cstrong\u003ewarning fatigue\u003c/strong\u003e: developers become so desensitized to them that even critical issues go unnoticed.\u003c/p\u003e\n\u003cp\u003eOur project’s warnings could be grouped into three categories:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e\u003cstrong\u003eLegacy Code Issues\u003c/strong\u003e: Deprecated APIs and outdated practices from years of development.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eAnalyzer Rules\u003c/strong\u003e: New code-quality rules introduced by Roslyn analyzers and other tools.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eNullability Warnings\u003c/strong\u003e: Warnings about potential null reference exceptions after enabling nullable reference types.\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eEach required a distinct approach to address.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"configuring-net-build-turning-the-tide-against-warnings\"\u003e\u003ca href=\"/posts/managing-errors-warnings-and-configurations/#configuring-net-build-turning-the-tide-against-warnings\" title=\"Configuring .NET Build: Turning the Tide Against Warnings\"\u003eConfiguring .NET Build: Turning the Tide Against Warnings\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eThe first step in tackling warnings is understanding how to configure their behavior in .NET Build. By setting global and file-specific properties, we gained control over how warnings were treated across the project.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"global-properties-in-net-build\"\u003e\u003ca href=\"/posts/managing-errors-warnings-and-configurations/#global-properties-in-net-build\" title=\"Global Properties in .NET Build\"\u003eGlobal Properties in .NET Build\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eA centralized configuration helps ensure consistency across your solution. While some properties tighten the rules around warnings, others allow for flexibility where needed. Here’s how we set up critical properties in our project:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-xml\" data-lang=\"xml\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003e\u0026lt;PropertyGroup\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nt\"\u003e\u0026lt;TreatWarningsAsErrors\u0026gt;\u003c/span\u003etrue\u003cspan class=\"nt\"\u003e\u0026lt;/TreatWarningsAsErrors\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nt\"\u003e\u0026lt;WarningsAsErrors\u0026gt;\u003c/span\u003eCS8602;CS8604\u003cspan class=\"nt\"\u003e\u0026lt;/WarningsAsErrors\u0026gt;\u003c/span\u003e \u003cspan class=\"c\"\u003e\u0026lt;!-- Specific warnings treated as errors --\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nt\"\u003e\u0026lt;WarningsNotAsErrors\u0026gt;\u003c/span\u003eCS1591\u003cspan class=\"nt\"\u003e\u0026lt;/WarningsNotAsErrors\u0026gt;\u003c/span\u003e \u003cspan class=\"c\"\u003e\u0026lt;!-- Exceptions for specific warnings --\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nt\"\u003e\u0026lt;NoWarn\u0026gt;\u003c/span\u003eCS0618\u003cspan class=\"nt\"\u003e\u0026lt;/NoWarn\u0026gt;\u003c/span\u003e \u003cspan class=\"c\"\u003e\u0026lt;!-- Suppressing non-critical warnings --\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003e\u0026lt;/PropertyGroup\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003e\u003ccode\u003eTreatWarningsAsErrors\u003c/code\u003e\u003c/strong\u003e: This global setting enforces a \u0026ldquo;no warnings allowed\u0026rdquo; policy, treating every warning as a build-breaking error. While this is great for enforcing high standards, it can be overly strict for legacy codebases.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e\u003ccode\u003eWarningsAsErrors\u003c/code\u003e\u003c/strong\u003e: This allows you to escalate specific warnings to errors. For example, warnings like \u003ccode\u003eCS8602\u003c/code\u003e (dereference of a possibly null reference) and \u003ccode\u003eCS8604\u003c/code\u003e (null passed as a non-nullable parameter) were prioritized as errors in our project.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e\u003ccode\u003eWarningsNotAsErrors\u003c/code\u003e\u003c/strong\u003e: A complementary property to \u003ccode\u003eWarningsAsErrors\u003c/code\u003e, it provides exceptions to the rule. In our case, we decided not to escalate \u003ccode\u003eCS1591\u003c/code\u003e (missing XML documentation) to an error because enforcing this across the entire project wasn’t immediately feasible.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e\u003ccode\u003eNoWarn\u003c/code\u003e\u003c/strong\u003e: Temporarily suppresses warnings that are acknowledged but cannot be resolved right away. For instance, \u003ccode\u003eCS0618\u003c/code\u003e (usage of deprecated APIs) was suppressed for legacy code that we plan to refactor incrementally.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eCombining these properties allowed us to enforce critical standards while giving flexibility for legacy code.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"practical-strategies-for-managing-warnings\"\u003e\u003ca href=\"/posts/managing-errors-warnings-and-configurations/#practical-strategies-for-managing-warnings\" title=\"Practical Strategies for Managing Warnings\"\u003ePractical Strategies for Managing Warnings\u003c/a\u003e\u003c/h2\u003e\n\n\n\n\n\u003ch3 id=\"1-triage-and-categorize-warnings\"\u003e\u003ca href=\"/posts/managing-errors-warnings-and-configurations/#1-triage-and-categorize-warnings\" title=\"1. Triage and Categorize Warnings\"\u003e1. Triage and Categorize Warnings\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eNot all warnings are created equal. We divided them into:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003eCritical Warnings\u003c/strong\u003e: Must be resolved immediately (e.g., potential null reference exceptions).\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eInformational Warnings\u003c/strong\u003e: Desirable to fix but not urgent (e.g., missing XML documentation comments).\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eLegacy Warnings\u003c/strong\u003e: Related to outdated APIs or practices that require phased modernization.\u003c/li\u003e\n\u003c/ul\u003e\n\n\n\n\n\u003ch4 id=\"example-prioritizing-critical-warnings\"\u003e\u003ca href=\"/posts/managing-errors-warnings-and-configurations/#example-prioritizing-critical-warnings\" title=\"Example: Prioritizing Critical Warnings\"\u003eExample: Prioritizing Critical Warnings\u003c/a\u003e\u003c/h4\u003e\n\u003cp\u003eCritical warnings, like nullability issues, were escalated to errors using the \u003ccode\u003eWarningsAsErrors\u003c/code\u003e property:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-xml\" data-lang=\"xml\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003e\u0026lt;WarningsAsErrors\u0026gt;\u003c/span\u003eCS8602;CS8604\u003cspan class=\"nt\"\u003e\u0026lt;/WarningsAsErrors\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis ensured they were always addressed before a build could succeed.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"2-using-automatic-code-fixers-wisely\"\u003e\u003ca href=\"/posts/managing-errors-warnings-and-configurations/#2-using-automatic-code-fixers-wisely\" title=\"2. Using Automatic Code Fixers Wisely\"\u003e2. Using Automatic Code Fixers Wisely\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eVisual Studio provides a convenient feature for resolving many warnings through \u003cstrong\u003eautomatic code fixers\u003c/strong\u003e. These tools analyze the code and offer one-click solutions for issues, such as simplifying expressions, adding missing null checks, or suppressing warnings with \u003ccode\u003e#pragma\u003c/code\u003e directives. While these fixers can save time, they must be used with caution.\u003c/p\u003e\n\n\n\n\n\u003ch4 id=\"example-applying-an-automatic-code-fix\"\u003e\u003ca href=\"/posts/managing-errors-warnings-and-configurations/#example-applying-an-automatic-code-fix\" title=\"Example: Applying an Automatic Code Fix\"\u003eExample: Applying an Automatic Code Fix\u003c/a\u003e\u003c/h4\u003e\n\u003cp\u003eConsider the following nullable warning:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kt\"\u003estring?\u003c/span\u003e \u003cspan class=\"n\"\u003ename\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"kc\"\u003enull\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eConsole\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eWriteLine\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eLength\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e \u003cspan class=\"c1\"\u003e// Warning: Possible null reference exception\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eVisual Studio might suggest adding a null-forgiving operator (\u003ccode\u003e!\u003c/code\u003e) to suppress the warning:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eConsole\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eWriteLine\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e!.\u003c/span\u003e\u003cspan class=\"n\"\u003eLength\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e \u003cspan class=\"c1\"\u003e// Suppression applied\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWhile this eliminates the warning, it introduces a potential runtime exception if \u003ccode\u003ename\u003c/code\u003e is actually \u003ccode\u003enull\u003c/code\u003e. This type of fix addresses the symptom but not the root cause, leaving the code vulnerable.\u003c/p\u003e\n\n\n\n\n\u003ch4 id=\"risks-of-overusing-automatic-fixers\"\u003e\u003ca href=\"/posts/managing-errors-warnings-and-configurations/#risks-of-overusing-automatic-fixers\" title=\"Risks of Overusing Automatic Fixers\"\u003eRisks of Overusing Automatic Fixers\u003c/a\u003e\u003c/h4\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003eMasking Real Issues\u003c/strong\u003e: Automatic fixes often silence warnings without addressing underlying logic problems.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eIntroducing Complexity\u003c/strong\u003e: Generated fixes can add unnecessary code, such as redundant null checks, making the code harder to read and maintain.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eFalse Sense of Security\u003c/strong\u003e: Developers might trust that the issue is resolved, only to find that the automatic fix created new problems.\u003c/li\u003e\n\u003c/ul\u003e\n\n\n\n\n\u003ch4 id=\"best-practices-for-using-code-fixers\"\u003e\u003ca href=\"/posts/managing-errors-warnings-and-configurations/#best-practices-for-using-code-fixers\" title=\"Best Practices for Using Code Fixers\"\u003eBest Practices for Using Code Fixers\u003c/a\u003e\u003c/h4\u003e\n\u003col\u003e\n\u003cli\u003e\u003cstrong\u003eReview Every Fix\u003c/strong\u003e: Treat automatic suggestions as starting points. Always evaluate whether the proposed fix aligns with your code’s intent.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eCombine with Analysis\u003c/strong\u003e: Use code fixers in tandem with a clear understanding of the warning.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eAvoid Blanket Suppressions\u003c/strong\u003e: If a fixer suggests suppressing a warning (e.g., adding \u003ccode\u003e#pragma warning disable\u003c/code\u003e), consider whether this is appropriate or just hiding a deeper issue.\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eBy using automatic code fixers wisely, you can ensure that they improve your code’s quality rather than creating hidden risks.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"real-world-example-integrating-warning-management-in-cicd-pipelines\"\u003e\u003ca href=\"/posts/managing-errors-warnings-and-configurations/#real-world-example-integrating-warning-management-in-cicd-pipelines\" title=\"Real-World Example: Integrating Warning Management in CI/CD Pipelines\"\u003eReal-World Example: Integrating Warning Management in CI/CD Pipelines\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eOne of the most effective ways we managed warnings was by integrating warning handling into our \u003cstrong\u003eCI/CD pipeline\u003c/strong\u003e. This allowed us to enforce consistent rules across every build and ensure that no warning could slip through the cracks during deployment.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"automated-build-configuration\"\u003e\u003ca href=\"/posts/managing-errors-warnings-and-configurations/#automated-build-configuration\" title=\"Automated Build Configuration\"\u003eAutomated Build Configuration\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eWe configured our \u003cstrong\u003eCI pipeline\u003c/strong\u003e to treat warnings as errors, particularly for release builds. This configuration forced the team to resolve any warnings before code could be deployed, ensuring that only clean code made it to production. By doing this, we effectively ensured that our codebase maintained a high standard without relying solely on manual intervention.\u003c/p\u003e\n\u003cp\u003eHere’s how we configured the pipeline using a \u003cstrong\u003eYAML file\u003c/strong\u003e for a .NET Core project to treat warnings as errors during the build process:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-yaml\" data-lang=\"yaml\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003esteps\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e- \u003cspan class=\"nt\"\u003etask\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eDotNetCoreCLI@2\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003einputs\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e\u003cspan class=\"nt\"\u003ecommand\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;build\u0026#39;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e\u003cspan class=\"nt\"\u003earguments\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;--configuration Release /p:TreatWarningsAsErrors=true\u0026#39;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIn this setup:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eFor \u003cstrong\u003erelease builds\u003c/strong\u003e, the \u003ccode\u003eTreatWarningsAsErrors=true\u003c/code\u003e argument was specified, ensuring that the build would fail if any warning appeared.\u003c/li\u003e\n\u003cli\u003eFor \u003cstrong\u003edebug builds\u003c/strong\u003e, we chose to allow warnings, as they would not disrupt the ongoing development work but would still be tracked for later resolution.\u003c/li\u003e\n\u003c/ul\u003e\n\n\n\n\n\u003ch3 id=\"ensuring-consistency-across-environments\"\u003e\u003ca href=\"/posts/managing-errors-warnings-and-configurations/#ensuring-consistency-across-environments\" title=\"Ensuring Consistency Across Environments\"\u003eEnsuring Consistency Across Environments\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eBy enforcing these settings in the pipeline, we ensured that no matter who worked on the code, whether locally or remotely, the same strict rules were applied. This helped prevent situations where developers ignored warnings during their local builds but let them accumulate over time, only to be caught late in the development cycle.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"continuous-monitoring-and-refinement\"\u003e\u003ca href=\"/posts/managing-errors-warnings-and-configurations/#continuous-monitoring-and-refinement\" title=\"Continuous Monitoring and Refinement\"\u003eContinuous Monitoring and Refinement\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eAs part of our ongoing integration process, we continually refined the warning rules based on feedback and evolving project needs. We also configured the pipeline to provide detailed reports on warnings and errors, which could be easily reviewed by the team. This helped us identify patterns or areas that required more attention, such as recurring issues with nullability or outdated API usage.\u003c/p\u003e\n\u003cp\u003eBy integrating warning management into our CI/CD pipeline, we automated and enforced quality standards across the board. This shift not only improved the code’s stability but also created a more accountable and transparent development process.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"final-thoughts-building-a-legacy\"\u003e\u003ca href=\"/posts/managing-errors-warnings-and-configurations/#final-thoughts-building-a-legacy\" title=\"Final Thoughts: Building a Legacy\"\u003eFinal Thoughts: Building a Legacy\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eAs software developers, the code we write today becomes the foundation for future teams—or for ourselves. Ignoring warnings and errors undermines that foundation. By managing them effectively, we leave behind a valuable legacy of maintainable, high-quality code.\u003c/p\u003e\n\u003cp\u003eThrough the measures we implemented, including integrating warning management into our CI/CD pipeline, we were able to address a number of previously unknown issues. Many bugs that had quietly lurked in the codebase were brought to light and resolved—issues that had been hidden under the surface and hadn\u0026rsquo;t surfaced until we made the handling of warnings and errors a priority. Some of these bugs were revealed through the warnings themselves, while others came to light as we reviewed log files during builds and deployments.\u003c/p\u003e\n\u003cp\u003eThis process reinforced a crucial point: warnings are not just noise. They often signal deeper issues that need to be resolved before they cause significant problems down the road.\u003c/p\u003e\n\u003cp\u003eWhile we may never completely rid our projects of warnings, the key is \u003cstrong\u003eto manage them effectively\u003c/strong\u003e—and in doing so, create cleaner, more maintainable code that will stand the test of time.\u003c/p\u003e","date_modified":"2026-05-26T10:22:03+02:00","date_published":"2024-12-23T16:00:00+01:00","id":"https://daily-devops.net/posts/managing-errors-warnings-and-configurations/","language":"en","summary":"Learn strategies for managing static code analysis warnings, improving code quality, configuring analyzers, and integrating into CI/CD pipelines.","tags":["msbuild","bestpractices","codequality","csharp","dotnet","softwareengineering","technicaldebt"],"title":"Managing Errors, Warnings, and Configurations in C# and .NET","url":"https://daily-devops.net/posts/managing-errors-warnings-and-configurations/"},{"authors":[{"name":"Martin Stühmer","url":"https://daily-devops.net/authors/martin/"}],"content_html":"\u003cp\u003eAs developers, we’re often tasked with maintaining and modernizing legacy codebases that were written long before some of the best practices of today—such as nullability annotations—were available. While modern C# now supports nullable reference types, enabling us to avoid the dreaded \u003ccode\u003eNullReferenceException\u003c/code\u003e, introducing this feature to existing, large codebases can be a challenge.\u003c/p\u003e\n\u003cp\u003eIn this article, I’ll share my step-by-step approach for introducing nullability into a legacy .NET and C# project. You’ll learn how to apply nullability in a controlled, incremental manner using project-level settings, scoped annotations, and file/method-level directives, all while maintaining the integrity of your legacy codebase. After all, modernizing your code doesn’t have to be an all-or-nothing endeavor—gradual change is key to a successful transition. Let’s get started!\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"why-gradually-introduce-nullability\"\u003e\u003ca href=\"/posts/introducing-nullability-in-legacy-code/#why-gradually-introduce-nullability\" title=\"Why Gradually Introduce Nullability?\"\u003eWhy Gradually Introduce Nullability?\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eNullability annotations in C# allow us to specify whether a reference type can be \u003ccode\u003enull\u003c/code\u003e or not. This feature brings more type safety and reliability to your code, reducing the chance of runtime errors caused by \u003ccode\u003enull\u003c/code\u003e values. But here’s the challenge: introducing nullability into an existing, possibly large codebase and no clear code style, where methods and properties might be riddled with potential \u003ccode\u003enull\u003c/code\u003es, can result in an overwhelming number of compiler warnings. In such cases, it\u0026rsquo;s easy to give up on nullability altogether, leaving your codebase vulnerable to null reference exceptions. But it doesn\u0026rsquo;t have to be that way.\u003c/p\u003e\n\u003cp\u003eTo address this, you can take an \u003cstrong\u003eincremental approach\u003c/strong\u003e. Rather than trying to make your entire codebase \u003ccode\u003enull\u003c/code\u003e-safe in one go, you can introduce nullability \u003cstrong\u003estep by step\u003c/strong\u003e—starting with new code and gradually refactoring old code. This method minimizes disruption and lets your team handle the transition without being flooded with warnings.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"step-1-understanding-nullability-annotations-in-c\"\u003e\u003ca href=\"/posts/introducing-nullability-in-legacy-code/#step-1-understanding-nullability-annotations-in-c\" title=\"Step 1: Understanding Nullability Annotations in C#\"\u003eStep 1: Understanding Nullability Annotations in C#\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eIn modern C#, a \u003ccode\u003estring\u003c/code\u003e is assumed \u003cstrong\u003enot nullable\u003c/strong\u003e by default, meaning it cannot contain a \u003ccode\u003enull\u003c/code\u003e value without a compiler warning. However, you can explicitly declare a \u003ccode\u003estring\u003c/code\u003e as nullable by using the \u003ccode\u003estring?\u003c/code\u003e syntax. Here’s an example:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kt\"\u003estring\u003c/span\u003e \u003cspan class=\"n\"\u003enonNullableString\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;Hello\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e   \u003cspan class=\"c1\"\u003e// Can\u0026#39;t be null, compiler will warn\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kt\"\u003estring?\u003c/span\u003e \u003cspan class=\"n\"\u003enullableString\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"kc\"\u003enull\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e        \u003cspan class=\"c1\"\u003e// Can be null, no compiler warning\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe nullable \u003ccode\u003estring?\u003c/code\u003e type indicates that the variable may contain a \u003ccode\u003enull\u003c/code\u003e value, while the non-nullable \u003ccode\u003estring\u003c/code\u003e enforces that \u003ccode\u003enull\u003c/code\u003e values are not allowed.\u003c/p\u003e\n\u003cp\u003eThe beauty of nullable reference types is that they make your intent clear, and C#’s compiler will help enforce this through warnings whenever there’s a potential for \u003ccode\u003enull\u003c/code\u003e dereferencing. And there is a huge list of possible nullable warnings, see \u003ca href=\"https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/compiler-messages/nullable-warnings\" target=\"_blank\" rel=\"noopener external noreferrer\"\u003eNullable reference types warnings\u003c/a\u003e.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"step-2-enabling-nullability-at-the-project-level\"\u003e\u003ca href=\"/posts/introducing-nullability-in-legacy-code/#step-2-enabling-nullability-at-the-project-level\" title=\"Step 2: Enabling Nullability at the Project Level\"\u003eStep 2: Enabling Nullability at the Project Level\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eThe most straightforward way to introduce nullability across your entire project is by enabling it globally in your project’s \u003ccode\u003e.csproj\u003c/code\u003e file or your \u003ccode\u003eDirectory.Build.props\u003c/code\u003e file. You can do this by adding the following property inside your \u003ccode\u003e\u0026lt;PropertyGroup\u0026gt;\u003c/code\u003e section:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-xml\" data-lang=\"xml\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003e\u0026lt;PropertyGroup\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nt\"\u003e\u0026lt;Nullable\u0026gt;\u003c/span\u003eenable\u003cspan class=\"nt\"\u003e\u0026lt;/Nullable\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003e\u0026lt;/PropertyGroup\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis setting will enable nullable reference types for every file in the project, enforcing null-safety checks everywhere. However, this can result in a lot of warnings right away—especially in a large legacy codebase. If you’re not ready to tackle all these warnings at once, you might want to consider a more cautious approach.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"a-safer-option-nullableannotationsnullable\"\u003e\u003ca href=\"/posts/introducing-nullability-in-legacy-code/#a-safer-option-nullableannotationsnullable\" title=\"A Safer Option: \u0026lt;Nullable\u0026gt;annotations\u0026lt;/Nullable\u0026gt;\"\u003eA Safer Option: \u003ccode\u003e\u0026lt;Nullable\u0026gt;annotations\u0026lt;/Nullable\u0026gt;\u003c/code\u003e\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eInstead of enabling nullability across the board from the start, you can take a more cautious approach by using the following property in your \u003ccode\u003e.csproj\u003c/code\u003e file:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-xml\" data-lang=\"xml\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003e\u0026lt;PropertyGroup\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nt\"\u003e\u0026lt;Nullable\u0026gt;\u003c/span\u003eannotations\u003cspan class=\"nt\"\u003e\u0026lt;/Nullable\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003e\u0026lt;/PropertyGroup\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis option only enables \u003cstrong\u003eannotations\u003c/strong\u003e (i.e., you can specify nullable or non-nullable reference types), but it won’t trigger warnings for potential null reference issues in your code. This way, you can begin adding annotations like \u003ccode\u003estring?\u003c/code\u003e and \u003ccode\u003eint?\u003c/code\u003e to clarify nullability without worrying about fixing warnings immediately. This is particularly useful for large legacy projects where refactoring everything at once isn\u0026rsquo;t practical.\u003c/p\u003e\n\u003cp\u003eOnce you feel confident with the annotations you’ve added, you can switch the property to \u003ccode\u003e\u0026lt;Nullable\u0026gt;enable\u0026lt;/Nullable\u0026gt;\u003c/code\u003e and start addressing the warnings generated by the compiler for potential nullability issues. But until then, you can work on adding nullability annotations at your own pace.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"step-3-using-nullable-directives-for-scoped-control\"\u003e\u003ca href=\"/posts/introducing-nullability-in-legacy-code/#step-3-using-nullable-directives-for-scoped-control\" title=\"Step 3: Using #nullable Directives for Scoped Control\"\u003eStep 3: Using \u003ccode\u003e#nullable\u003c/code\u003e Directives for Scoped Control\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eIn addition to enabling nullability project-wide, you may also want to control nullability at more granular levels. C# provides the \u003ccode\u003e#nullable\u003c/code\u003e directive, which allows you to enable or disable nullability checks at the file or method level. This gives you more control over where nullability is enforced, allowing you to gradually introduce null-safety to your codebase.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"file-level-nullability-control\"\u003e\u003ca href=\"/posts/introducing-nullability-in-legacy-code/#file-level-nullability-control\" title=\"File-Level Nullability Control\"\u003eFile-Level Nullability Control\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eIf you want to enable nullability for just a specific file, you can use the \u003ccode\u003e#nullable enable\u003c/code\u003e directive at the top of the file. This should be the first line in the file, before any namespaces or other code. Here’s an example:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cp\"\u003e#nullable\u003c/span\u003e \u003cspan class=\"n\"\u003eenable\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eCustomerService\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kt\"\u003estring?\u003c/span\u003e \u003cspan class=\"n\"\u003eGetCustomerName\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"n\"\u003eid\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"c1\"\u003e// Null-safety checks enabled\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"kc\"\u003enull\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"c1\"\u003e// This is allowed because \u0026#39;string?\u0026#39; is nullable\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"k\"\u003evoid\u003c/span\u003e \u003cspan class=\"n\"\u003eAddCustomer\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eCustomer\u003c/span\u003e \u003cspan class=\"n\"\u003ecustomer\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"c1\"\u003e// Additional code\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIn this case, all code within the file will now adhere to nullability rules. This is useful if you\u0026rsquo;re working on a new file or refactoring an older one.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"method-level-nullability-control\"\u003e\u003ca href=\"/posts/introducing-nullability-in-legacy-code/#method-level-nullability-control\" title=\"Method-Level Nullability Control\"\u003eMethod-Level Nullability Control\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eIf you only want to enable nullability for a specific method rather than an entine file, you can do so using \u003ccode\u003e#nullable\u003c/code\u003e around the method itself. This can help you focus on null-safety checks for specific methods without affecting the rest of the file. This is particularly helpful when you’re working on large files and want to incrementally refactor certain methods:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eCustomerService\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"cp\"\u003e#nullable\u003c/span\u003e \u003cspan class=\"n\"\u003eenable\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kt\"\u003estring?\u003c/span\u003e \u003cspan class=\"n\"\u003eGetCustomerName\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"n\"\u003eid\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"c1\"\u003e// Null-safety checks enabled just for this method\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"cp\"\u003e#nullable\u003c/span\u003e \u003cspan class=\"n\"\u003erestore\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"k\"\u003evoid\u003c/span\u003e \u003cspan class=\"n\"\u003eLegacyMethod\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eCustomer\u003c/span\u003e \u003cspan class=\"n\"\u003ecustomer\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"c1\"\u003e// Null-safety checks are restored to project setting\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis way, you can enable null-safety in smaller, manageable chunks and refactor legacy methods over time. The \u003ccode\u003e#nullable restore\u003c/code\u003e directive resets the nullability setting to the project level, ensuring that the rest of the file adheres to the global nullability setting.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"step-4-transitioning-to-full-nullability\"\u003e\u003ca href=\"/posts/introducing-nullability-in-legacy-code/#step-4-transitioning-to-full-nullability\" title=\"Step 4: Transitioning to Full Nullability\"\u003eStep 4: Transitioning to Full Nullability\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eOnce you’ve added nullability annotations throughout your code and feel confident in the accuracy of those annotations, you can switch the project setting from \u003ccode\u003e\u0026lt;Nullable\u0026gt;annotations\u0026lt;/Nullable\u0026gt;\u003c/code\u003e to \u003ccode\u003e\u0026lt;Nullable\u0026gt;enable\u0026lt;/Nullable\u0026gt;\u003c/code\u003e. This change will turn on full null-safety, meaning the compiler will now generate warnings for potential null reference issues.\u003c/p\u003e\n\u003cp\u003eAt this point, your task will be to resolve any warnings by checking for null values, using the null-coalescing operator (\u003ccode\u003e??\u003c/code\u003e), or adjusting method signatures to ensure null-safety. Gradually fixing these warnings will make your code more robust and reduce the risk of runtime null reference exceptions.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"conclusion\"\u003e\u003ca href=\"/posts/introducing-nullability-in-legacy-code/#conclusion\" title=\"Conclusion\"\u003eConclusion\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eIntroducing nullability into a legacy codebase doesn’t have to be a daunting task. By taking an incremental approach—starting with project-level settings like \u003ccode\u003e\u0026lt;Nullable\u0026gt;annotations\u0026lt;/Nullable\u0026gt;\u003c/code\u003e, using \u003ccode\u003e#nullable\u003c/code\u003e directives for scoped control, and focusing on new or refactored code—you can modernize your codebase while avoiding the flood of warnings that comes with a full-on switch to nullability.\u003c/p\u003e\n\u003cp\u003eRemember, the goal is to improve the long-term reliability of your code, and with nullability annotations, you’re well on your way to a safer, more maintainable C# project. Take it step by step, and soon, your legacy codebase will be a thing of the past. Modern, \u003ccode\u003enull\u003c/code\u003e-safe code awaits\u003c/p\u003e","date_modified":"2026-05-26T10:22:03+02:00","date_published":"2024-10-07T17:15:00+02:00","id":"https://daily-devops.net/posts/introducing-nullability-in-legacy-code/","language":"en","summary":"Step-by-step guide for implementing nullable reference types in legacy .NET and C# codebases with practical strategies, patterns, and best practices.","tags":["csharp","bestpractices","codequality","dotnet","softwareengineering"],"title":"Introducing Nullability in Legacy .NET Code","url":"https://daily-devops.net/posts/introducing-nullability-in-legacy-code/"}],"language":"en","title":"Code Quality and Clean Code Principles on Daily DevOps \u0026 .NET","version":"https://jsonfeed.org/version/1.1"}