A4.5 · Invariants · dive 3
Always true
An invariant says “always”. A check says “for this case”. No finite set of passing checks proves a rule holds for every value — there is always a next value.
An invariant is universally quantified: it claims a property of every value, including values no check has tried. A finite check set samples it. Step a stream of request values on the right and watch the master invariant hold for every one, while a single check holds for only its scenario.
Hold the rule over a value stream · step it
For all, not for one
An invariant is universally quantified. It claims a property of every value the increment
can take — including values no test has ever run. “The web layer calls only the
Portal facade” is true for the next request, and the one after that, and every
request the Portal will ever serve, including users that do not exist yet.
A check is one point. Each Given/When/Then names a single scenario and proves it behaves. Passing checks raise confidence, and that confidence matters — but no count of them closes the universal claim, because the claim is about all values and a finite set names only some. That is why a spec carries invariants as their own named rules, held over every value as the increment runs, separately from the checks that sample them.
- for all
- An invariant quantifies over every value the increment can take, at all times — including values no check has tried.
- a sample
- Each check is one point on the rule. A finite set of passing checks raises confidence; it does not close the universal claim.
- held, not asserted
- An invariant is a rule held against every value as the increment runs — named once, exercised continuously — not a fact asserted from one green scenario.
This is the formal heart of A4.5: an invariant is the “for all” claim, and a check is one “for this” sample of it — the two never collapse into one.
The gap a check set leaves
Add passing checks one at a time. Each covers one more scenario, so the covered-scenario count rises. The master invariant, though, stays unproven by them: a finite set of checks samples the rule, it does not close it. There is always a next request — a value no check named — and the invariant must hold for that one too.
Drag the slider to add checks. The readout reports how many scenarios are covered, and reports that the universal claim is still not closed. The point is not that checks are weak — it is that an invariant is a different, stronger claim, and the spec names it as one so the rule is held over every value, not only the values that happen to be checked.
A check set never closes “for all” · add checks
A finite set of passing checks samples a universal rule but never closes it — the next value is always one no check named.
On the Portal: the rule over every request
On the Portal — the learning platform this course builds — the master invariant holds for every
request the platform ever serves. F6.1-US5's check — “an unknown user id renders the empty state, a
clean 200” — tests one value. The facade is total at F6.1 · Bootstrap the Phoenix PortalThe first web rung: stand the F5 engine up as a Phoenix app, with one facade-backed route and a liveness route.View the spec ladder →, so the empty
state holds; but that one passing check is a sample of the universal rule, not a proof of it for all ids.
The closed error set is universal in the same way. Every error value the web layer renders is a
%Portal.Error{} whose code is one of the four known kinds,
extended only by an explicit no-catch-all mapping. A new error kind without that mapping would break the
invariant — whatever the scenario, and whether or not any check happened to cover it.
# f6.1.md — Invariants (the rule over ALL values). # F6.1-INV1 (master): the web layer calls only the Portal facade … → ∀ request # f6.1.stories.md — one check that samples it. # F6.1-US5: unknown user id → renders the empty state, a clean 200. → ∃ this case # Coverage maps it to deliverables D4, D7 — one sample of the rule, not the rule.
The principle
An invariant is a universal claim; a finite set of passing checks samples it but never closes it — so the spec names the invariant as its own rule, held over every value.
On the Portal
The master invariant holds for every request; F6.1-US5's “unknown id → 200” check tests one value — it exercises the rule, it does not prove it for all ids.
An invariant is held over every value as the increment runs; a check is one sample — which is why “always” is a named rule, not an inference from green scenarios.
Recap — the universal rule, sampled
An invariant says “for all”: it claims a property of every value the increment can take, including values no test has run. A finite set of passing checks samples it — the covered-scenario count rises, but the universal claim is never closed, because the next value is always one no check named. So a spec names invariants as their own rules, held over every value, distinct from the checks that exercise them. That is the discipline A4.6 turns into the closure that makes “done” correct by definition.
After A4.5, the next module — A4.6 · Traceability — correct by definition — walks the chain from deliverable to story to check and invariant, closing “done” by a rule. Return to the A4.5 module navigation to continue.
References
Primary sources for the universal rule held continuously over every value, and where it connects in the course.
Sources
- Humble & Farley — Continuous Delivery — an invariant held continuously over every change, not asserted from a passing run.
- Adzic, G. — Specification by Example — examples sample a rule; the rule is named once and stays the source of truth.
- Gherkin reference — Given/When/Then as one named scenario, a sample of the rule it exercises.
Related in this course
- A4.5 · Invariants — the module hub.
- A4.5.2 · The master invariant — the previous dive.
- A4 · The spec layer — the chapter.
- A1.05 · Correct by definition — the closure this rule feeds into.
- Companion · Phoenix (F6) — the chapter whose master invariant holds for every request.