// Spec before you generate · lesson 03

Invariants, or naming what must always be true

Of the six parts of a spec, the one that does the most work and gets skipped the most is the invariant. An invariant is a property that must hold no matter what, before, during, and after, regardless of how the model chooses to implement anything. It's the load-bearing wall. Everything else can flex; this cannot.

The reason invariants matter so much is that they catch bugs before the bug exists. A normal test checks a case after the code is written. An invariant declares a truth the code is not allowed to violate in the first place. "The total charged always equals the sum of the line items." "A user can only ever see their own records." "The balance never goes negative." State those as invariants up front and you've told the model the shape of the fence it has to build inside, before it lays a single brick.

How is an invariant different from a test?

A test asks, after the fact, "did this specific case work?" An invariant asks, always, "is this truth still holding?" Tests are samples. Invariants are laws. You can pass a thousand tests and still violate an invariant on the case you didn't sample, which is exactly the case that reaches production. When you hand the model invariants, you're not giving it more cases to pass. You're giving it the rules of the world it's building in, and those rules constrain every case, including the ones neither of you thought to write down.

Preconditions and postconditions

Two useful siblings. A precondition is what must be true before an action runs; a postcondition is what must be true after. "Before charging, the cart must be non-empty. After charging, the order status is exactly one of confirmed or failed, never both, never neither." Naming these turns fuzzy intentions into checkable gates, and checkable gates are the only kind that survive contact with a fast model.

The takeaway: invariants are the truths your code may never break, and declaring them up front is how you catch the bug before it's written instead of after it ships. Name the fence, then let the model build inside it.