Lesson 0037 · Master Designer · Module 3
Correctness vs Delivery Speed
Right or now — the oldest trade. The master's resolution: classify the change as reversible or irreversible. Reversible changes may go fast; irreversible ones can't afford to be wrong.
Mission tie-in: the third Evolution Trade-Off — where Phase I's fail-fast and Phase II's tests meet the real pressure of shipping.
Knowledge: reversible or irreversible?
Every change has a failure cost: what happens when it's wrong? Classify:
- Reversible — wrong means a rollback, a revert, a fix-forward. The blast radius is a deploy cycle. These can ship fast: tests cover the essentials, the team watches the metrics, and the correction loop is cheap.
- Irreversible — wrong means money lost, records corrupted, legal exposure, data destroyed, trust burned. These ship with the full apparatus: invariants (lesson 0011), fail-fast (lesson 0014), tests around the domain rules, and a slow, staged rollout (lesson 0035).
The two forces are confidence before release — verification you pay for up front — and learning from release — information only production can give you, because only production has real users, real data and real load. The unit of decision is the change, classified by reversibility × blast radius.
The mechanism is that both forces buy the same thing — information — and they differ only in when you buy it and what it costs. Where being wrong costs a revert, the cheapest information is usually bought afterwards: ship, watch, correct. Where being wrong moves money, destroys records or sends 40,000 emails, the "afterwards" option does not exist at any price, so the whole information budget must be spent before the release. That is the real content of the trade-off, and it explains why the answer changes per change rather than per team.
The boundary condition is the one most teams miss: reversibility is a function of detection time, not of your deployment tooling. A change you could revert in thirty seconds but will not notice for nine days is irreversible in every way that matters, because nine days of consequences are already in the world. Two corollaries follow. A reversible change with no telemetry is an irreversible change wearing a disguise — you will hear about it from customers, late. And a stream of individually reversible changes can aggregate into an irreversible state: three weeks of rows written in a subtly wrong shape is a migration project, not a revert.
The trick that collapses the trade: tests are the speed technology. A suite that locks the domain rules (characterization tests from lesson 0005, TDD for new rules) makes fast shipping safe — the team's speed comes from the safety net, not from skipping it.
# reversible: a display change — ship fast, watch the metric
def render_header(title: str) -> str: # tests: the essentials
...
# irreversible: a charge — ship with the apparatus
class ChargeService:
def charge(self, amount: Money, token: str) -> str:
charge_id = self._gateway.charge(amount, token) # idempotency key
self._ledger.record(charge_id, amount) # invariants, fail-fast
return charge_id
The discipline is naming the class out loud: before cutting the corner, say "this is reversible — a rollback fixes it" or "this is irreversible — it needs the apparatus." The silent version is where the trade goes wrong: irreversible changes shipped at reversible speed.
Worked: the same bug, priced by how long it hides
An automated returns-eligibility rule is being changed. If it is wrong in the permissive direction it will approve items that fall outside the policy window. Volume is 3,000 returns a day; suppose a bad rule wrongly approves 6% of them — 180 a day — at an average refund of €62.
The first thing a lead asks is not "how many tests do we have?" but "if this is wrong, how long until we know?" That single number sets the price of the mistake. Caught within an hour: 125 returns pass through, 7.5 of them wrongly approved, about €465. Caught after nine days: €11,160 a day, roughly €100,000, plus a reconciliation project and a conversation with finance about a guardrail the initiative promised to hold. Same bug, same code, same tests — the entire difference is detection time.
So the apparatus this change earns is not simply "more tests". It is a daily reconciliation that recomputes approvals from the policy independently and reports disagreements, which caps detection at 24 hours by construction. A lead will often take that over three extra unit tests, because the tests only encode what somebody already thought of, whereas the reconciliation catches the thing nobody thought of at all.
The alternative the lead rejects is the traditional one: a manual QA pass before every release. It lengthens the feedback loop for every change, including the 90% that are reversible, and it cannot see what production data actually looks like — the case that breaks the rule is a 2019 order with a null shipping date, and no one writes that test by hand.
The more interesting rejection is "ship it, it's behind a flag". A flag at 1% does not make the money reversible; it makes the loss bounded — about €111 a day instead of €11,160. That is not nothing: it is a known price for information, and choosing to pay it deliberately is a legitimate senior move. Pretending it makes the change reversible is not.
Over a year, the teams that classify out loud diverge from the ones that do not in a specific way. Both ship at similar speed most weeks; the difference is that one of them has never had an incident it could not undo, because the two or three genuinely irreversible changes each quarter were recognised in advance and got the apparatus. Wisdom here is not caution — it is knowing which three.
† AI accelerates both sides: it ships fast code faster, and fast wrong code faster too. The classification is exactly the judgment AI can't provide — state the class in the prompt (lesson 0010) and let the generated tests match it.| The change | Class | What the class earns it |
|---|---|---|
| a colour, a label, a copy tweak | reversible | Ship it; watch the metric for an hour. |
| an outbound email to every user | irreversible — you cannot unsend | A dry run, one small cohort, a kill switch. |
| a migration that drops a column | irreversible | A backup, expand/contract (lesson 0035), a rehearsed rollback. |
| a pricing rule change | irreversible — the money already moved | Tests on the rule, staged rollout, a reconciliation report. |
| an endpoint nobody calls yet | reversible | Ship it; the users are the flag, not the code. |
- "Reversible means we can revert the deploy."
- Reverting the code does not unsend the emails, unmove the money, or rewrite the rows other systems have already read. Reversibility is a property of the change plus its side effects plus how fast you would notice — the deploy button is only one of the three.
- "More tests mean more correctness."
- Tests encode the failures somebody imagined. For irreversible changes the marginal value usually sits elsewhere: in a reconciliation that computes the same answer a second way and shouts when the two disagree, which catches the failure nobody imagined.
- "Quality and speed are a permanent trade-off."
- Not at team level. The practices that shorten the feedback loop — small batches, automated checks, observable releases — improve both, and the measured research says so. The trade is real per change, not per culture, and teams that treat it as cultural end up slow and wrong.
- "Classifying the change is a formality."
- Unsaid, everything is implicitly reversible, because that is the assumption that lets work continue. Saying "this one is irreversible" out loud is the entire mechanism; it is also the sentence that makes a deadline conversation possible before the incident rather than after it.
When this is the wrong move
The classification is overhead for a team whose real problem is elsewhere. If you release once a quarter, every release is a giant irreversible batch and the useful work is shrinking it (lesson 0035), not labelling it. If you are pre-launch with no users, nothing is irreversible because nobody depends on anything yet — the apparatus is pure cost, and speed of learning is the only force in the room. And in a regulated or safety-critical domain the class is fixed in advance by policy: everything is irreversible, so the discipline that pays is the evidence trail, not the sorting.
There is also a failure mode on the cautious side. A team that classifies everything as irreversible has not become careful; it has stopped deciding, and it will spend its apparatus budget on colour changes and have none left for the migration that actually deserved it.
In the lab: the companion Phase 3 exercise asks you to classify each pending change in a returns service — a copy tweak, an eligibility rule, a refund calculation, a schema drop — and to add, for one irreversible change, the detection mechanism that bounds how long it can be wrong.
Skill: which class is this change?
What makes a technically revertible change effectively irreversible?
A charging flow whose error corrupts the ledger is:
Tests make fast shipping safe because they:
Practice on your own code
Take your last three shipped changes. Classify each: reversible or irreversible? For any that shipped fast without the apparatus while being irreversible — name the loan you took (and the next lesson's ledger will make it honest).
Reveal: a classification audit
Three changes: a navbar tweak (reversible — shipped fast, correct call); a new discount rule (irreversible — shipped with tests, correct call); a migration that dropped a column (irreversible — shipped without a backup rollback path; the loan: recovery took a day of manual repair). The audit names the loan; lesson 0038 tracks it.
- In two sentences, explain to a junior why "we can roll it back" is not the same claim as "it is reversible".
- Find one change shipping this week in your own work. Write its detection time — how many hours until you would know it was wrong — and multiply the hourly damage by it.
- Predict the one-year state if nothing is ever classified: which single category of change will cause the incident that gets a postmortem, and who will first notice it?
Your win
You can classify any change by its failure cost, apply the apparatus only where the class demands it, and use tests as the speed technology instead of the speed tax.
Read and watch deeper
- Test-Driven Development by Example, Beck — the discipline that makes speed safe: tests as the design and delivery instrument.
- Accelerate, Forsgren, Humble & Kim — ch. 2: the evidence that throughput and stability rise together, and the four measures that expose it, including time to restore.
- Working Effectively with Legacy Code, Feathers — ch. 1–2: characterization tests as the bridge into changes you can't fully verify.
- A Philosophy of Software Design, Ousterhout — ch. 10 "Define Errors Out of Existence": design the irreversible class so it can't go wrong.
- Watch: ArjanCodes YouTube — search "testing" or "TDD".
- Next: lesson 0038 — the final trade-off.
- Reference: Trade-offs — correctness vs delivery speed; Patterns — characterization test.
Classify your pending changes with your agent-teacher before you start — the out-loud version is the discipline.