Decision-Making Frameworks
The previous chapters gave you the raw material — styles, boundaries, data models, design principles. This chapter is about the judgment that turns raw material into good decisions. These three frameworks do more work, day to day, than any specific architectural pattern, because they apply to every decision regardless of the technology involved.
Reversible vs irreversible decisions
Not all decisions deserve the same amount of deliberation, and treating them as if they do is one of the most common ways teams waste time and move too slowly on the things that matter.
The useful frame, popularized as one-way and two-way doors: a two-way door is a decision you can walk back through cheaply if it turns out wrong — try it, learn, reverse it if needed. A one-way door is expensive or impossible to undo. The mistake is spending one-way-door deliberation on two-way-door decisions, and vice versa.
Two refinements that make this sharper in practice:
You can often turn a one-way door into a two-way door. Before committing irreversibly, ask "how could I make this cheaper to reverse?" Run a new datastore in shadow mode alongside the old one. Roll out a risky change behind a feature flag to a small percentage. Build an anti-corruption layer so a third-party dependency can be swapped later. Converting irreversible decisions into reversible ones is one of the most valuable design moves you can make — it lets you move fast and safely.
Reversibility is about cost, not possibility. Almost anything is technically reversible given infinite time and money. The question is whether reversal is cheap enough that being wrong is fine. A data model migration is "possible" but if it means weeks of work and risky backfills across terabytes, treat it as a one-way door.
Conway's Law
"Any organization that designs a system will produce a design whose structure is a copy of the organization's communication structure." — Melvin Conway
This is one of the most important and least appreciated forces in software. It means your system architecture will tend to mirror your team and communication structure whether you intend it or not. Three teams will build a three-part system. Teams that don't talk will build components with awkward, under-designed interfaces between them — because the interface reflects a conversation that isn't happening.
The practical consequences for a lead are large:
Org design is architecture design. This is uniquely your concern, sitting where technical and people decisions meet. If you want a particular architecture, you often have to shape the team structure to match it — otherwise the organization will quietly drag the system back toward its own shape.
The "Inverse Conway Maneuver." Deliberately structure your teams to mirror the architecture you want. If you want loosely-coupled services with clean boundaries, organize into small, autonomous teams that own those boundaries. The org chart becomes a tool for shaping the system, not just an accident the system has to fight.
flowchart TB
subgraph org["Communication structure"]
direction LR
TA[Team A] <--> TB[Team B]
TB <--> TC[Team C]
TA -. rarely talk .-> TC
end
org ==> arch
subgraph arch["Resulting architecture"]
direction LR
CA[Component A] <-->|"clean interface"| CB[Component B]
CB <-->|"clean interface"| CC[Component C]
CA -. "awkward, thin,<br/>under-designed seam" .-> CC
end
The reason boundaries between teams that don't communicate come out badly isn't carelessness — it's that a good interface is negotiated, and you can't negotiate a conversation you never have.
Matching complexity to the problem
The final framework is a posture more than a procedure, and it's the antidote to most architectural regret. Every design sits somewhere on a spectrum from "too simple for the problem" to "more complex than the problem warrants," and both ends hurt.
Over-engineering is building for requirements you don't have: designing for ten million users at ten thousand, splitting into microservices before the team is big enough to feel the pain, adding configuration for variation no one has asked for. It feels responsible ("we're planning ahead!") but it spends your scarcest resource — the team's capacity to understand and change the system — on hypothetical futures, often guessed wrong. Complexity you add today is paid for by everyone who touches the system every day after.
Under-engineering is the opposite: ignoring foundations you can clearly see coming. No tests on critical logic, no observability, a data model that already can't represent obvious near-term needs, a "we'll deal with scale when we get there" stance toward growth that's plainly arriving next quarter.
The skill is honest assessment of your actual trajectory — not your fantasy of it, and not a refusal to look ahead at all. Where will load realistically be in a year? Which parts will genuinely need to scale, and which won't? Which decisions are one-way doors that you must get right now versus two-way doors you can defer? Design so you can evolve toward the future you actually expect, without building all of it today.
A reliable tie-breaker: prefer the simplest thing that could possibly work, and keep it changeable. Simple systems that are easy to change beat clever systems that anticipated the wrong future — because the one certainty is that requirements will surprise you, and changeable beats prescient.
What this means for you as a lead
These three frameworks turn into three habitual questions you can bring to almost any decision:
- Is this a one-way or two-way door — and can I make it a two-way door? (Calibrate how much to deliberate; reduce risk by increasing reversibility.)
- What team structure does this architecture imply, and do we have it? (Conway's Law will win if you ignore it.)
- Am I matching complexity to our real trajectory, or to a fantasy of scale (or a refusal to look ahead)? (Guard both ends.)
Asked consistently, these do more to prevent expensive mistakes than any amount of pattern knowledge. They're also exactly the questions a team can't easily ask itself in the moment — which is why holding them is so much of what a technical lead is for.