Tradeoffs

Every decision costs something. The grade is not the choice you make — it's whether you can name what it buys and what it takes.

The core axes Read vs write Latency vs consistency Build vs buy Complexity budget
🎓

Tradeoffs Are the Grading Rubric

Junior candidates argue about which option is correct. Senior candidates know there is rarely a correct option — there's a fit for the constraints in front of them. The interviewer is not checking whether you picked Redis or Memcached. They're checking whether you know what the pick costs.

So the unit of a good answer is not the decision. It's this sentence:

   "I'd choose  ┌─────────┐  because it gives us  ┌─────────┐
                │    X    │                       │    Z    │
                └─────────┘                       └─────────┘
    and I'm accepting  ┌─────────┐  which is fine here because ┌──────────────┐
                       │    Y    │                             │  requirement │
                       └─────────┘                             └──────────────┘

        X = the choice     Y = what it costs     Z = what it buys

Say all three parts. "I'll denormalize the feed" is half an answer. "I'll denormalize the feed so reads are a single lookup, paying with write amplification and the risk of stale data, which is acceptable because this is a read-heavy timeline where a few seconds of staleness is invisible" is the whole answer. Same decision. One of them passes.

💡
When you're stuck between two options, don't stall trying to find the "right" one. Say both out loud with their costs, state which constraint tips it, and move. The reasoning is worth more than the verdict (Design Process).
🎛

The Core Axes

Almost every decision in a design lands on one of a few axes. Learn these and you'll recognize the tradeoff before you've finished hearing the question.

Latency vs Consistency the everyday one

Wait for replicas to agree before answering (consistent, slower) or answer from the nearest copy (fast, maybe stale). You pay this on every replicated write, not just during failures.

Example: read from the primary for correctness, or from a replica to cut latency and offload the leader (Consistency Models).

Throughput vs Latency batching's bargain

Batch work together and you push more total volume, but each item waits for the batch to fill. Process items one at a time and each is fast, but the system does less overall.

Example: group writes into one commit, or buffer log lines before flushing — more throughput, higher tail latency.

Availability vs Consistency CAP, during a partition

When nodes can't talk, either refuse the request (consistent, unavailable) or serve what you have (available, possibly wrong). There is no third door once the network splits.

Example: a balance check should refuse; a like counter should serve stale (CAP).

Cost vs Durability how many copies

More replicas, more regions, more backups mean fewer ways to lose data — and a bigger bill. The right number of nines is the one the data is worth, not the most you can buy.

Example: three-way replication for the system of record, single-copy cache that you can rebuild.

Simplicity vs Flexibility the abstraction tax

A rigid design is easy to reason about and hard to extend. A flexible one absorbs future needs and hides more moving parts. Generality you don't need yet is complexity you pay for now.

Example: a fixed schema vs a generic key-value blob; a direct call vs a plugin system.

Build vs Buy whose problem is it

Build it and you get exactly what you want plus the burden of running it forever. Buy or adopt and you get it now, bounded by someone else's roadmap and priced by their vendor.

Example: a managed queue vs your own, hosted search vs a self-run Elasticsearch cluster.

ℹ️
These axes are not independent. Pushing consistency up usually pushes latency up and availability down at once. Part of the skill is seeing that moving one slider drags the others, and saying so.
↔️

Read vs Write Optimization

The most reused tradeoff in system design: you can make reads cheap or writes cheap, rarely both. Every technique here moves work from one side to the other. Which side you optimize follows from the read/write ratio, and you should have that ratio from your estimation (Estimation).

Denormalization, materialized views, and precomputed indexes all say the same thing — do the work once at write time so every read is a lookup. The clearest instance is feed fan-out.

  FAN-OUT ON WRITE                        FAN-OUT ON READ
  (precompute — cheap reads)              (compute on demand — cheap writes)

  Alice posts ──▶ copy into the feed      Alice posts ──▶ append to Alice's
                  of every follower                        own list. Done.
                  (1 write → N writes)
                                          Bob opens feed ──▶ gather posts from
  Bob opens feed ──▶ read his list.                          everyone Bob follows,
                     One lookup.                             merge, sort.
                                                             (1 read → N reads)

  Great when: reads ≫ writes,             Great when: writes ≫ reads, or the
  followers bounded.                      author has millions of followers.
  Dies on: celebrities (one post          Dies on: users following thousands
  fans out to millions).                  (every open is a huge scatter-gather).

