Every team I have joined already knew what SOLID stands for. Most could recite the CQRS definition. Neither fact had any measurable effect on their defect rate, and for a long time I assumed that meant the principles were overrated.

They are not overrated. They are just routinely taught as properties of code, when the value they deliver is a property of a team. At Zafin, leading delivery and mentoring 10+ engineers through consistent SOLID and CQRS adoption cut defect leakage by 40%. Very little of getting there was explaining the principles.

The bug class that actually leaks

When I looked at what was escaping to production, the expensive defects were almost never wrong algorithms. They were disagreements. Two services held slightly different opinions about whether a discount was still valid. A validation existed in three places and had been updated in two. A rule was enforced in a controller in one service and in a repository in another, so the second path bypassed it entirely.

That is not a knowledge problem, and you cannot test it away — each component passes its own tests, because each was built to its author's understanding of the rule. It is a boundary problem, and boundaries are a team-level artefact.

Four copies of a rule versus one owner On the left, four services each carry their own copy of an eligibility check, so a rule change means four edits and four chances to disagree. On the right, the rule lives in one agreement aggregate and the same four services call it, so there is one place to change and one answer. Before: four copies Pricing · own check Invoicing · own check Reporting · own check Deal API · own check four places to change, four ways to disagree After: one owner Agreement aggregate the invariant lives here Pricing Invoicing Reporting and Deal API one place to change, one answer
The defects that leak are rarely wrong logic. They are two components that were each individually correct about a rule they disagreed on.

SOLID as a shared answer to “where does this go?”

Read the original write-up of the design principles and what strikes you is how much of it is about the direction of dependencies and the cost of change — not about class size or file layout, which is what the principles have degraded into in most code reviews.

The version that produced results was much narrower than the acronym. For every change, one question: where does this behaviour belong, and who owns it? Single-responsibility stopped being “this class is too big” and became “this class has two reasons to change, held by two different people.” Dependency inversion stopped being “introduce an interface” and became “the domain rule must not import the database, because the rule outlives the storage choice.”

Framed that way the principles are decidable in review. “Is this class cohesive?” produces opinion. “Which team gets paged when this rule is wrong, and is that the team that owns this file?” produces an answer.

CQRS as a boundary, not a framework

CQRS was the higher-leverage half, and the part I had to keep pulling people back from. The failure mode is treating it as an architecture to install — separate databases, event sourcing, a bus, a projection framework — on a service whose reads and writes are not actually in tension. Fowler is explicit that it should be applied to the portions of a system where the split pays, and using it everywhere is the main way teams get burned by it.

What we standardised was much smaller: a command mutates state and enforces invariants; a query returns a shape for a caller and enforces nothing. A command handler may not return a projection for the UI to render. A query may not contain a rule. That is it — no bus required.

The defect reduction came from the second half of that rule. Validation embedded in read paths was where a large share of the disagreements lived, because a rule enforced on the way out of the system is enforced only for callers who happen to use that path. Once queries were not allowed to hold rules, there was exactly one place a rule could be, and the class of bug where two components disagree had nowhere left to live.

Mentoring is mostly removing ambiguity

Mentoring 10+ engineers on this taught me that abstract principles do not transfer and worked examples do. The things that moved the needle:

  • A reference implementation in our own codebase. Not a blog example — one real service, ours, that we agreed was the shape to copy. “Follow SOLID” is unactionable. “This module is the pattern; make yours look like it” is a five-minute conversation.
  • Review comments phrased as the question, not the verdict. “This violates SRP” teaches nothing and invites an argument about definitions. “If the tax rule changes, does this file change? What about if we switch the store?” gets the author to the same place holding the reasoning.
  • Writing it down where the code is. A decision that lives in one review thread is not adopted; it is remembered by whoever was in the thread. The rules had to exist as text in the repository, or they regressed with every new joiner.
  • Naming what we deliberately were not doing. “No event sourcing here, the read and write models are the same shape” prevented more wasted effort than any positive guidance, because a team that has just learned a pattern will apply it everywhere otherwise.

Measuring leakage rather than defects

Defect leakage — the share of defects that reach production instead of being caught earlier — is a better target than raw defect count, and the difference matters. Total defect count falls if the team writes less code or tests less honestly. Leakage only falls if problems are being caught closer to where they were introduced.

It also tells you where to intervene, which raw counts do not. A leak caught in staging means the test suite has a gap; a leak that reaches production means a whole stage is missing. That maps onto the automated-testing balance the test pyramid argues for, and onto DORA's change failure rate, which is the same instinct measured at delivery level rather than per defect.

The 40% did not come from a new tool. It came from three things being true at once: there was one place for each rule to live, everyone agreed which place that was, and the queries that used to quietly hold a second copy no longer could.

The part that was not technical at all

Consistency is the whole mechanism, and consistency is a leadership problem. Four engineers each applying a reasonable but different interpretation of the same principle produces a codebase that is worse than four engineers applying one mediocre convention — because the second at least lets you predict where to look.

So most of the work was unglamorous: reviewing enough code to keep the convention visible, being willing to say “this is fine but it is not what we do here,” and being equally willing to change the convention in the open when someone showed it was wrong. The principles were never the hard part. Getting ten people to hold the same one, for long enough that the codebase reflected it, was.