Staff engineering is, more than anything, a job of judgment, making good calls under uncertainty, often with incomplete information and no clear right answer. You can’t teach judgment as a formula, but you can learn the tools that make judgment better: a way to calibrate how carefully to decide, a way to make trade-offs visible so the team reasons together, and a way to record decisions so they’re not re-litigated forever. This lesson covers all three. The biggest single improvement most engineers can make is also the simplest: stop treating every decision with the same weight.
One-Way vs Two-Way Doors
Jeff Bezos popularised a lens that prevents a huge category of decision mistakes: some decisions are reversible and some are not, and you should treat them completely differently.
- One-way doors (irreversible): hard or impossible to undo. A public API contract (external clients now depend on it), a database schema with lots of data in it, a core framework the whole codebase is built on, a vendor you’ve locked into, a security or privacy choice. A wrong one here costs years. So go slow: gather data, write it up (Lesson 4), get many eyes, consider the alternatives seriously. Spend the time, because you can’t take it back.
- Two-way doors (reversible): easy to undo if they’re wrong. An internal tool choice, a feature behind a flag, a service’s internal design, a job’s retry policy, most refactors. Go fast: decide, try it, learn, change it if it’s wrong. Holding a big design review for a reversible decision is waste.
The mistakes go both ways, and both are common among smart engineers:
- Treating a two-way door as one-way: months of debate and three rounds of design review for a choice you could reverse in an afternoon. This is the more common failure in careful engineering cultures, it feels responsible but it’s slow, demoralising, and it trains the team that every decision is a big deal.
- Treating a one-way door as two-way: shipping a public API or a data model in a hurry to hit a deadline, then being stuck with its mistakes for years because external clients or millions of rows now depend on it.
The skill is to classify the door first, then match your speed and rigour to it. And here’s the liberating part: most decisions are two-way doors. The instinct to agonise over everything is usually wrong. Reserve your slow, careful, heavyweight process for the genuinely irreversible calls, and move fast on everything else. A team that does this ships far more and argues far less.
Make the Trade-off Visible
Many technical arguments go in circles because the people arguing are weighing different things without realising it. One person cares most about time-to-ship, another about long-term scalability, a third about operational burden, and they talk past each other, each sure the others are being unreasonable. A decision matrix fixes this by making the trade-off explicit and shared:
You list the criteria that actually matter down the side (time to ship, ops burden, throughput, cost, team familiarity, fit with the roadmap) and the options across the top, then fill in how each option scores. Suddenly the argument changes character. Instead of “Kafka is obviously right” versus “Postgres is obviously right”, the conversation becomes “we agree Kafka has the highest throughput ceiling, but do we actually need that in the next two years, and is it worth the ops burden?” You’re now arguing about the weights (which criteria matter most for this decision) rather than talking past each other, and that’s a productive argument.
Crucially, the matrix doesn’t decide for you. It doesn’t spit out a winner (resist the urge to add up fake numbers and pick the highest, that just hides the judgment inside arbitrary weights). What it does is make the trade-off legible, so the team’s judgment is applied to the real question: given what we actually care about, which option fits best? For a one-way door especially, a matrix in the design doc is how you show you weighed the alternatives seriously (Lesson 4’s “alternatives considered”).
Record the Why: Architecture Decision Records
Here’s a scenario you’ll live many times: eighteen months from now, someone (maybe you) looks at a decision and thinks “this is clearly wrong, let’s change it”, not knowing why it was made. Maybe it was wrong. Or maybe there was an excellent reason that’s no longer obvious, and reversing it will reintroduce a problem everyone forgot about. The fix is to record the why at the time, in an Architecture Decision Record (ADR):
An ADR is deliberately tiny, half a page, and captures:
- Title & status: “ADR-014: Use idempotency keys for all write APIs”, marked Proposed / Accepted / Superseded.
- Context: the forces at play, the problem, the constraints, and what we knew at the time. This is the part future-readers need most.
- Decision: what you chose, in a sentence or two.
- Consequences: what this makes easy, what it makes hard, what cost you’re accepting.
- Alternatives: what else you considered, briefly, and why not.
The key properties: ADRs are short, numbered, kept in the repo (docs/adr/), and never edited, when a decision changes, you write a new ADR that supersedes the old one, leaving the history intact. The old ADR still explains why the original choice made sense then, which is exactly what the person trying to reverse it needs to know.
The real value is the why, not the what. The code already shows what was decided; only the ADR captures the reasoning, the constraints, and the alternatives that were rejected, the context that makes a decision look sensible in hindsight instead of baffling.
A reusable ADR template
# ADR-NNN: [short title of the decision]
**Status:** Proposed | Accepted | Superseded by ADR-MMM
**Date:** ... · **Deciders:** names
## Context
The forces at play: the problem, the constraints, what we know right now,
and what makes this decision necessary. Enough that a reader in two years
understands the situation we were in.
## Decision
What we are doing, in one or two clear sentences.
## Consequences
- What this makes easier.
- What this makes harder (the cost we accept).
- Any follow-on work or risks it creates.
## Alternatives considered
- **X** — not chosen because ...
- **Y** — not chosen because ...Adopt an ADR habit in your team and you create an invaluable record: a trail of why the system is the way it is, which is the single hardest thing for new people (and future-you) to reconstruct.
Deciding Well Under Uncertainty
Beyond these tools, a few habits of good technical judgment that are harder to template but worth naming:
- Decide at the last responsible moment, not the last possible one. Gather information while you can, but don’t let a two-way door rot in indecision, deciding is itself progress.
- Separate the decision from the decider. Be clear who actually makes a given call (you? the team? a lead? by consensus?). Ambiguity about who decides stalls more projects than hard trade-offs do (Lesson 6).
- Make reversible bets to buy information. When you’re unsure, a cheap reversible experiment often beats more debate. Try it behind a flag, learn, decide.
- Disagree and commit once a call is made (Lesson 7): argue hard beforehand, then back the decision fully, even if it wasn’t yours.
- Beware your own biases: the technology you find exciting isn’t automatically the right one; “we’ve always done it this way” isn’t a reason; and sunk cost (we’ve already built half of it) shouldn’t drive a one-way door.
Judgment improves with reps and reflection. The tools in this lesson, classifying the door, the matrix, the ADR, are scaffolding that makes your reps count, and that lets your judgment be shared and checked rather than locked in your head.
Pitfalls to Avoid
- Treating all decisions with the same weight. Classify the door first; most are two-way, so move fast on them.
- Agonising over reversible decisions. A big review for a call you can undo in an afternoon is waste.
- Rushing irreversible ones. A public API or data model shipped in a hurry haunts you for years.
- Arguing past each other. Use a matrix to surface which criteria each person is really weighing.
- Letting a matrix “decide” via fake scores. It makes the trade-off legible; the judgment is still yours.
- Not recording the why. Future-you will want to reverse a decision without knowing the reason. Write an ADR.
- Leaving “who decides” ambiguous. Unclear ownership stalls more than hard trade-offs do.
Quick Recap
- Classify decisions as one-way (irreversible, go slow and careful) or two-way (reversible, go fast). Most are two-way.
- The biggest mistake is treating them the same, over-agonising reversible calls or rushing irreversible ones.
- Make trade-offs visible with a decision matrix so the team argues about the right things, not past each other.
- Record the why with short, numbered, never-edited ADRs in the repo; they capture the context future-you will need.
- Good judgment: decide at the last responsible moment, be clear who decides, make reversible bets to buy information.
Next lesson: taking a decision and turning it into reality across many teams, driving a cross-team project, where the hard part is people and dependencies, not code.