Skip to content

Latest commit

 

History

8 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

System Design Playbook

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.

Java Dependencies Code Docs

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

Why this one

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.

Who it's for

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.

Start here

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 algorithms

Then 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.

If your onsite is in under three weeks

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:

  1. hld/00-framework: the forty-five minute clock. Pacing loses more rounds than knowledge does, and this is the cheapest fix in the repo.
  2. lld/10-concurrency: the double-booking race, reproduced. Any prompt with seats, inventory or a balance is this question wearing a costume.
  3. hld/05-contention: the same problem across machines. Read it straight after, while the shape is still fresh.
  4. lld/02-strategy and lld/06-state: the two patterns machine-coding rounds are actually built on.
  5. 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.

What's inside

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

How to actually use it

Reading a folder takes ten minutes and teaches you almost nothing. The sequence that works:

  1. Read the README. Close it.
  2. Write the pattern from scratch in a blank file. Not from memory of the code, but from memory of the problem it solves.
  3. Run your version, then diff your thinking against the folder's. The places you differ are the lesson.
  4. 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.

Running the code

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.

Licence

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.

Credit

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.

Releases

Packages

Contributors

Languages