Skip to main content

Reversibility: One-Way vs Two-Way Doors

What This Concept Is​

Some decisions are easy to undo. Some are expensive, slow, or impossible. The Amazon-style framing: decisions are either

  • Two-way doors -- if wrong, you notice and walk back. Internal naming, class layout, which file hosts a helper, how a module is split.
  • One-way doors -- reversing requires heavy cost: migrations, rewrites, external communication. Database choice, public API shape, authentication model, language / framework, cross-team integration contracts, persisted data format.

The design-review question is: how reversible is this decision? The answer should drive how much time, how many reviewers, and how much documentation the decision deserves.

Why It Matters Here​

Most engineers spend the same energy on every decision, which over-invests in two-way doors and under-invests in one-way doors. Over-investing in two-way doors is not free -- it slows shipping, it centers opinions that will not matter in a week, and it teaches teams that every PR needs a meeting.

Under-investing in one-way doors is worse. An under-reviewed database migration, a poorly thought-through API shape, or a hurried auth choice can shape the codebase's work for years.

Concrete Example​

Decision A: What do I name the internal helper that converts line items to tax?

  • Two-way door. You can rename anytime. Ship, move on.

Decision B: Should our public API return ISO 8601 strings or Unix timestamps?

  • One-way door (mostly). Once customers integrate, reversing is a version bump and a deprecation window. Worth 30 minutes of deliberate thought and an ADR. Not worth two weeks.

Decision C: Should we run this service on Postgres or DynamoDB?

  • Firmly one-way door. Data migration plus operational retraining plus a rewrite of querying code. Worth a design doc, an ADR, and at least two senior reviewers.

Decision D: Should I use an enum or a string constant for the order status?

  • Two-way door. A targeted refactor covers it.

Decision E: Put retry logic in the client library or in the service?

  • One-way-ish. Reversing means cascading changes in every client. Deserves an ADR.

Common Confusion / Misconception​

"If in doubt, treat as one-way." Sounds safe, but the cost is every decision becoming heavyweight. Teams that do this ship slowly and decide badly on the few real one-way doors, because the review muscle is exhausted.

"If in doubt, treat as two-way." Also wrong, because the failures from a missed one-way door are exactly the catastrophic kind.

"Reversibility = code difficulty." Reversibility is about total cost to change, not code lines. A database choice is one config line but enormous to reverse. A 400-line refactor of a private module is reversible by one PR.

"Old decisions become two-way doors over time." The opposite is true. The more callers, integrations, or persisted data depend on a decision, the more one-way it becomes. Revisit sooner, not later, when you suspect a decision was wrong.

"Reversibility replaces judgment." It does not. It reframes it. You still have to decide. You are calibrating how much effort the decision deserves.

How To Use It​

Before starting on a design:

  1. List the decisions inside it.
  2. Label each as two-way, one-way, or one-way-ish.
  3. Route effort accordingly:
    • Two-way: ship, iterate.
    • One-way-ish: write a short section in the design doc or a lightweight ADR.
    • One-way: full ADR, two senior reviewers, explicit alternatives considered.

In review, when you push back on a decision, say which category it is in:

This is a one-way door (public API shape). Worth a longer discussion before merge.

or

This is a two-way door (internal naming). I have a suggestion below but do not treat it as blocking.

That phrase alone saves hours of thread time.

Check Yourself​

  1. Give a two-way door inside your current codebase and a one-way door.
  2. Why is labeling every decision as one-way a failure, even though it sounds cautious?
  3. Why do one-way doors get more one-way, not less, as a system ages?

Mini Drill or Application​

For each decision, label it and justify. For one-way doors, list two things you would want in an ADR. For two-way doors, note how you would reverse if wrong.

  1. Choice of primary key format (UUID vs auto-increment int) in a customer-facing table.
  2. Function naming convention in a private utility file.
  3. Whether to expose a webhook to customers.
  4. Whether to use a CSS framework internally (React + Tailwind vs React + CSS modules).
  5. Decision to send emails synchronously from a request handler instead of via a queue.

Read This Only If Stuck​