Invariants

The things that must never stop being true, and the engineering question that follows: which layer is actually responsible for protecting each one.

Invariants
▶ lab

A balance that cannot go negative, a username that is unique, an order that cannot ship before payment, a tenant that cannot see another tenant. These are not features — they are the properties everything else is built on top of.

Q · What is the difference between a rule my code checks and a property my system guarantees?
Where Invariants Live
▶ lab

Every invariant is enforced somewhere specific — a type, domain logic, a transaction, a database constraint, an API contract — and the design question is which, because each covers a different set of paths at a different price.

Q · I have written the rule down. Which layer is actually responsible for making sure it holds?
Enforcing Invariants

Types, runtime guards, database constraints and tests are four different mechanisms with four different coverages. Using all four is defence in depth, and it means four places to keep in sync — which is a trade, not a free win.

Q · Should I enforce this rule in one place or in every place I can, and what does the second option actually cost?
Invariant Leaks
▶ lab

The rule is enforced in the service. Then a background job, an admin tool or a migration writes straight to the table. This is the characteristic failure of the whole module, and it is an ownership failure before it is a technical one.

Q · The rule is correctly implemented and the data is still wrong. Where did the guarantee go?
Consistency Boundaries
▶ lab

Which set of things must change together, atomically, for an invariant to hold. Answer that and you have chosen your aggregates, your transactions and — later — where a service could ever be split.

Q · Which pieces of state have to move as one, and what happens to the ones I decide do not?