Design for Reversibility
The most expensive architecture decision in most systems is not the framework. It is the dependency nobody can remove.
The most expensive architecture decision in most systems is not the framework. It is the dependency nobody can remove.
Frameworks can be replaced. A shared database that four teams write to directly is harder. So is a synchronous call in the checkout path that nobody owns, a vendor SDK embedded in every service, or a message contract with no versioning.
The test
The test is simple: can you replace this component without coordinating a release across three teams?
If the answer is no, you have an architectural constraint, not a design preference. Constraints are fine. They should be chosen on purpose and written down, not discovered during an incident.
Why reversibility beats elegance
Architecture decisions are made with incomplete information. The team that picks a messaging platform today does not know the volume in three years, the team that picks a data model does not know which workflow will become the business, and the team that picks a vendor does not know the renewal price.
You cannot avoid being wrong. You can keep the cost of being wrong low. That is what reversibility buys: the ability to change your mind without stopping the business.
What reversibility looks like
- Put a contract between components, not a shared table.
- Version messages and APIs from day one.
- Keep irreversible actions behind a small, well-tested boundary.
- Use feature flags to separate deployment from release.
- Make failure visible with timeouts, retries, and circuit breakers.
None of these are exotic. They are the difference between a decision you can revisit and a decision that becomes permanent by accident.
The coupling you cannot see
The expensive couplings are rarely in the architecture diagram. They are in the things nobody thinks to draw: a scheduled job that reads another system's tables, a shared cache key convention, an authentication cookie that three applications depend on, a log format that an alert parses.
Each one is small. Together they make a system that cannot be moved in pieces, only replaced in one very long project.
A cheap exercise
Pick the component you would most like to replace. Write down everything that would have to change on the day you replace it. If that list is longer than the component itself, the constraint is doing more work than the code.
Reversibility keeps the cost of being wrong low. That is the point.
Which dependency in your system would be hardest to remove?
This was published first as Dispatches from the Workshop.
Subscribe on LinkedIn