Track C — Distributed Systems Design
The reported technical screen's second round was a job scheduler with fault tolerance (
../../research/source-report.mdrow 8), reportedly in Excalidraw. The reported anti-pattern is name-dropping technologies without being able to defend the tradeoff.This track produces written design artifacts, not reading notes. Twelve of them, each attacked in writing by a hostile staff-level interviewer, then revised.
→ Study guide: WARMUP.md — every primitive from zero: quorums, Raft, leases and fencing, consistency models, partitioning, delivery semantics, load control. → d01-job-scheduler.md — a complete worked design with a hostile critique and the revision.
Table of Contents
- The Design Template
- Concept Inventory
- The Failure-Mode Catalog
- Back-of-Envelope Calculators
- The Twelve Design Exercises
- The Critique Loop
- Drill Set
- Failure Modes
- Self-Assessment Rubric
- References
The Design Template
Use it every time, in this order. It is what keeps you from rambling when the clock is running, and it makes the 45-minute budget survivable.
# Design: <name>
## 1. Requirements and scope
Functional. Non-functional. Explicitly out of scope.
## 2. Scale numbers
The numbers I assumed and the arithmetic I did with them.
## 3. API surface
The three to five calls that matter, with request/response shapes.
## 4. Data model
Tables/collections, keys, indexes — and WHY those keys.
## 5. High-level architecture
Components and flow. This is the diagram.
## 6. Deep dive: the two hardest components
Not the easy ones. The two where the design could actually fail.
## 7. Failure and recovery
For each: DETECTION, CONTAINMENT, RECOVERY. All three legs.
## 8. Bottlenecks and evolution
What breaks first at 10x. What I would change.
## 9. Tradeoffs I explicitly rejected, and why
Section 9 is the one that separates candidates. It is also the one that gets skipped when time runs short, which is why the time budget puts it at minute 35, not minute 44.
Time budget inside 45 minutes: 5 clarify · 5 API and data · 10 architecture and diagram · 15 deep dive · 10 failure and tradeoffs. If you are still drawing boxes at minute 25, the round is lost regardless of how good the boxes are.
Concept Inventory
Each primitive gets a one-page design note in designs/ and appears as a required
element in at least one exercise.
C1. Replication and Consensus
| Primitive | The question it answers | Where it is drilled |
|---|---|---|
| Leader election | Who decides, when several replicas could? | d01, d05 |
| Raft: log, terms, commit index | How do replicas agree on an ordered log? | d02, d11 |
| Paxos vs Raft, at a usable depth | Why does anyone still mention Paxos? | d11 |
| Leases and their expiry | How do you hand out temporary authority safely? | d01, d04 |
| Fencing tokens | How do you survive the zombie that comes back? | d01, d02 |
| Quorum reads/writes, R + W > N | What does a quorum actually buy you? | d02, d11 |
| Sync vs async vs semi-sync replication | What do you lose on failover? | d02, d09 |
| Read replicas and replica lag | Why did the user not see their own write? | d09, d12 |
Fencing tokens are the single highest-value item in this list. A lease expires; the holder does not necessarily know. Without a monotonically increasing token that the storage layer checks, a partitioned worker's late write silently corrupts state after its replacement has already run. Naming it unprompted is a reliable staff-level signal, and almost nobody does.
C2. Partitioning and Placement
| Primitive | The question | Drilled in |
|---|---|---|
| Hash vs range partitioning | Which one and what does it cost you? | d02, d06 |
| Consistent hashing, virtual nodes | How much moves when a node joins? | d02, d12 |
| Rebalancing without downtime | What happens to in-flight requests? | d02 |
| Hot partitions | One key is 40% of traffic. Now what? | d03, d06 |
| Shard ownership and membership change | Who owned this key during the transition? | d01, d02 |
C3. Storage and Consistency
| Primitive | The question | Drilled in |
|---|---|---|
| Write-ahead logging | What survives a crash mid-write? | d02, d07 |
| LSM trees vs B-trees | Write-heavy or read-heavy? | d07, d08 |
| MVCC and snapshot isolation | How do readers avoid blocking writers? | d02 |
| Write skew | The anomaly snapshot isolation still permits | d02 |
| Linearizability vs serializability | Two different words for two different things | d02, d11 |
| The outbox pattern | How do you write to a DB and a queue atomically? | d04, d10 |
| Idempotency keys | How does at-least-once become tolerable? | d04, d10 |
C4. Messaging and Delivery
| Primitive | The question | Drilled in |
|---|---|---|
| At-most / at-least / "exactly" once | Why the third one is a lie about delivery | d04, d10 |
| Visibility timeouts | The queue's version of a lease | d01, d04 |
| Dead-letter queues and redrive | Where does a poison message go? | d04 |
| Ordering guarantees, per-key ordering | Global ordering costs a single writer | d04, d10 |
| Consumer groups and rebalancing | Who is reading this partition right now? | d10 |
| Backpressure vs buffering vs shedding | Three different answers to "too much" | d03, d05 |
C5. Control Under Load
| Primitive | The question | Drilled in |
|---|---|---|
| Little's law: L = λW | The one equation you must have cold | calculators |
| The utilization/latency knee | Why 80% utilization is not "80% as bad as 100%" | calculators, d05 |
| Retry storms and retry budgets | How retries turn a blip into an outage | d04, d05 |
| Backoff with jitter | Full vs equal vs decorrelated | d04 |
| Circuit breakers | Half-open, and why the threshold is hard | d04, d05 |
| Load shedding and admission control | Refusing work as a feature | d05, and Track D |
| Cascading failure | How one slow dependency takes down everything | d05 |
| Bulkheads and cellular architecture | Containing blast radius by construction | d05, d12 |
C6. Time
| Primitive | The question | Drilled in |
|---|---|---|
| Wall clock vs monotonic | Why leases must use elapsed time on one node | d01 |
| Clock skew and NTP bounds | What "synchronized" actually means | d01, d11 |
| Logical clocks, vector clocks | Ordering without agreeing on time | d11 |
| Hybrid logical clocks | Causality with a bounded relation to real time | d11 |
| CRDTs | When you can avoid coordination entirely | d11 |
The Failure-Mode Catalog
Every design you produce must include a failure section, and every failure needs all three legs. "It retries" is not a failure analysis.
| Failure | Detection | Containment | Recovery |
|---|---|---|---|
| Node crash (fail-stop) | Heartbeat / lease expiry | Traffic drains to healthy nodes | Replacement joins, state re-replicates |
| Node hang (fail-slow) | Latency percentiles, not liveness pings | Eject on latency SLO breach, not on ping failure | Restart; investigate. Worse than a crash — it answers pings |
| Network partition | Quorum loss on the minority side | Minority refuses writes | Merge on heal; reconcile |
| Zombie holder | You cannot detect it | Fencing token rejected at the storage layer | Nothing to recover if fencing worked |
| Thundering herd after outage | Queue-depth spike | Rate-limited catch-up, jittered restarts | Drain at a bounded rate |
| Retry storm | Request rate rising while success rate falls | Retry budget as a fraction of base traffic | Circuit break, then half-open probe |
| Poison message | Attempt count exceeded | Dead-letter after N | Manual or automated redrive |
| Hot partition | Per-key metrics | Split, or cache, or rate-limit that key | Rebalance |
| Cascading failure | Correlated latency across services | Bulkheads, timeouts everywhere, shedding | Shed until stable, then ramp |
| Data corruption | Checksums, invariant audits | Quarantine; stop replicating it | Restore from a known-good point |
| Clock skew | Skew monitoring against NTP | Treat a skewed node as unhealthy | Resync; re-elect if it was leader |
Fail-slow deserves its own emphasis. A crashed node is easy: it stops answering. A node that answers health checks in 2ms while serving real requests in 40s is invisible to liveness probes and poisons every load balancer pointing at it. If your design's only health signal is "does it respond," you have not handled the common case.
Back-of-Envelope Calculators
cd tracks/systems-design/calculators
python3 envelope.py --help
python3 envelope.py qps --rps 50000 --ms 8 # concurrency and cores
python3 envelope.py queue --rho 0.8 # the utilization knee
python3 envelope.py storage --rows 1e10 --bytes 512 # bytes, replicated
python3 envelope.py retry --rps 10000 --fail 0.3 # retry amplification
The point is not the tool. The point is that you do the arithmetic out loud in the round. "50k rps at 8ms means 400 concurrent in flight, so at 200 per box that's 2 boxes plus headroom — call it 4 for failure tolerance" is worth more than a paragraph of adjectives. Any arithmetic beats none.
Numbers to have memorized cold — verify them yourself with envelope.py latencies:
| Operation | Order of magnitude |
|---|---|
| L1 cache reference | ~1 ns |
| Main memory reference | ~100 ns |
| SSD random read | ~16–100 µs |
| Round trip in the same datacenter | ~0.5 ms |
| Disk seek (spinning) | ~10 ms |
| Round trip US cross-country | ~40–70 ms |
| Round trip US to Europe | ~80–150 ms |
The Twelve Design Exercises
Written into designs/ as artifacts, in this order.
| # | Exercise | Why it is here |
|---|---|---|
| d01 | Fault-tolerant distributed job scheduler | Row 8 — the reported screen question. Do this one first |
| d02 | Distributed versioned KV store | The distributed counterpart to the reported coding question. Doing both makes the connection between rounds |
| d03 | Distributed rate limiter | Small surface, deep tradeoffs. Good early confidence |
| d04 | Webhook delivery system | Feeds ../../projects/ — build it after you have designed it |
| d05 | Load shedding and admission control gateway | The reliability primitive everything else leans on |
| d06 | Feature store (online + offline) | Your background. Should be your fastest |
| d07 | Log analytics pipeline | Ingest, index, query at volume |
| d08 | Multi-region metadata store | Where consistency stops being free |
| d09 | Search / retrieval serving | Your strongest area — make it the portfolio-adjacent one |
| d10 | Event streaming platform | Consumer groups, ordering, replay |
| d11 | Distributed lock / coordination service | Raft in anger; fencing tokens; the honest limits |
| d12 | Multi-tenant control plane | Isolation, fairness, noisy neighbours |
d01 and d02 are mandatory and come first. The rest are ordered by leverage, not by difficulty.
The Critique Loop
The mechanism that makes this track work. Every design goes through it.
- You write the design against the template, to a 45-minute clock.
- I attack it in writing, playing a hostile staff-level interviewer. Not "have you
considered" — specific, adversarial, with a concrete failure scenario:
"Your scheduler claims at-least-once. Worker A claims job J with a 30-second lease, then GC-pauses for 45 seconds. You re-dispatch to worker B. B completes and writes the result. A wakes up, finishes, and writes its result too. Walk me through what the user sees, and tell me which line of your design prevents it. If none does, say so."
- You revise, in place, with a changelog at the bottom.
- I re-attack the revision, harder.
- It is done when I cannot find a failure you have not named — including the ones you name and choose to accept.
Deliberately accepting a failure mode with a stated reason is a staff behavior. Claiming to have handled everything is a junior one, and it is trivially falsified by one good question.
Drill Set
| Drill | Cadence | Trains |
|---|---|---|
| Full design, 45 min | 1–2/week | The round itself. Template, timer, Excalidraw |
| Critique + revise | After every design | Defending under attack |
| Envelope-only, 10 min | 3×/week | Numbers reflex. One prompt, arithmetic only, no architecture |
| Deep-dive selection, 5 min | Daily | Read a prompt, name the two hardest components in 60 seconds. This is the highest-weight rubric line and it is trainable on its own |
| Failure-first | Weekly | Write section 7 before section 5. Forces detection/containment/recovery to shape the architecture |
| Rejected-alternatives | Weekly | Take a finished design and add two more rejected alternatives with quantified reasons |
| Diagram speed | Weekly | Redraw a past design in Excalidraw in 6 minutes |
Failure Modes
| Failure | Symptom | Fix |
|---|---|---|
| Deep-diving the wrong components | Polished API design, nothing on consensus or leases | Deep-dive-selection drill, daily |
| No numbers | Adjectives instead of arithmetic | Envelope-only drill |
| Name-dropping | "I'd use Kafka" with no defence of the alternative | Section 9 is mandatory |
| Failure section is one leg | "It retries" | The three-leg table above |
| Wrong altitude | Class diagrams, or "we'll use a queue" with no visibility timeout | Time budget enforces the middle |
| Running out of time | Sections 7–9 missing | Failure-first drill; enforce the minute markers |
| Ignoring fail-slow | Only liveness checks | Read the catalog; add latency-based ejection |
| Silent scope decisions | Never stated what is out of scope | Section 1, always |
Self-Assessment Rubric
| Level | Standard |
|---|---|
| L0 | ≤8/25 on the diagnostic rubric; no deep dive; no numbers |
| L1 | 9–14; coherent architecture; deep-dives the easy parts; thin failure analysis |
| L2 | 15–20; identifies both hard components; three-legged failures; some quantified rejections |
| L3 | 21–25; both hard components plus fencing/quorum reasoning unprompted; names an accepted failure mode and why; rejections quantified and reversible-condition stated |
Scoring detail in ../../diagnostics/RUBRIC.md.
The hard cap applies: if you deep-dived the wrong components, the round caps at L1 no matter
how good the rest was.
References
- Kleppmann, M. Designing Data-Intensive Applications, 2nd ed. — Ch. 5 (replication), 6 (partitioning), 7 (transactions, write skew), 8 (unreliable clocks, fencing), 9 (consistency and consensus)
- Ongaro, D. and Ousterhout, J. In Search of an Understandable Consensus Algorithm (Raft). USENIX ATC 2014. https://raft.github.io/raft.pdf
- Lamport, L. Paxos Made Simple. 2001.
- Burrows, M. The Chubby Lock Service for Loosely-Coupled Distributed Systems. OSDI 2006.
- Corbett et al. Spanner: Google's Globally-Distributed Database. OSDI 2012.
- Kingsbury, K. Jepsen analyses. https://jepsen.io/analyses — the best available catalog of how real systems break
- Beyer et al. Site Reliability Engineering. O'Reilly, 2016 — Ch. 21 (handling overload), Ch. 22 (cascading failures)
- Brooker, M. Exponential Backoff and Jitter. https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/
- Brooker, M. Timeouts, retries, and backoff with jitter. Amazon Builders' Library.
- Kulkarni et al. Logical Physical Clocks (HLC). OPODIS 2014.
- Shapiro et al. Conflict-free Replicated Data Types. SSS 2011.