Reference document · Master Designer in one page
Trade-Offs Cheat Sheet
At Master Designer level there are no perfect solutions — only forces. For each trade-off: the two forces, the questions that decide it, the default posture when evidence is thin, and — the entry to read in hindsight — the signal that you chose wrong.
How to run a trade-off
Three moves turn an argument into a decision, and they are the same three every time.
- Write both columns. Not the one you favour — both, with their hidden costs. Most trade-off arguments are two people each holding one column.
- Name the unit. Almost none of these are decided per system. Consistency is decided per operation, sync/async per workflow, build/buy per component. A global answer is usually a refusal to decide.
- Name the reversibility. If the choice can be undone in a deploy, pick fast and learn. If it cannot, spend the hour on designing it twice.
| Trade-off | Decided per… | Default when evidence is thin | Lesson |
|---|---|---|---|
| Build vs Buy | component | buy, until the differentiator argument is made | 0027 |
| Abstraction vs Duplication | occurrence | duplicate, until three copies change together | 0028 |
| Configuration vs Convention | setting | convention, until variation is measured | 0029 |
| Generality vs Specificity | module | specific, until the second real case | 0030 |
| Synchronous vs Asynchronous | workflow | sync, until a concrete reason demands async | 0031 |
| Consistency vs Availability | operation | availability plus an idempotent write path | 0032 |
| Coupling vs Coordination | boundary | independence, with the contract written down | 0033 |
| Performance vs Maintainability | hot path | clean, until a number says otherwise | 0034 |
| Stability vs Speed of Change | change | small reversible steps — buy both | 0035 |
| Isolation vs Integration | concept | isolate where the meanings differ | 0036 |
| Correctness vs Delivery Speed | change | speed if reversible, apparatus if not | 0037 |
| Short-Term Productivity vs Long-Term Maintainability | loan | borrow if you record it | 0038 |
Complexity trade-offs
Build vs Buy
Forces: control and fit (build) vs time and maintenance burden (buy).
Deciding questions: Is this your business's core differentiator? Will a library do 80% now and 80% in three years? Who owns the upgrade path — you or the vendor? Who is paged at 3 a.m. either way?
Default: buy (or take the open-source package) until the differentiator argument is made explicitly. Choose boring technology.
Signal you chose wrong: you built it, and the team's best engineers spend their quarters on a problem no customer has ever mentioned. Or: you bought it, and half the codebase is workarounds for the 20% that never fitted.
Abstraction vs Duplication
Forces: one source of truth (abstraction) vs freedom to diverge (duplication).
Deciding questions: Will these two things change for the same reason? Is the shape proven by three occurrences, or guessed from one? How often have the copies actually changed together?
Default: duplication with intent beats a wrong abstraction. Wrong abstractions are the expensive kind — Sandi Metz.
Signal you chose wrong: the shared function has grown a kind parameter and a branch per caller. Every new case is a negotiation with the shape, and no caller can change alone.
Configuration vs Convention
Forces: explicit flexibility (configuration) vs predictable defaults (convention).
Deciding questions: How often does this actually vary per deployment or per tenant? Who is the operator — engineers or non-technical users? Is the convention documented and discoverable?
Default: convention for the common case; configuration only where variation is measured.
Signal you chose wrong: a settings file of forty knobs, of which four have ever been changed — and an incident caused by the thirty-sixth being set to something nobody tested.
Taught in: lesson 0029
Generality vs Specificity
Forces: reusable and future-proof (general) vs simple and fit-for-purpose (specific).
Deciding questions: What is the actual evidence of a second use case? Does the general version make today's case simpler, or only tomorrow's cheaper? What does the generalisation cost in parameters, indirection and tests?
Default: specific until the second use case is real — then generalise, guided by the rule of three.
Signal you chose wrong: the module has parameters no caller passes and hooks nothing hooks. Reading the one real use case means reading past machinery built for cases that never arrived.
Taught in: lesson 0030
System trade-offs
Synchronous vs Asynchronous
Forces: immediate, simple, debuggable (sync) vs decoupled, resilient, scalable (async).
Deciding questions: Must the caller have the result to continue? Can you answer what happens when a handler dies mid-way, when events arrive out of order, when one is delivered twice?
Default: sync unless a concrete reason — latency, load, decoupling — demands async. Async everywhere turns every feature into a distributed-systems problem.
Signal you chose wrong: an incident whose timeline nobody can reconstruct, because the workflow lives in six handlers and no single trace covers it. Or: a queue whose dead-letter count nobody has looked at in a month.
Consistency vs Availability
Forces: every read sees the last write (consistency) vs every request gets an answer (availability).
Deciding questions: What does a stale read cost here — a retry, an apology, or a financial error? What does a timeout cost? Which of the two failures does the business tolerate better?
Default: don't choose globally — choose per operation. CAP is a framing, not a product picker.
Signal you chose wrong: a customer was shown “in stock” and charged for something that wasn't; or the checkout went down because a read that could have been served from cache waited for a quorum.
Taught in: lesson 0032
Coupling vs Coordination
Forces: independent components (low coupling) vs the standing cost of keeping them aligned (coordination).
Deciding questions: When the contract changes, who must change together? Can one side deploy alone? Does Conway's law already put the boundary at a team edge?
Default: favour independence, and pay the coordination tax explicitly — in a written contract, not a chat thread — where the domain genuinely needs shared consistency.
Signal you chose wrong: a release runbook with the words “deploy A before B”. That sentence means you are paying both currencies at once: coupled systems and a coordination ritual.
Taught in: lesson 0033
Performance vs Maintainability
Forces: fast (performance) vs clear (maintainability).
Deciding questions: Is there a measured requirement, or a guess? Where is the bottleneck actually — profile first. Can the fast version hide behind a clean interface?
Default: clean until measured; then optimise the few hot paths and keep them behind a deep module.
Signal you chose wrong: an optimisation nobody can justify with a number, and a profile showing the real bottleneck somewhere else entirely — the cleverness bought complexity and no speed.
Taught in: lesson 0034
Evolution trade-offs
Stability vs Speed of Change
Forces: predictable, safe, hard to break (stable) vs quick to ship, quick to pivot (fast).
Deciding questions: What does a break cost — users, money, trust? What does waiting cost — market, feedback, debt? Can you buy speed safely with flags, a strangler, branch by abstraction, expand/contract?
Default: make change cheap and safe rather than choosing a side. The techniques that buy both are the master's toolkit.
Signal you chose wrong: a change that “had to” be one big deploy, and a rollback plan that was really a hope. Or the opposite: a process so heavy that a one-line fix takes a week and people route around it.
Taught in: lesson 0035
Isolation vs Integration
Forces: independent evolution (isolated) vs shared consistency and leverage (integrated).
Deciding questions: Do both sides mean the same thing by the same word, with the same rules? What must genuinely stay consistent across them? Who needs the shared schema — the domain, or a report?
Default: isolation at the seams the domain provides: bounded contexts, not blind modularisation.
Signal you chose wrong: a field named status2, or a migration that needs three teams in a meeting. One model is being bent to hold two meanings.
Taught in: lesson 0036
Correctness vs Delivery Speed
Forces: right (correct) vs now (fast).
Deciding questions: What does “wrong” cost — a rolled-back deploy, or a legal liability? Is the risky part cheaply testable? Which slice delivers value while keeping the irreversible part contained?
Default: correctness for irreversible or costly operations; speed for reversible ones, with tests as the safety net rather than the tax.
Signal you chose wrong: an incident that could not be fixed by a revert. That is the signature of an irreversible change shipped at reversible speed.
Taught in: lesson 0037
Short-Term Productivity vs Long-Term Maintainability
Forces: ship this week (productive) vs understand it next year (maintainable).
Deciding questions: Is this code touched once, or for years? Who maintains it — you, a team, or nobody? Is the debt recorded as debt, or disguised as done?
Default: you may borrow from the future — but record the loan. Debt that is invisible is never repaid.
Signal you chose wrong: the team estimates in multiples: “that used to be a day, now it's a week”. The interest is being paid; the loan was never written down.
Taught in: lesson 0038
Sources: Rich Hickey, Simple Made Easy; Kleppmann, Designing Data-Intensive Applications; Bass et al., Software Architecture in Practice; Fowler bliki (Technical Debt, Strangler Fig); Dan McKinley, Choose Boring Technology; ArjanCodes, Software Design Mastery.