Neither wins outright, which is why real feeds go hybrid: fan-out on write for normal accounts, fan-out on read for celebrities, stitched together at read time. That hybrid is itself a tradeoff — more moving parts in exchange for handling both ends of the follower distribution (News Feed).

Moving work from read to write and back
TechniquePays forCosts you
DenormalizationReads with no joinsDuplicated data, update fan-out, drift risk
Materialized viewPrecomputed query resultsRefresh lag, storage, staleness
Secondary indexFast lookups on non-key fieldsSlower writes, more space (Indexing)
CachingRepeated reads served from memoryInvalidation, staleness, a cold-start cliff (Caching)
Fan-out on writeFeed reads as one lookupWrite amplification, celebrity blowup
⚠️
Every read optimization on this list creates a second copy of the truth. Two copies can disagree. The hidden cost is never just storage or write latency — it's the consistency problem you now own and have to close with invalidation or a refresh path.
🧮

The Complexity Budget

Each component you add is not free even if it's fast. It's another thing to deploy, monitor, secure, keep consistent, and page someone about at 3am. Treat complexity as a budget you spend, and make every box justify itself against a requirement you actually have.

The failure mode in interviews is the opposite: reaching for Kafka, a cache tier, and five microservices before anyone has established there's a problem that needs them. That reads as pattern-matching, not engineering.

  For each component, ask:

    Requirement it serves? ──no──▶ delete it. It's decoration.
            │ yes
            ▼
    Does the simple version fail? ──no──▶ keep the simple version.
            │ yes                          (single DB, sync call, no cache)
            ▼
    Fails on which number? ──▶ name it: QPS, data size, latency target,
            │                  blast radius. That number is your justification.
            ▼
    Add the component. Now you can defend it.

The discipline is to start simple and let a specific number force each addition. One database until the read QPS exceeds what it serves — then a cache. One region until latency to distant users hurts — then a second (Scaling Patterns). Adding the machinery before the number arrives is spending budget you didn't need to.

💡
A strong line: "The simplest thing that works here is a single Postgres instance. It handles the write load from our estimate. I'll add a read replica the moment read QPS crosses X, and shard only if a single node's storage can't hold a year of data." You've shown you know the fancy version and that you won't reach for it early.
📋

Quick Reference

You chooseYou gainYou pay with
Strong consistencyCorrect reads, single-copy illusionLatency, availability under partition
Eventual consistencyLow latency, stays up in a partitionStale reads, reconciliation logic
DenormalizationRead with no joinsDuplicated data, update fan-out
NormalizationOne source of truth, clean writesJoins on the read path
CachingFast repeated readsInvalidation, staleness, cold starts
Fan-out on writeCheap feed readsWrite amplification, celebrity blowup
Fan-out on readCheap writesExpensive scatter-gather reads
BatchingThroughputHigher tail latency
Synchronous callSimple, immediate resultTight coupling, blocking on failure
Async / queueDecoupling, spike absorptionEventual consistency, harder debugging
More replicas / regionsDurability, locality, failoverCost, cross-region latency, conflicts
ShardingHorizontal scale past one nodeCross-shard queries, rebalancing, hot keys
SQLTransactions, joins, flexible queriesHarder horizontal scaling
NoSQLScale, schema flexibilityQuery-first modeling, weaker transactions
MicroservicesIndependent deploy and scaleNetwork calls, distributed failure, ops load
MonolithSimple deploy, local calls, real transactionsScales as one unit, coupled teams
BuildExactly what you needYou run it forever
Buy / managedIt works nowVendor limits, cost, less control
ℹ️
The one-liner to keep ready. There are no free wins in a distributed system — only trades. Name the choice, name what it buys, name what it costs, and tie the cost back to a requirement that makes it acceptable. Do that out loud on every major decision and you're answering at the level they're grading.