Two choices ship in the same sprint - the field names in a public webhook payload and the queue library used inside one service. Eighteen months later, renaming the fields is a nine-month deprecation programme and swapping the queue library is a two-day job. Neither was flagged as irreversible at the time. What actually made one of them hard to undo?
Show the full answer Hide the answer
What actually makes a decision hard to undo
Not the size of the component, and not the price of the technology. A decision becomes irreversible when other parties have copied its shape into places you do not control. The queue library sits behind one interface in one deployable, so there is exactly one caller and one person who has to agree. The field names were copied into the source code of every integrator who ever parsed that payload.
This is why cost-of-technology instincts misclassify so reliably. A team will spend three weeks choosing a database, because databases feel momentous, and ten minutes naming the fields in a public response. The database sits behind a repository interface and one team's code. The field names are now in 400 other codebases, and you no longer control the schedule on which they change.
The three things that close the window
- Persisted data in the shape of the choice. Once 40 million rows in production carry the structure, undoing it means a backfill, a dual-read period and a verification pass. Closes slowly, in proportion to data volume and retention.
- Published contracts. An API response, an event payload, a webhook body, a file format, a URL. Closes the instant the first external consumer integrates, because from then on the change runs at their release cadence plus whatever deprecation notice you promised. A 365-day notice period means a year of dual support before you write any removal code, and breaking it early means a customer's integration fails in their production, not yours.
- Operational muscle memory. Runbooks, dashboards, alert thresholds, the on-call engineer's intuition about what normal looks like. Closes gradually and invisibly, and it is the one nobody budgets for.
The first two are the reason a reversal takes calendar time. The third is the reason it takes longer than the plan said.
How to keep the window open
Add an indirection where the carriers are, not where the cost is. A published contract stays reversible if the external name is decoupled from the internal one by a mapping layer you own, so an internal rename is a mapping change. Stored data stays reversible if the shape is versioned from the first row, because a backfill you planned for is a job and a backfill you did not is a project. Spend the analysis in proportion to the number of parties who will copy the shape, not the invoice value of the component.
When not to bother
If the system has no external consumers, no data older than a retention window of 30 days and one team on call, almost every decision is a two-way door and the deliberation costs more than being wrong. Pick, ship, and keep the migration muscle warm. The classification only earns its keep once there is a second party - another team, a customer, a regulator - whose calendar you would have to negotiate with.
Common weak answers
- "The field names were a public API, and public APIs are always irreversible." They are irreversible because consumers exist, which is a count you can check. A public endpoint with zero integrators is still a two-way door, and internal endpoints with forty internal consumers are not.
- "We should have written an ADR for the field names." Recording the decision would not have changed its reversibility. Putting a name-mapping layer in front of it would have.