Engineering rules

Write C# an agent can change without a guided tour

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.

Overarching metric

How many files, concepts, and dependencies does an AI agent need to understand before it can safely change this code?

Ten core rules

01

Boring beats clever

If two implementations are equivalent, choose the one an agent understands immediately. Avoid dense expressions that save lines and spend reasoning.

02

Explicit beats implicit

Named arguments, visible dependencies, and obvious state transitions. Meaning should be in the code, not in a convention the agent has to rediscover.

03

Local reasoning beats global knowledge

Answer “what happens when this command runs?” from a small folder, not from fifty projects. Vertical slices exist for this.

04

Few abstractions beat abstraction everywhere

Interfaces, factories, and decorators must justify themselves. External I/O and genuine multiple implementations qualify. Ceremony does not.

05

Consistent patterns beat flexibility

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.

06

Immutable state beats hidden mutation

Records and explicit transitions are cheaper to reason about than objects with fifteen setters and a secret status field.

07

Strong types beat primitive obsession

Account identifiers and money are not strings and decimals. Invalid states should be difficult to represent.

08

Tests are executable documentation

Name them after behaviour. Prefer a real host over a wall of mocks. The suite should teach the system.

09

Measured optimisation beats premature optimisation

Spans, pools, and lock-free queues must buy a measured win. Complexity that does not show up in a profile is agent tax.

10

Minimise context per change

Keep functions small enough to answer one question, and large enough that the agent does not jump through seventeen one-liners.

Supporting rules that show up in real C#

Shallow graphs

Prefer

API → handler → database

over

API → mediator → handler → façade → manager → strategy → factory → repository → unit of work → database

Every hop is a file.

No magic in the use case

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.

Explicit errors

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.

Obvious cost

An API named GetCustomer should not secretly load accounts, transactions, bonuses, and identity documents. Name the load you actually perform.

Cohesion without a god service

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.

Comments explain why

Code already says what. Comments earn their tokens when they capture a constraint an agent would otherwise re-learn from production.

Index

An AI Maintainability Index for .NET

  • Dependency depth
  • Number of abstractions
  • Files touched per feature
  • Cyclomatic complexity of the operation, not the solution
  • Test coverage of the real path
  • Token and context requirements per change

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.