A design interview guide you can run. Low-level and high-level design, arranged as a practice sheet: every pattern gets a folder with the signal that tells you to reach for it, a worked example you can run in dependency-free Java, and three problems to practise on.
Most design prep is a wall of prose you read once and forget, or a pile of solutions you copy without ever writing. This is neither. Every pattern here has a folder: a page that explains when it applies and what an interviewer will push on, plus dependency-free Java you can run, break, and rewrite from scratch.
./run.sh lld/06-state
./run.sh hld/07-aggregation-and-counting
You can run most of it. 27 folders, 200-plus Java files, no Maven, no
Gradle, no dependencies. 23 of the 27 folders ship a runnable Demo, and all 23
compile and run clean on OpenJDK 21; the other four are writing exercises with
nothing to run. A watermark either drops a late event or routes it to a side
output, and you can watch it happen instead of reading a claim about it.
That last sentence is the whole pitch, so here it is happening. Real terminal output from one folder, two sections cut for length and otherwise untouched:
$ ./run.sh hld/07-aggregation-and-counting
==========================================================================
2. Tumbling, sliding and session, over identical events
==========================================================================
Tumbling, one minute. Fixed and non-overlapping, so every click is counted
exactly once and the counts add up to the event total:
[0:00..1:00) ######### 9
[1:00..2:00) ########## 10
[2:00..3:00) # 1
[5:00..6:00) ######### 9
[6:00..7:00) ######### 9
[7:00..8:00) ## 2
[10:00..11:00) ######## 8
[11:00..12:00) ######### 9
[12:00..13:00) ### 3
total across windows: 60 (equals the 60 events)
[ sliding windows omitted ]
Session, ninety second inactivity gap. 13 sessions across 4 users:
user-1 [1:04..1:50 3 clicks] [5:25..7:12 7 clicks] [10:13..10:13 1 click] [12:02..12:02 1 click]
user-2 [0:23..1:45 6 clicks] [5:06..6:52 5 clicks] [10:48..12:08 7 clicks]
user-3 [0:07..2:01 6 clicks] [5:11..7:06 5 clicks] [10:21..10:56 5 clicks]
user-4 [0:12..1:56 5 clicks] [5:56..6:16 3 clicks] [10:07..12:14 6 clicks]
[ sections 3 and 4 omitted: event vs processing time, watermarks ]
==========================================================================
5. At-least-once versus effectively-once at the sink
==========================================================================
60 records, a checkpoint every 20, and the process is killed after 35.
The last complete checkpoint at that point covers 20 records, so 15 get
replayed from the source. Both sinks see identical input.
no crash, two-phase-commit sink : total 60 (4 transactions committed, 0 aborted on restore)
crash, naive sink (increments on every record)
records in stream 60, replayed after restore 15
sink total 75, overcounted by 15
75 direct writes, no transactions
crash, two-phase-commit sink (commits only on checkpoint completion)
records in stream 60, replayed after restore 15
sink total 60, overcounted by 0
4 transactions committed, 1 aborted on restore
Same events, two window types, and the bursts that are invisible in one are obvious in the other. Then a job killed mid-stream: the naive sink is over by exactly the fifteen replayed records, the two-phase-commit sink lands on sixty. None of that is a diagram of a system. It is the system, small enough to read in an evening.
It teaches the trigger, not just the pattern. Knowing what Strategy is has never been the hard part. Hearing "should support multiple ways to…" and reaching for it under a clock is. Both tracks open with a table that maps the sentence an interviewer says to the pattern underneath.
It covers the things prep material skips. Concurrency inside an LLD answer, which decides more rounds than any single pattern. The forty-five minute HLD script, because candidates lose on pacing rather than knowledge. Defending the technologies on your CV. And a timed-drill folder, because recognising a design and generating one are different skills and only the second is tested.
It argues, rather than lists. Every trade-off comes with a recommendation and
a reason. Where there's no right answer, it says so: addChild on Node or on
Directory, orchestration or choreography. Interviewers know, and they are
listening for whether you do.
Backend engineers with a few years of experience preparing for design rounds, who can already write the code and need to get fast at producing designs under observation. If you have never written Java, the patterns still transfer; the code is deliberately plain.
brew install openjdk@21 # if you don't have a JDK; run.sh finds it on its own
./run.sh # lists every folder with runnable code
./run.sh lld/02-strategy # a rate limiter with three swappable algorithmsThen pick a track:
- lld/: 16 folders. Modelling, SOLID, the eight patterns that actually turn up, concurrency, and timed machine-coding drills.
- hld/: 11 folders. The delivery framework, seven scaling patterns, technology deep dives, and building one signature design.
There's also a browser version of the same sheet with progress tracking, live at abhishek-gola.github.io/system-design-playbook. Your ticks save locally in the browser.
Ignore the week numbers on the track pages. They assume a fourteen-week run, and
reading in order puts you in lld/00-modelling when what's about to cost you the
round is pacing and locking. Six folders, in this order:
- hld/00-framework: the forty-five minute clock. Pacing loses more rounds than knowledge does, and this is the cheapest fix in the repo.
- lld/10-concurrency: the double-booking race, reproduced. Any prompt with seats, inventory or a balance is this question wearing a costume.
- hld/05-contention: the same problem across machines. Read it straight after, while the shape is still fresh.
- lld/02-strategy and lld/06-state: the two patterns machine-coding rounds are actually built on.
- hld/01-scaling-reads: the default hard part in anything read-heavy.
Then the two "picking the pattern" tables, at the top of lld/README.md and hld/README.md. Ten minutes, and they carry more per minute than anything else here.
Then three timed drills, 50 minutes each, blank file, no notes: Rate Limiter, BookMyShow, Splitwise. Method and scorecard are in lld/15-timed-drills. If you run short on evenings, cut reading before you cut drills; recognising a design and producing one are different skills and only the second one gets tested.
| Low-level design | High-level design | ||
|---|---|---|---|
| Modelling | relationships before patterns | The framework | the 45-minute script |
| SOLID | a logger refactored five times | Scaling reads | caches, stampedes, single-flight |
| Strategy | a rate limiter | Scaling writes | shard keys and hot partitions |
| Factory | and its two cousins | Real-time updates | connection routing |
| Builder | an order that can't be built wrong | Long-running tasks | queues, idempotency, DLQs |
| Observer | with real backpressure | Contention | holds, locks, fencing |
| State | a vending machine | Multi-step processes | sagas and the outbox |
| Chain of responsibility | a risk pipeline | Aggregation | windows, watermarks, sketches |
| Decorator | ordering changes behaviour | Blobs, geo, search | chunking, geohash, inverted index |
| Adapter | two payment gateways | Technology deep dives | Kafka, Flink, Redis, DynamoDB |
| Concurrency | the race, reproduced | Signature design | one answer nobody else has |
| Singleton · Composite · Command · The rest · Timed drills |
Reading a folder takes ten minutes and teaches you almost nothing. The sequence that works:
- Read the README. Close it.
- Write the pattern from scratch in a blank file. Not from memory of the code, but from memory of the problem it solves.
- Run your version, then diff your thinking against the folder's. The places you differ are the lesson.
- Do one practice problem from the README's list, on a clock.
Step 2 is the one people skip and it's the only one that counts.
Each folder is a flat set of plain .java files with no package declaration and
exactly one class with a main, called Demo.
./run.sh <folder> # compiles that folder and runs Demo
./run.sh # lists every folder with runnable code
If you'd rather not use the script:
javac -d /tmp/out lld/02-strategy/*.java && java -cp /tmp/out Demo.
Output is deterministic wherever the topic allows it: clocks are injected rather than read from the wall, and anything random is seeded. The few genuinely concurrent demos say so and report aggregate counts rather than pretending a race is reproducible.
Code is MIT, written material is CC BY 4.0. See LICENSE. The split is there so you can lift the Java into your own projects without ceremony, and share or adapt the notes with attribution.
The practice problems point at awesome-low-level-design, AlgoMaster and Refactoring Guru. Those are the sources worth reading; this repo is the part you have to write yourself, written out so you can run it first.