Software Architecture · Technical Debt

Reversibility as the Currency of Architectural Decisions

Decisions differ less in how good they are than in what it costs to undo them. How to classify one-way doors, what preserving an option costs, and how to test a rollback path.

Two decisions of equal quality can carry very different risk, because they differ in what happens if they are wrong. Renaming a module is undone in a commit. Publishing a field in a public response is undone across a support window measured in years. Writing an event in a particular shape is undone by keeping a transformation for every historical message forever. The cost of reversal, not the quality of the choice, determines how much deliberation the choice deserves — and how much inertia it creates.

This is the most useful way to price architectural debt, because it converts a vague sense of commitment into a measurable property of the decision. A debt whose repayment cost is bounded by a day is a different instrument from one whose repayment cost is bounded by the number of external consumers. Both may be rational; they belong in different decision processes.

What determines the cost of reversal

Three properties account for almost all of the variation.

Who else must change. If the only party affected is the code being edited, reversal is a local edit. If other teams’ services, published client libraries, or third-party integrations depend on the artifact, reversal requires those parties to move, and their schedules are outside the decider’s control.

Whether state has been written. A code shape is reversed by editing code. A data shape is reversed only by transforming the rows that were written under it, and the transformation may be lossy — the information needed to undo may have been destroyed at write time. The same applies to logs, backups, and anything else that was persisted and retained.

Whether the transformation is total or partial. A flag can be turned off, restoring the previous behaviour exactly, as long as both code paths still exist. A migration that dropped the old column cannot be partially reversed; it can only be redone in the other direction, which is a new migration with its own risk.

These combine into a ladder that is worth keeping in mind when a decision is framed as “easy to change later”. Internal code shape: reversible within a deployment. Module boundaries inside one deployable: reversible within a few changes. Cross-service interfaces with internal consumers: reversible within the consumers’ release cadence. Published APIs and SDKs: reversible across deprecation windows. Stored data: reversible only by migration, sometimes not at all. On-the-wire protocols and retained event logs: effectively permanent, since old messages must remain readable as long as they are retained.

The two-door framing and where it misleads

The language of one-way and two-way doors, popularised in Amazon’s 2015 shareholder letter, draws the distinction sharply: a two-way door can be walked back, so it should be made quickly and by the people closest to it; a one-way door cannot, so it deserves deliberation. The framing’s value is that it counteracts two opposite pathologies — analysis paralysis on cheap decisions and casual adoption of expensive ones.

Its weakness is that the classification is usually made by the person who wants the decision to be cheap. “We can always change it later” is offered as reassurance in almost every design discussion, and it is often wrong for reasons the speaker has not examined: the data is already written, the consumers already integrated, the flag already shipped without a removal date. A decision acquires irreversibility silently, through use rather than through design. The module that was internal last quarter is now imported by four services; the field that was added “temporarily” is now in a partner’s integration.

The discipline that follows is to classify by evidence rather than by assertion. Three questions, answered concretely: what exactly must change to reverse this, who controls each of those things, and how long does the slowest of them take? If the answer to the third is longer than the interval in which the decision is likely to be revisited, the decision is one-way in practice regardless of its nominal category.

Rehearsing the reversal

A rollback path that has never been executed is a hypothesis. Feature flags are the clearest example: a flag whose “off” branch has not been exercised since the flag was created is dead code wearing a safety label, and the moment it matters is the moment of highest stress. The remedy is mechanical — flip it in a pre-production environment on every release, or delete it — and it costs almost nothing.

The same applies to migrations. A schema change with a documented down-migration is reversible only if the down-migration has been run against a copy of production data, including rows written after the migration, because those rows may not have a representation in the old shape. The usual failure is that the down-migration exists, was written by the migration tool’s generator, and cannot actually run: it drops the new column and recreates the old one, losing every value written since. The rehearsal is what turns the claim into a fact.

-- A down-migration that runs is not the same as one that restores state.
ALTER TABLE orders DROP COLUMN total_minor;      -- the values are gone
ALTER TABLE orders ADD COLUMN total NUMERIC;     -- and cannot be recomputed

