Boring beats clever
If two implementations are equivalent, choose the one an agent understands immediately. Avoid dense expressions that save lines and spend reasoning.
These rules exist to shrink context per change. If two designs are equivalent for humans, pick the one that does not require global knowledge, hidden framework behaviour, or a chain of façades.
How many files, concepts, and dependencies does an AI agent need to understand before it can safely change this code?
If two implementations are equivalent, choose the one an agent understands immediately. Avoid dense expressions that save lines and spend reasoning.
Named arguments, visible dependencies, and obvious state transitions. Meaning should be in the code, not in a convention the agent has to rediscover.
Answer “what happens when this command runs?” from a small folder, not from fifty projects. Vertical slices exist for this.
Interfaces, factories, and decorators must justify themselves. External I/O and genuine multiple implementations qualify. Ceremony does not.
One data-access style, one error vocabulary, one way to add an operation. An agent should not have to discover your preferred approach every time.
Records and explicit transitions are cheaper to reason about than objects with fifteen setters and a secret status field.
Account identifiers and money are not strings and decimals. Invalid states should be difficult to represent.
Name them after behaviour. Prefer a real host over a wall of mocks. The suite should teach the system.
Spans, pools, and lock-free queues must buy a measured win. Complexity that does not show up in a profile is agent tax.
Keep functions small enough to answer one question, and large enough that the agent does not jump through seventeen one-liners.
Prefer
API → handler → database
over
API → mediator → handler → façade → manager → strategy → factory → repository → unit of work → database
Every hop is a file.
Middleware, filters, interceptors, and convention DI are fine at the edges — HTTP, logging, auth. Business rules should not disappear into them. If an agent has to reconstruct the decision from an action filter, a mediator pipeline, or a “register everything” convention, the rule is no longer local. Keep the construction visible and the outcome in the handler or operation file.
Return a closed set of outcomes. Do not use exceptions for expected business flow, and do not swallow them into null. The set itself lives on Playbooks.
An API named GetCustomer should not secretly load accounts, transactions, bonuses, and identity documents. Name the load you actually perform.
A type that owns create, cancel, refund, fees, email, audit, report, and CSV export is a god service — that is the problem. Splitting by every verb is the overcorrection. Split by responsibility.
Code already says what. Comments earn their tokens when they capture a constraint an agent would otherwise re-learn from production.
This is an engineering standard for .NET, not a slogan and not limited to one kind of app. Measure it on a change you actually ship, not on a toy refactor.