CMD Guide
HomeSystem Design

System Design Building Blocks

Step 2 in the System Design path · 31 concepts · 0 problems

0 / 31 complete

📘 Learn System Design Building Blocks from zero

System Design "building blocks" are the reusable primitives — caches, load balancers, replicas, partitions, indexes — you snap together to answer the open-ended "design X" interview question. The interviewer rarely wants the one right answer; they want to hear you reason about WHY a block belongs there, the exact mechanism by which it helps, and the cost it quietly adds (staleness, complexity, more failure modes). Each step below asks you a concrete question first — commit to an answer in your head before you reveal the reasoning. That habit of "decision → mechanism → trade-off" is exactly what separates a hire from a no-hire.

✨ Added by the guide to build intuition — not from the source course.

📐 Numbers every engineer should know — quick reference

Keep these orders of magnitude in your head for back-of-the-envelope estimation in interviews. Exact values vary by hardware and year — what matters is the relative scale (cache < memory < SSD < network < disk < cross-continent).

Latency numbers (order of magnitude)

OperationTimeRelative
L1 cache reference~0.5 ns
Branch mispredict~5 ns10×
L2 cache reference~7 ns14×
Mutex lock / unlock~25 ns50×
Main memory (RAM) reference~100 ns200×
Compress 1 KB (Snappy/Zippy)~10 µs20,000×
Send 1 KB over 1 Gbps network~10 µs20,000×
Read 4 KB random from SSD~150 µs300,000×
Read 1 MB sequential from RAM~250 µs500,000×
Round trip within same datacenter~500 µs1,000,000×
Read 1 MB sequential from SSD~1 ms2,000,000×
Disk seek (HDD)~10 ms20,000,000×
Read 1 MB sequential from disk (HDD)~20 ms40,000,000×
Round trip CA ↔ Netherlands ↔ CA~150 ms300,000,000×

Takeaways: memory is ~100× faster than SSD and ~100,000× faster than a disk seek; a cross-continent round trip (~150 ms) dwarfs everything — minimize them. Sequential beats random by orders of magnitude on both SSD and disk.

Powers of two (for capacity math)

PowerApprox valueBytes
210~1 thousand1 KB
220~1 million1 MB
230~1 billion1 GB
240~1 trillion1 TB
250~1 quadrillion1 PB

Time & capacity shortcuts

  • Seconds/day ≈ 86,400 ≈ 105. So 1 write/sec ≈ ~86K/day ≈ ~2.5M/month ≈ ~30M/year.
  • QPS = (daily active users × actions per user per day) ÷ 86,400. Multiply by a peak factor of 2–10× for peak QPS.
  • Storage/day = writes/day × bytes/write. Bandwidth = QPS × payload size.
  • Availability: 99.9% ≈ 8.7 h/yr down · 99.99% ≈ 52 min/yr · 99.999% ≈ 5 min/yr.

Worked example — sizing a URL shortener

Assume 100M new URLs/month, 100:1 read:write.

  • Write QPS = 100M ÷ (30 × 86,400) ≈ ~40 writes/sec.
  • Read QPS = 40 × 100 = ~4,000 reads/sec.
  • Storage (5 yrs, ~500 bytes/record): 100M × 12 × 5 × 500 B ≈ ~3 TB.

Adapted and re-authored for this guide from the System Design Primer (donnemartin, CC BY 4.0) and Jeff Dean / Peter Norvig’s “latency numbers every programmer should know.”

Lessons in this topic

🏗️ Apply it — design walkthrough

Work through this after you've learned the concepts in the lessons above.

Estimate before you design

🤔 You're asked to design a Twitter-like feed for 200M daily users posting 2 tweets/day each. Before drawing a single box, what numbers must you derive, and why does this gate every later block?

Reveal the reasoning

Chain: 200M × 2 = 400M tweets/day → ÷ 86,400s ≈ ~4,600 writes/sec average, and peak is ~2–3x average → ~9,000–14,000 writes/sec (round to ~12K). Reads dominate: if each user refreshes a 100-tweet feed several times/day, you land around ~100K–1M reads/sec.

  • This read:write ratio of roughly 100:1 is the single most important number — it is what justifies caching and read replicas later.
  • Storage: 400M tweets × ~300 bytes ≈ 120 GB/day → ~44 TB/year of raw text → forces partitioning.

