System Design — a 13-week curriculum
Pace: 1–3 lessons a day. ~120 lessons over 13 weeks, with built-in review days. Numbering is continuous — Module 0 is the seven lessons already built, and every module after it continues the same sequence. Grounding: every lesson names a primary source before it is written. No lesson ships on parametric knowledge. Sources are drawn from four spines plus first-party engineering blogs:
- DDIA, 2nd edition (Kleppmann & Riccomini) — the data-systems spine. Part I: architecture trade-offs · non-functional requirements · data models · storage and retrieval · encoding. Part II: replication · partitioning · transactions · the trouble with distributed systems · consistency and consensus. Part III: batch · stream · stream philosophy · doing the right thing.
- MIT 6.5840 Distributed Systems — the papers and their order: MapReduce, GFS, Paxos, Raft, linearizability, ZooKeeper, distributed transactions, Spanner, chain replication, FaRM, memcache, Lambda, Ray, BFT.
- Google SRE Book — the operations spine: SLOs, toil, monitoring, load balancing, handling overload, cascading failure, distributed consensus for reliability, cron, data integrity.
- AWS Builders' Library — failure modes, written by people who carry the pager.
- First-party engineering blogs — the real architectures, with numbers.
Method. Each lesson: one tangible win, completable in a sitting, anchored to a documented real system, following the shape Poonnachit's own questions forced — problem → topology chosen → alternatives rejected and their prices → mechanism → residual risk. Each ends in retrieval practice with interleaved callbacks. See NOTES.md.
Module 0 · Foundations — built (weeks 0–1)
The seven lessons that already exist. They were written before this plan and set the vocabulary and the method everything after them assumes. Later modules deepen them rather than repeat them.
| # | Lesson | Primary source | Deepened later by |
|---|---|---|---|
| 01 | The Replica Time Machine — replication lag, read-your-writes, the staleness ladder | Facebook, Scaling Memcache (NSDI '13) §5 | M3 (consistency), M4 (consensus) |
| 02 | Choosing Your Failure Mode — partition keys, hot partitions, p99 | Discord, 2017 + 2023 | M1 (storage), M7 (consistent hashing) |
| 03 | The Dangerous Moment — thundering herds, stale sets, leases | Facebook §3.2.1 | M5 (async work), M8 (overload) |
| 04 | Retries Are Selfish — timeouts, 243× amplification, backoff, jitter | AWS Builders' Library | M8 (cascading failure), M9 (alerting) |
| 05 | The Dial — N/R/W quorums and the availability trade | Amazon, Dynamo (SOSP '07) | M3 (consistency), M4 (consensus) |
| 06 | The Cheap Move First — sequencing the scaling moves | Figma | M1, M6 |
| 07 | Five Minutes — migrating a live database in four phases | Notion | M5 (CDC), M6 (reprocessing) |
Threads already running through these, which later modules pick up by name rather than reintroducing: idempotence as what makes distributed messiness survivable; the tail (p99, peak, per-second) rather than the mean deciding engineering; where enforcing state lives and what happens when the user is not where it is.
Module 1 · Data on one machine (weeks 1–2) — built
| # | Lesson | Primary source |
|---|---|---|
| 08 | How a B-tree actually stores your table | DDIA 2e ch.4 |
| 09 ✅ | LSM-trees, compaction, and why write-heavy systems chose them | DDIA 2e ch.4; RocksDB docs |
| 10 ✅ | Read amplification, write amplification, space amplification | RocksDB wiki |
| 11 ✅ | What an index costs you on every write | Postgres docs |
| 12 ✅ | Row stores vs column stores, and when the answer flips | DDIA 2e ch.4 |
| 13 ✅ | Encoding and schema evolution: the compatibility you owe | DDIA 2e ch.5 |
| — | Review day — retrieval across 01–13 | — |
Module 2 · Transactions (week 2) — built
| # | Lesson | Primary source |
|---|---|---|
| 14 ✅ | What ACID actually promises, and what it doesn't | DDIA 2e ch.8 |
| 15 ✅ | Read committed, snapshot isolation, and MVCC in Postgres | Postgres docs |
| 16 ✅ | The anomalies: dirty reads, lost updates, write skew | DDIA 2e ch.8 |
| 17 ✅ | Serializable isolation, and why it is rarer than you think | DDIA 2e ch.8 |
| 18 ✅ | Two-phase commit and why it is a coordination tax | MIT 6.5840 |
| 19 ✅ | Sagas: giving up atomicity on purpose | Garcia-Molina & Salem (1987) |
| — | Review day | — |
Module 3 · Time, order and truth (week 3) — built
| # | Lesson | Primary source |
|---|---|---|
| 20 ✅ | Why you cannot trust a timestamp | DDIA 2e ch.9 |
| 21 ✅ | Lamport clocks and happens-before | Lamport (1978) |
| 22 ✅ | Vector clocks, and Dynamo's version history | Dynamo §4.4 |
| 23 ✅ | Linearizability: the strongest thing you can ask for | Herlihy & Wing; MIT 6.5840 |
| 24 ✅ | The consistency map: what each guarantee actually means | Jepsen |
| 25 ✅ | CAP, PACELC, and what the theorem does not say | Brewer; Abadi |
| — | Review day | — |
Module 4 · Consensus (week 4) — built
| # | Lesson | Primary source |
|---|---|---|
| 26 ✅ | Why agreement is hard: FLP and the two generals | MIT 6.5840 |
| 27 ✅ | Paxos, explained through what it refuses to do | Paxos Made Simple |
| 28 ✅ | Raft: leader election | Raft extended §5 |
| 29 ✅ | Raft: log replication and safety | Raft extended §5 |
| 30 ✅ | ZooKeeper: consensus as a product | ZooKeeper (2010) |
| 31 ✅ | Spanner and TrueTime: buying order with atomic clocks | Spanner (2012) |
| 32 ✅ | Chain replication: a different shape of agreement | Chain Replication (2004) |
| — | Review day | — |
Module 5 · Asynchronous work (weeks 5–6)
| # | Lesson | Primary source |
|---|---|---|
| 33 ✅ | Queues: the load you defer is still load | AWS Builders' Library |
| 34 ✅ | Insurmountable queue backlogs, and how they form | AWS Builders' Library |
| 35 ✅ | At-least-once, at-most-once, and the exactly-once myth | Kafka docs |
| 36 ✅ | The outbox pattern: writing to a database and a queue | Debezium docs |
| 37 ✅ | Kafka's log: partitions, offsets, consumer groups | Kafka docs |
| 38 ✅ | Backpressure, and what happens without it | Google SRE ch.21 |
| 39 | Dead letters, poison messages, and replay | AWS docs |
| 40 | Change data capture as an architecture | Debezium; Netflix DBLog |
| 41 | Event sourcing: the log as the source of truth | Kleppmann |
| 42 | Stream processing: windows, watermarks, late data | DDIA 2e ch.12 |
| — | Review days ×2 | — |
Module 6 · Batch and derived data (week 7)
| # | Lesson | Primary source |
|---|---|---|
| 43 | MapReduce, and why it mattered | MapReduce (2004) |
| 44 | GFS: the assumptions that shaped a filesystem | GFS (2003) |
| 45 | Data lakes and the 200-billion-entity problem | Notion data lake post |
| 46 | Materialised views and the cost of derived data | DDIA 2e ch.11 |
| 47 | Idempotent pipelines and reprocessing history | Google SRE ch.25 |
| — | Review day | — |
Module 7 · The network and the edge (week 8)
| # | Lesson | Primary source |
|---|---|---|
| 48 | Load balancing at the frontend: DNS, anycast, and their limits | Google SRE ch.19 |
| 49 | Load balancing in the datacentre: why round-robin is wrong | Google SRE ch.20 |
| 50 | The power of two random choices | Mitzenmacher (1996) |
| 51 | Consistent hashing and virtual nodes | Dynamo §4.2; Karger (1997) |
| 52 | Shuffle sharding: blast radius as a design parameter | AWS Builders' Library |
| 53 | CDNs: what actually gets cached, and what cannot | Cloudflare docs |
| 54 | Connection pooling, keepalives, and head-of-line blocking | Postgres; HTTP/2 spec |
| — | Review day | — |
Module 8 · Reliability engineering (week 9)
| # | Lesson | Primary source |
|---|---|---|
| 55 | SLIs, SLOs and error budgets: reliability as a number | Google SRE ch.3–4 |
| 56 | Handling overload: shedding load before it sheds you | Google SRE ch.21 |
| 57 | Cascading failure: how one slow service takes the fleet | Google SRE ch.22 |
| 58 | Avoiding fallback: why the backup path is the danger | AWS Builders' Library |
| 59 | Health checks that lie | AWS Builders' Library |
| 60 | Static stability: surviving without the control plane | AWS Builders' Library |
| 61 | Distributed cron, and the hard part of "run this once" | Google SRE ch.24 |
| — | Review day | — |
Module 9 · Observability (week 10)
| # | Lesson | Primary source |
|---|---|---|
| 62 | Monitoring distributed systems: the four signals | Google SRE ch.6 |
| 63 | Percentiles, histograms, and why you cannot average a p99 | Prometheus docs |
| 64 | Distributed tracing: Dapper and what it costs | Dapper (2010) |
| 65 | Structured logs, cardinality, and the bill | Honeycomb |
| 66 | Alerting that people do not learn to ignore | Google SRE ch.10 |
| 67 | Incident response and the blameless post-mortem | Google SRE ch.14–15 |
| — | Review day | — |
Module 10 · Real architectures, told properly (weeks 11–12)
Each lesson: the problem, the topology, the alternatives rejected, the mechanism, the residual risk.
| # | Lesson | Primary source |
|---|---|---|
| 68 | Instagram: three engineers, fourteen million users | Instagram Eng |
| 69 | Netflix: the API gateway and four architectural eras | Netflix Tech Blog |
| 70 | Netflix: chaos engineering as a practice | Netflix Tech Blog |
| 71 | Uber: geospatial indexing and dispatch | Uber Eng |
| 72 | Uber: from monolith to domain-oriented microservices | Uber Eng |
| 73 | Slack: how a message reaches a million clients | Slack Eng |
| 74 | Dropbox: leaving S3 for their own storage | Dropbox Tech Blog |
| 75 | Stripe: idempotency keys as a public API | Stripe docs |
| 76 | Shopify: pods, and surviving Black Friday | Shopify Eng |
| 77 | Cloudflare: how a request is served at the edge | Cloudflare blog |
| 78 | Twitter: the timeline fan-out problem | Twitter Eng |
| 79 | Pinterest / LinkedIn: sharding a social graph | Eng blogs |
| 80 | Zoom / WebRTC: real-time media at scale | Zoom Eng |
| 81 | Figma: multiplayer and CRDT-adjacent editing | Figma blog |
| 82 | Google Bigtable and the descendants it spawned | Bigtable (2006) |
| 83 | Amazon S3: eleven nines, and what that costs | AWS re:Invent talks |
| — | Review days ×2 | — |
Module 11 · The tech-lead layer (week 13)
This is where the mission actually lands.
| # | Lesson | Primary source |
|---|---|---|
| 84 | Writing a design doc people can disagree with | Google eng practices |
| 85 | Architecture decision records, and recording the rejected options | Nygard |
| 86 | Non-functional requirements: writing the list Discord wrote | Discord 2017; DDIA 2e ch.2 |
| 87 | Estimating: back-of-envelope numbers worth memorising | Dean's numbers; Brooker |
| 88 | Running a design review without being the smartest person | — |
| 89 | Migration planning: the four phases, generalised | Lesson 07 |
| 90 | Saying no to a design, and what to say instead | — |
Capstones (throughout, one per fortnight)
Open-ended design exercises graded against the real system's published choices.
- C1 Design a chat system → compare against Discord and Slack.
- C2 Design a URL shortener → compare against real key-generation and caching choices.
- C3 Design a feed → compare against Twitter's fan-out.
- C4 Design a ride-hailing dispatch → compare against Uber.
- C5 Design a payments API → compare against Stripe's idempotency model.
- C6 Design a metrics store → compare against Prometheus and its trade-offs.
Spacing plan
Retrieval is scheduled, not incidental. A concept is re-tested at roughly +1 week, +3 weeks, +8 weeks, inside a later, unrelated lesson's quiz — never in a block. Current debts are tracked in NOTES.md.
Gaps to close before the lessons that need them
- No verified first-party source yet for module 5 beyond AWS and Kafka docs — hunt for a queue post-mortem.
- Module 10 blog URLs need verifying individually; several first-party blogs block scripted fetches.
- Lessons 84–90 and the capstones have no external source by design; they are judgement, drawn from the rest.