System-design interview
The System Design method, out loud in 45 minutes: requirements→estimation→API→data→architecture→scale, deep-diving a bottleneck with tradeoffs — and the senior-vs-staff signals.
Learning objectives
- Drive a system-design interview with a repeatable method.
- Do capacity estimation out loud.
- Cover API, data model, architecture, and scale.
- Hit the seniority signals (senior vs staff).
code/cr4-systemdesign-interview/ — run them against your own resume, stories, and offers.1 · The method (you already have it) essential
The System Design capstone taught the 5-step method; the interview is that method, out loud, in 45 minutes. Drive it — don't wait to be led.
The system-design interview flow
- Requirements: functional + non-functional (scale, latency, consistency). Clarify first.
- Estimate: users, QPS, storage — back-of-envelope (SD6).
- API: the handful of endpoints.
- Data model + shard key (SD2/SD7).
- Architecture: client→LB→service→cache/DB (SD5); then scale the bottleneck.
2 · Estimation out loud essential
Interviewers want to see the method, not exact numbers: state assumptions, compute average then peak, size from there. Here's the SD6 math as a reusable helper.
estimate.pydef estimate(dau, actions_per_day, read_write_ratio=10, peak=3):
avg_qps = dau * actions_per_day / 86_400
peak_qps = avg_qps * peak
writes = peak_qps / (read_write_ratio + 1)
reads = peak_qps - writes
return {
"avg_qps": round(avg_qps),
"peak_qps": round(peak_qps),
"peak_reads": round(reads),
"peak_writes": round(writes),
}
print(estimate(dau=10_000_000, actions_per_day=10)) # a big consumer app
{'avg_qps': 1157, 'peak_qps': 3472, 'peak_reads': 3157, 'peak_writes': 316}
In a system-design interview you're expected to do quick back-of-the-envelope capacity math out loud — roughly how many requests per second (QPS) your system must handle. This helper does that math so you can see the method: start from daily users, get an average, then a peak, then split into reads and writes.
- The inputs (parameters):
dauis daily active users,actions_per_dayis how many actions each does.read_write_ratio=10andpeak=3have defaults — a typical app reads about 10× more than it writes, and peak traffic is roughly 3× the average. avg_qps = dau * actions_per_day / 86_400spreads the day's total actions over the number of seconds in a day (86,400) to get the average requests per second. The underscores in86_400are just digit separators for readability.peak_qps = avg_qps * peakscales up to the busy period. Thenwrites = peak_qps / (read_write_ratio + 1)andreads = peak_qps - writessplit that peak into the write share and the (much larger) read share.- The function returns a small dictionary of the four rounded numbers. The bottom line calls it for a big consumer app: 10 million users doing 10 actions a day.
What the output means: {'avg_qps': 1157, 'peak_qps': 3472, 'peak_reads': 3157, 'peak_writes': 316} — about 3,500 requests/sec at peak, mostly reads. Those numbers are what justify decisions like "add a read cache" or "use read replicas".
Try this: Change read_write_ratio to 1 (a write-heavy system) and see reads and writes even out. In the interview, narrate each step as you compute it — the method matters more than the exact figure.
3 · Cover the pillars intermediate
Walk API → data model → architecture, drawing boxes. Name the SD building blocks as you go: a cache for read-heavy load (SD5), replicas for read scaling (SD6), sharding + consistent hashing for write scaling (SD7), a queue to decouple (SD7). Justify each with your estimate.
4 · Advanced — deep-dive a bottleneck advanced
Interviewers steer you to go deep on one component. Be ready to detail the data schema, the caching/invalidation strategy, how sharding handles a hot key, or how the queue guarantees delivery. Depth on one area beats shallow coverage of all.
5 · Professional — tradeoffs & failure modes professional
Senior signal is naming tradeoffs, not reciting components. "SQL gives me transactions but caps write scale; I'll shard / add a cache / accept eventual consistency here because…" (CAP, SD6). Also discuss failure: what happens when the cache/DB/a shard dies?
6 · Tech-lead — the seniority ladder in the room tech-lead
The same prompt is graded differently by level. Knowing the bar tells you what to emphasize.
| Level | What the design shows |
|---|---|
| Mid | a working design with the right components |
| Senior | justified tradeoffs, bottleneck deep-dive, failure handling |
| Staff/Lead | ambiguity resolved, cost/ops/org impact, drives the whole conversation |
Exercise CR4.1 — Mock a design, out loud
Context: The method only sticks under the clock. Running a full 45-minute mock aloud — clarify, estimate, design, deep-dive a bottleneck — and grading yourself against the seniority table is the rep that counts.
Your task: Pick a prompt (URL shortener, news feed, chat). In 45 minutes and talking aloud: clarify, estimate, sketch API/data/architecture, then deep-dive one component with explicit tradeoffs and a failure mode. Record it and grade yourself against the seniority table.
Requirements:
- Run the five steps: requirements, estimation, API, data model, architecture
- Narrate throughout and keep to ~45 minutes
- Deep-dive one component: name a failure mode and give layered mitigations
- Make at least one tradeoff explicit and defend it
- Grade yourself: mid (working design), senior (failure + tradeoffs), staff (org + product framing)
💡 Hint: Pick the bottleneck the estimation exposed and drive the deep-dive there — the numbers should point you at what carries the load.
🪜 Practice ladder beginner → industry
Six graded exercises, easy to real-world. Try each before opening its solution.
Context: Designing before scoping is the most common junior mistake in a system design interview. The first move is clarifying questions that each change a box on the diagram.
Your task: The prompt is "Design a URL shortener." Write the first four clarifying questions you'd ask to pin scope and scale before designing anything, and say why each changes the design.
Requirements:
- Ask read:write ratio and QPS — changes caching and replication vs ID strategy
- Ask total URLs and lifetime — sizes the key space and encoding length
- Ask about expiry and custom aliases — TTL/cleanup and collision checks
- Ask latency target and consistency needs — how aggressively to cache and async analytics
- For each, state which part of the design it changes
💡 Hint: A good clarifying question is one whose answer moves a box on the diagram — if it doesn't change the design, it's ritual.
Show solution
- “What’s the read:write ratio and expected QPS?” — a 100:1 read-heavy system pushes you toward caching and read replicas; write-heavy changes the ID-generation strategy.
- “How many URLs total / over what lifetime?” — sizes the key space and whether 7 vs 8 base-62 characters is enough.
- “Do links expire? Custom aliases allowed?” — expiry needs a TTL/cleanup path; custom aliases need a collision check.
- “Latency target and consistency needs?” — “redirect in <50ms” and “eventual consistency is fine” let you cache aggressively and go async on analytics.
Designing before scoping is the most common junior mistake. Each question changes a box on the diagram — that’s how you show the interviewer the questions are load-bearing, not ritual.
Context: The interviewer wants to hear back-of-envelope math with round numbers and stated assumptions — because the numbers drive the next design decision ("so we need a cache").
Your task: For that URL shortener, estimate storage and QPS from assumptions. Show the back-of-envelope math — round numbers, stated assumptions.
Requirements:
- State assumptions (e.g. new URLs/month, read:write ratio, retention period)
- Derive average and peak write QPS
- Derive average and peak read QPS from the ratio
- Estimate total storage (record size × record count over retention)
- Draw a conclusion from each number (e.g. reads demand a cache; storage fits a sharded DB)
💡 Hint: Round aggressively and say the assumptions aloud — the point is that each number forces the next decision, not decimal precision.
Show solution
Assume: 100M new URLs/month, read:write = 100:1, keep 5 years.
- Writes: 100M / month ≈ 100M / 2.5M s ≈ ~40 writes/s average, call it ~400/s peak (10×).
- Reads: 100× writes ≈ ~4,000 reads/s average, ~40k/s peak.
- Storage: ~500 bytes/record (URL + metadata) × 100M/mo × 60 mo ≈ 6B records × 500B ≈ ~3 TB.
Conclusions you state: 3TB fits comfortably in one sharded DB; 40k reads/s demands a cache in front. The point isn’t precision — it’s that the numbers drive the next design decision (“so we need a cache”), spoken aloud.
Context: A complete design covers the pillars — API, data model, ID generation, read path, write path — and then names the one component that carries the load, showing you can be thorough and prioritise.
Your task: Sketch (in words) the end-to-end design for the shortener covering the core pillars: API, data model, ID generation, read path, and write path. Name the one component that carries the load.
Requirements:
- API: a shorten endpoint and a redirect endpoint
- Data model: id (base-62 PK) → long URL, timestamps, optional TTL
- ID generation: an encoded counter or Snowflake-style IDs to avoid collision checks
- Read and write paths distinguished (cache on the read path, ID gen + DB on the write path)
- Name the cache as the load-bearing component and say why
💡 Hint: Cover every pillar, then point at the one box that carries the load — completeness plus prioritisation is the signal.
Show solution
Client
|
v
[API gateway] --write--> [App servers] --> [ID generator] --> [DB: id -> long_url, sharded]
| (base-62 counter / Snowflake-style)
|--read (GET /abc123)--> [App] --> [Cache: id -> long_url] --hit--> 301 redirect
|--miss--> [DB] --> populate cache --> redirect
Analytics: click events --> [queue] --> async aggregation (off the hot path)
- API:
POST /shorten{long_url} → {short};GET /{id}→ 301. - Data model: id (PK, base-62) → long_url, created_at, ttl.
- ID gen: a counter encoded base-62 (or Snowflake IDs) — avoids collision checks that a random hash would need.
- Load-bearing component: the cache — 40k reads/s make it the difference between a fast redirect and a hammered DB.
Covering all pillars then pointing to the one that carries the load shows you can both be complete and prioritize.
Context: The senior signal is a failure analysis: when the interviewer kills the cache node, you name the failure mode (thundering herd) and give layered mitigations, not just "add another cache".
Your task: The interviewer says: "The cache node fails. Walk me through what happens and how you'd prevent an outage." Give the failure analysis and the mitigation.
Requirements:
- Trace the failure: cache loss → every read misses → the DB is hit all at once → cascading timeouts
- Name it: the thundering herd
- Mitigate with a replicated/clustered cache so one failure loses a shard, not everything
- Guard the herd: request coalescing (single-flight) and jittered TTLs
- Protect the DB: a concurrency limiter / circuit breaker for graceful degradation
- Warm hot keys on deploy so a cold cache isn't empty
💡 Hint: Naming the failure mode and layering the mitigations (don't-run-one-node, coalesce, protect-the-DB, warm) is what reads as senior.
Show solution
What happens on cache loss: every read misses → all ~40k reads/s hit the DB at once (a thundering herd) → DB latency spikes → timeouts cascade to the app tier.
Mitigations, in order of leverage:
- Don’t run one cache node. Use a replicated/clustered cache so a single failure loses a shard, not everything.
- Guard the herd: request coalescing (single-flight) so 10k concurrent misses for the same key trigger one DB read, plus jittered TTLs so keys don’t all expire together.
- Protect the DB: a concurrency limiter / circuit breaker in front so a cache-cold event degrades gracefully (some slow redirects) instead of taking the DB down.
- Warm on deploy: pre-populate hot keys so a cold cache isn’t empty.
The move that separates senior from mid: naming the failure mode by name (thundering herd) and giving layered mitigations rather than just “add another cache.”
Context: The professional signal is presenting a tradeoff as a table and defending a choice for this system — and naming when you'd choose the opposite.
Your task: You must choose between strong and eventual consistency for click-analytics counts. Present it as a tradeoff table and defend a choice for this system.
Requirements:
- Table strong (sync count on redirect) vs eventual (async via queue)
- Strong: exact count instantly, but latency on the hot path and analytics coupling
- Eventual: low latency and decoupled, but counts lag and you need a queue + consumer
- Choose eventual here because the redirect is sacred and analytics can lag seconds
- Name the exception (e.g. billing counts) where you'd pay for strong consistency
💡 Hint: Protect the SLA that matters — the redirect — and let analytics lag; naming when you'd flip the choice is what proves you're reasoning, not guessing.
Show solution
| Strong (sync count on redirect) | Eventual (async via queue) | |
|---|---|---|
| Redirect latency | Higher — write on hot path | Low — count off the hot path |
| Accuracy | Exact, instantly | Exact within seconds, may lag |
| Failure blast radius | Analytics DB down → redirects fail | Queue backs up; redirects unaffected |
| Cost/complexity | Simpler code, worse coupling | Needs a queue + consumer |
Choose eventual. For a shortener, the redirect is sacred and analytics can lag a few seconds — decoupling protects the SLA that matters. I’d state the exception: if this were billing counts, I’d pay the latency for strong consistency. Naming when you’d choose the opposite is the professional signal.
Context: The same prompt is scored differently for mid, senior, and staff. Knowing what a passing answer looks like at each level tells you what to reach for.
Your task: The same "design a rate limiter" prompt is scored differently for mid vs staff. Write what a passing answer looks like at each level (mid, senior, staff), so you know what to reach for.
Requirements:
- Mid: a correct algorithm (token bucket), the data structure, the distributed case (shared counter), the complexity
- Senior: all of that plus failure modes (fail-open vs fail-closed), sync overhead, algorithm choice with reasons
- Staff: all of that plus the product framing (per user/IP/key? business goal?), org cost as shared infra, safe rollout/observability
- State the core axis: mid = correct, senior = correct under failure, staff = correct for the org and business
- If targeting senior, volunteer the failure analysis before being asked
💡 Hint: Climb the same answer: correct, then correct-under-failure, then correct-for-the-organization — each level adds a layer rather than replacing one.
Show solution
| Level | What a pass looks like |
|---|---|
| Mid (E4) | Correct token-bucket, states the data structure, handles the basic distributed case (shared counter in Redis), knows the complexity. |
| Senior (E5) | All of the above + failure modes (Redis down → fail-open vs fail-closed decision), the sync overhead of a shared counter, and picks an algorithm (sliding-window vs token-bucket) with reasons. |
| Staff (E6) | All above + frames the product question (rate-limit per user? per IP? per API key? what’s the business goal?), the org cost of the limiter as shared infra, and how it’s rolled out/observed/tuned safely across many teams. |
Notice the axis: mid = correct, senior = correct under failure, staff = correct for the org and the business. If you’re targeting E5, don’t stop at the working design — volunteer the failure analysis before you’re asked.
✓ Checkpoint — you can move on when you can…
- Drive the 5-step method in 45 minutes.
- Estimate capacity out loud.
- Cover API/data/architecture and scale the bottleneck.
- Name tradeoffs + failure modes at the target seniority.
Knowledge check check yourself
What is the 5-step method for driving a system-design interview, and roughly how long do you have to run it?
Show answer
What distinguishes a senior-level system-design answer from just naming the right components?