Cause → effect: the estimate dictates which blocks are mandatory — a read-heavy ratio mandates caches/replicas, the storage figure mandates sharding. Cost/trade-off: these are order-of-magnitude guesses, not precise figures — stating a wrong assumption confidently (e.g. forgetting the peak multiplier) makes you under-provision by ~3x. Always say your assumptions out loud so the interviewer can correct them cheaply.

Why a load balancer?

🤔 You have 12,000 writes/sec hitting one server that maxes out at 2,000 req/sec. You add 10 servers. What exact problem does a load balancer solve, and what new single point of failure did you just create?

Reveal the reasoning

Chain: clients can't know which of 10 servers to hit → an LB sits behind one virtual IP / DNS name → it spreads 12,000 req/sec across 10 boxes = 1,200 each, comfortably under the 2,000 limit. It also runs health checks (e.g. every few seconds) and stops routing to a dead node within seconds, so one crash doesn't drop traffic.

  • Mechanism: L4 LBs route on IP/port (fast, payload-opaque); L7 LBs parse the HTTP request and can route /images vs /api differently (smarter, slightly more CPU per request).
  • When: the moment you have >1 server, or want zero-downtime rolling deploys.

Cost/trade-off: the LB itself is now a single point of failure — if it dies, all 10 healthy servers become unreachable. Fix: run LBs in an active-passive pair with a floating/virtual IP that fails over to the standby, which adds cost and one more component to monitor. You've traded many per-server failure modes for one smaller, centralized one that you then make redundant.

Which LB algorithm?

🤔 Round-robin sends request 1→server A, 2→B, 3→C, evenly. So why do real systems often NOT use plain round-robin, and when would "least connections" or a hashing algorithm be strictly better?

Reveal the reasoning

Chain: round-robin assumes every request costs the same. But if server A draws three 10-second report queries while B/C get 10ms lookups, round-robin keeps feeding A its even share → A's queue grows → its p99 latency spikes while B/C sit idle.

  • Least-connections: routes to whoever has the fewest in-flight requests → naturally drains work away from the slow/overloaded A. Best for uneven request durations.
  • Weighted round-robin: a 16-core box gets weight 4, a 4-core box weight 1 → handles heterogeneous hardware.
  • IP/URL hash: the same client always lands on the same server → enables warm local cache hits / sticky sessions.

Cost/trade-off: least-connections needs the LB to track live connection counts (extra state and bookkeeping). Hash-based stickiness deliberately breaks even distribution: one whale client (a celebrity, a hot key) can overload its assigned server, and naive mod N hashing reshuffles nearly every client when a server is added or removed — which is the exact pain consistent hashing later solves by remapping only ~1/N of keys.

Why cache, and how does it cut p99?

🤔 Your DB read takes 20ms; a Redis lookup takes 1ms. With a 100:1 read:write ratio and a 90% cache hit rate, walk through the exact latency and load math. Then: what breaks when the cached data is wrong?

Reveal the reasoning

Chain: cache-aside — the app checks Redis first; on a miss it reads the DB, then writes the value back to Redis with a TTL. At a 90% hit rate: 90% of reads cost 1ms, 10% cost 1ms + 20ms ≈ 21ms → weighted average ≈ 3ms vs 20ms before, and crucially the DB now sees only 10% of read traffic (e.g. 100K → 10K reads/sec). That drop is what lets the DB survive.

  • p99 effect: the slow tail (DB queueing under load) is now hit by far fewer requests, so once the DB is no longer saturated the p99 collapses.

Cost/trade-off — staleness: a cached value can be out of date until its TTL expires. You pick the dial: short TTL (fresher, more DB load) or write-through / invalidate-on-write (consistent, more write-path complexity). Eviction: RAM is finite, so LRU drops cold keys — only worth it if the hot subset fits in memory. Stampede: when a hot key expires, thousands of concurrent misses hammer the DB at once — mitigate with a per-key rebuild lock (only one request repopulates) or jittered TTLs. Caching trades correctness-by-default for speed.

Partition when one box isn't enough

🤔 Your ~44 TB/year of tweets won't fit on one machine, and writes exceed one node's capacity. You decide to shard. What's the difference between sharding by hash(user_id) vs by date range — and what's the failure mode of each?

Reveal the reasoning