The honest classification of that migration is “irreversible”, and the correct response is to make the forward change in the expand-contract sequence described in Data Shape Debt and the Migration That Outlives the Code, where reversibility comes from keeping both shapes until the switch is proven.

What optionality costs

Real-options reasoning gives the economic frame: an option to do something later has value that rises with uncertainty and with the time available to decide, and it has a price. In software, the price is the indirection that keeps the choice open — an interface with one implementation, a layer that exists only to defer a decision, a format that stores more than the current code needs.

That price is real and is charged continuously, like interest. An abstraction with a single implementation must be read, tested, and modified whenever the implementation changes; a storage format that keeps raw input alongside derived state costs space and a consistency burden. The decision to preserve an option is therefore itself an investment with a return, and the return is only realized if the option is exercised. Options that are preserved and never exercised are pure cost, which is why “we might need to swap the database” is a weak justification for a repository abstraction and a strong one when a specific replacement is being considered.

The discriminating question is whether the uncertainty is genuine and bounded. If two technologies are genuinely under evaluation and the decision will be made within a quarter, the indirection is worth its price. If the uncertainty is “something might change someday”, the indirection is a speculative structure that will be maintained by people who never see the payoff — and it will accumulate its own debt, because abstractions with one implementation tend to acquire the wrong shape for the second.

Mechanisms that keep a door open cheaply

Some decisions can be made reversible at low cost, and knowing the catalogue prevents both over- and under-engineering.

Derive rather than store. A value computed from durable inputs can be recomputed under a new rule; a value stored under an old rule cannot. Keeping the raw input and a derived projection allows the derivation to change without migration. The cost is recomputation time and the need to keep the two consistent, which is the same two-truths problem in a different guise.

Append and interpret. Storing events and deriving current state, rather than overwriting state, keeps the interpretation revisable, at the cost of a read path that must replay and a log that must be retained. The retention policy is what determines whether the door stays open; a log with a seven-day retention is not an archive.

Narrow the published surface. Every field exposed is a field that must be supported. An internal identifier that is opaque and stable, rather than a database key that leaks the storage design, keeps a class of internal changes from becoming external ones — the mechanism behind the obligations in Compatibility Promises as Borrowed Time.

Put the decision behind a boundary owned by the decider. If a decision is inside a module whose interface is stable, changing the decision is a local change even though it is architecturally significant. The boundary is what makes the decision reversible, not the decision’s intrinsic nature.

Each of these has a cost, and the cost is paid every day while the option remains unexercised. The catalogue is for decisions that are genuinely uncertain, not for every decision.

The asymmetry that makes this a debt question

Uncertainty is not symmetric: the loss from an irreversible wrong decision is usually larger than the gain from an irreversible right one, because the wrong decision must be lived with. That asymmetry justifies paying a premium for reversibility when the stakes are high and the information is thin — the classic situation at the start of a project, when the domain is least understood and the commitments are cheapest to defer.

It also explains why technical debt accumulates in exactly the places where reversibility is expensive. The decision to store money as a float was made when the cost of getting it right seemed like a distraction; the decision to expose an internal model in a public response was made to ship a feature; the decision to delete the raw input after deriving the projection was made to save space. None of these were reckless at the time. Each closed a door, and the closure was not recorded as a decision — which is why the record described in Tacit Design Knowledge and the Cost of Unwritten Decisions should include, for every significant choice, the cost of undoing it.

That field changes the conversation more than any other. A design review that establishes the reversal cost early tends to produce a decision that is either cheaper to undo or deliberately committed to, and both outcomes are better than the default, which is a decision that nobody realizes is permanent until it is.

References

  1. 2015 Letter to Shareholders — Jeff Bezos, Amazon.com
  2. Real Options: Managing Strategic Investment in an Uncertain World — Amram and Kulatilaka, Harvard Business School Press, 1999
  3. Documenting Architecture Decisions — Michael Nygard, 2011
  4. Designing Data-Intensive Applications — Martin Kleppmann, O'Reilly, 2017