Best Practices in Architecture and .NET Development

“Best practice” is one of the most abused terms in software engineering. It usually means “what looks defensible in a slide deck” rather than “what actually works in production”. The articles in this collection treat the label with skepticism — every practice is a trade-off, and the only honest question is which trade-off applies to the situation in front of you.

The framing across these articles is consistent: a practice is worth adopting when the failure mode it prevents matters more than the overhead it adds. SOLID principles are a starting point, not a creed. Clean Code passages get quoted in code reviews far more often than the code in the repository reflects them. The articles examine where the gap between stated practice and actual behavior comes from and what changes that gap in real teams.

Specific topics span the .NET stack and the systems built on top of it. Defensive programming with throw helpers, multi-targeting build hygiene, central package management, structured logging that does not lie about what happened, mutation testing that exposes the blind spots line coverage hides, and TimeProvider adoption two years after it shipped — each is treated as a concrete intervention with measurable cost and benefit rather than a virtue to be signaled.

A separate cluster addresses the practices that hold up under pressure: incident response procedures rehearsed before the incident, audit logging that survives the audit, access control that fails closed, and feedback loops that catch regressions before customers do. The recurring observation is that practices fail not because they are wrong but because they are applied as rituals rather than as decisions with named consequences.

The collection also documents the practices the author has changed his mind about. Calling a pattern a best practice today and a mistake five years later is normal — the discipline is naming why the context shifted, not pretending the original advice was always correct.

Standardize or Drift: One Defaults Package for All Your Solutions

Standardize or Drift: One Defaults Package for All Your Solutions

Directory.Build.props drift is the quiet tax every multi-repo .NET org pays. NetEvolve.Defaults ships MSBuild properties, .editorconfig, NuGet Audit, and ten Roslyn diagnostics as a private-asset NuGet package. Bump the version once, every repo gets the upgrade.
The Codebase Doesn't Know You Quit

The Codebase Doesn't Know You Quit

The first four parts of this series treated legacy as something between me and myself: Past Self leaving code for Future Self, with an AI in the middle. That framing is incomplete. Code outlives employment, not just memory. The companies I worked for have forgotten most of what I did there. The repositories haven’t. This is the fifth part of the Code as Legacy series, about the legacy you leave when you’re not around to defend it.
The Machine Writes. The Legacy Is Still Mine.

The Machine Writes. The Legacy Is Still Mine.

Part three ended with me promising to stop adding to Past Self’s pile. I was already wrong. I’m not the only author anymore: Copilot and Claude finish methods before I’ve finished thinking, shipping code under my name with my git config as the committer. This is part four of the Code as Legacy series, about what changes when the author is partly a machine.
You're Shipping Bugs Faster, and Your Tests Are Helping

You're Shipping Bugs Faster, and Your Tests Are Helping

AI coding agents make you faster. They also make your bugs faster: they generate plausible-looking code, you review quickly, the tests pass, and production breaks two weeks later. I keep falling into this pattern myself, always on the happy path, telling myself the edge cases come later. They don’t. Here’s the pattern, why most test frameworks make it worse, and how TUnit fights it.
I'm Done Making Empty Promises

I'm Done Making Empty Promises

I’ve spent two articles in this series describing Past Self and the damage he left behind. What I haven’t said yet is the uncomfortable part: I am Past Self, right now, making the same promises he made. // TODO: fix this properly. We’ll clean that up after the release. I’ll write the tests when there’s time. This is the third part of the Code as Legacy series, and I’m deciding to stop.