Chain: partitioning splits data across N nodes so each holds ~44 TB / N and absorbs ~12,000 / N writes/sec. The shard key decides everything downstream.

  • Hash(user_id): spreads load evenly → no single hot node. But a query like "all tweets between Jan–Mar" has no single home and must scatter-gather across all N shards, and with naive mod N hashing, adding a node remaps nearly every key (use consistent hashing to limit churn).
  • Range by date: time-range queries hit one shard → fast and cheap. But today's shard takes ~100% of writes while older shards sit idle → a write hotspot that moves over time.

Cost/trade-off: the deeper pain is cross-shard operations — a JOIN or a transaction spanning two shards needs distributed coordination (two-phase commit, slow and failure-prone) or app-level stitching. Celebrity hotspot: one user with 100M followers can overwhelm their shard regardless of key choice. Partitioning buys horizontal scale at the price of cheap joins and global transactions — so you pick a key that matches your dominant query pattern.

Index the read path

🤔 A query "find tweets by @user" scans a 10M-row table at maybe 1M rows/sec — that's ~10 seconds. You add an index and it returns in ~1ms. What did the index actually build, and why isn't it free to just index every column?

Reveal the reasoning

Chain: an index is a separate sorted structure (typically a B-tree) mapping user_id → row locations. Instead of a full scan (O(n), ~10M row reads), the DB descends the tree (O(log n); log₂(10M) ≈ ~23 levels of comparison, and far fewer actual node reads thanks to high B-tree fanout) → the 10s scan becomes a handful of seeks ≈ ~1ms.

  • Mechanism: it trades a linear scan for a logarithmic lookup by keeping the indexed column pre-sorted.
  • When: columns you frequently filter / sort / join on AND that are selective (many distinct values).

Cost/trade-off: every index is extra storage and must be updated on every write — each insert/update/delete also rewrites every affected index's B-tree. So 5 indexes can make writes meaningfully slower (often ~2–3x) and bloat disk. Indexing a low-cardinality column (e.g. a boolean) barely helps because it can't narrow the candidate set. Indexes optimize reads by taxing writes — the wrong default for a write-heavy table.

Replicate for HA and read scale

🤔 Your single DB serves 10K reads/sec fine, but if that box dies you lose everything, and you can't scale reads further. You add 2 replicas. How does this fix both problems at once — and why might a user post a tweet and then NOT see it?

Reveal the reasoning

Chain: leader-follower replication — all writes go to the leader, which streams its change log to 2 followers. Now: (1) reads spread across leader + 2 followers → roughly 3x read capacity; (2) if the leader dies, a follower is promoted (failover) → no committed data lost, only a brief unavailability window.

  • Mechanism: followers replay the leader's log, trailing it by anywhere from a few ms to seconds under load.

Cost/trade-off — replication lag: with async replication the leader acks the write immediately, so a user who posts (write → leader) and instantly reads from a lagging follower sees nothing yet → a read-your-writes violation. Fixes: pin that user's reads to the leader for a short window after a write, or use sync replication (the leader waits for a follower to confirm — safer, but every write now pays the slower of the two and write latency rises). Replication trades a window of inconsistency for availability and read scale — so you decide per read path how fresh the data must be.

🎯 Guided practice

  1. Easy — Pick the right block. "A news site shows the same 50 trending articles to 2M readers/hour; the DB is melting under read load. What do you add?" Step 1: classify the workload — extreme read-heavy, tiny working set (50 items), repeated reads of identical data. Step 2: map the symptom to a limit — DB CPU/IO saturated by redundant identical reads. Step 3: the matching block is a cache in front of the DB using cache-aside: on a request, check Redis; on miss, read DB and populate the cache. Step 4: name the tradeoff — cached articles can go stale, so set a short TTL (e.g. 60s) or invalidate on edit; guard the expiry moment against a thundering herd (request coalescing / lock-on-miss). Pattern learned: read-heavy + small hot set ⇒ cache; always pair a cache with an invalidation/expiry story.
  2. Medium — Scale storage and survive failure. "Design the data layer for a chat app: 500M messages/day, must stay available if a server dies, and inbox queries must be fast." Step 1 (estimate): 500M / 86,400 ≈ 5,800 writes/sec average (size for a peak multiple, say 3–5×); one node can't hold or serve this → scale out. Step 2 (partition): shard by user_id (or conversation_id) so one user's messages co-locate; use consistent hashing so adding capacity moves only ~1/N of keys, not the whole keyspace. Step 3 (availability): add replication — one leader per shard takes writes and replicates to followers; a heartbeat detects leader death and triggers failover so there's no single point of failure. Step 4 (fast queries): add a secondary index on (user_id, timestamp) for O(log n) range scans of a conversation. Step 5 (tradeoff): with async replication, a read served by a follower can lag the leader — you get eventual consistency and possible stale reads (no read-your-writes unless you route to the leader). This is not "AP" by itself: CAP only describes behavior during a partition, and a single-leader design typically sacrifices availability of the affected shard while a new leader is elected. Per PACELC, even with no partition (the Else branch) async replication trades consistency for lower latency. If correctness matters more, use synchronous quorum reads/writes with W + R > N. Pattern learned: scale via partition (+consistent hashing), survive via replication (+heartbeat/failover), query fast via indexes — and explicitly state the consistency/latency tradeoff each choice forces.

✨ Added by the guide — work these before the full problem set.

🧠 Review & recall

Active recall is what moves a topic into long-term memory. Flip each card before revealing, then test yourself — your results are saved on this device.

Flashcard
In the CAP theorem, why is 'CA' (Consistency + Availability without Partition tolerance) considered not a coherent choice for a distributed system?
tap to reveal →
Because network partitions are unavoidable in a distributed system, so when one occurs a non-partition-tolerant system is forced to abandon either Consistency or Availability anyway. The real choice is therefore: during a partition, pick C or A.
💡 Partitions aren't optional — so CAP is really 'CP vs AP'.
Flashcard
What guarantee does a Bloom filter make, and what is its failure mode?
tap to reveal →
A Bloom filter guarantees no false negatives (if an item was added, it always tests positive), but it can produce false positives (claim an item is present when it isn't). Add/query both run in O(k) time, independent of the number of stored items.
💡 'Definitely not, or probably yes' — never a false 'no'.
Flashcard
Why does naive hashing (key mod N servers) break when you add or remove a node, and how does consistent hashing fix it?
tap to reveal →
With modulo hashing, changing N changes every key's mapping, forcing a remap/move of nearly all data. Consistent hashing places nodes on a ring so only a small set of keys (those owned by the affected node, which pass to the next node) move when a node joins or leaves.
💡 Mod N = remap everyone; ring = only the neighbor's keys move.
Flashcard
What problem do virtual nodes (Vnodes) solve in consistent hashing?
tap to reveal →
Assigning a single large range per node causes non-uniform load and hotspots, and makes rebuild/rebalance expensive. Vnodes split the ring into many small subranges per physical node, spreading load evenly, speeding rebalancing, reducing hotspots, and supporting heterogeneous machines (more Vnodes to powerful servers).
💡 Many small slices per node, not one big wedge.
Flashcard
In a quorum system with N replicas, write quorum W, and read quorum R, what inequality guarantees every read sees the latest write, and what is a common strong-consistency config?
tap to reveal →
W + R > N guarantees the read and write sets overlap on at least one node, so every read sees at least one copy of the latest value. A common strongly-consistent config for N=3 is W=2, R=2.
💡 W + R > N forces an overlap; (3,2,2) is the classic.
Flashcard
Compare the three cache write strategies: write-through, write-around, and write-back, by their main trade-off.
tap to reveal →
Write-through writes to cache and DB simultaneously (strong consistency, no data loss, but higher write latency). Write-around writes only to storage, bypassing cache (avoids flooding cache, but recently-written reads miss). Write-back writes only to cache and confirms immediately, flushing to storage later (low latency/high throughput, but risk of data loss on crash).
💡 Through = safe+slow, Around = bypass cache, Back = fast+risky.
Q1. A stateful web app needs requests from the same client to consistently reach the same backend server, and the load balancer must avoid tracking per-connection state. Which algorithm best fits?
Q2. Using the lesson's load estimation method, a social platform has 100 million DAU each making 10 posts/day. Roughly what is the write request rate?
Q3. A Bloom filter is queried for an item and at least one of its k bit positions is 0. What can you conclude?
Q4. In a 5-node and a 4-node majority-quorum cluster, how many node failures can each tolerate, and why are odd node counts recommended?
Q5. Which mechanism is specifically used to detect that a server in a distributed system has failed, so traffic can be rerouted and the node replaced?