Case study
System Design Playground
Sketch a system, send traffic, watch it break, then fix it. A deterministic simulator in Go with a hand-drawn React canvas.
- Status
- Open source, MIT
- Engine
- Go, deterministic discrete-event
- Components
- 23 component types
- Challenges
- 108 guided scenarios
- 108Guided challengesEach one starts broken and teaches one kind of failure.
- 6-8%Gap to queueing theorySimulated waits match textbook formulas, so the lessons can be trusted.
- ~330xFaster than real time30 simulated seconds of a busy system run in about 90 ms.
The problem
System design is usually taught with static boxes and arrows. You never see a queue fill, a connection pool run dry or a retry storm build, which is exactly what interviews and production incidents are about.
I wanted a place to build a design and then stress it: send traffic, crash a node, and learn from the failure instead of memorising a diagram.
Architecture
- React canvas UI: zustand · roughjs · inspector
- WebSocket session: live edits · play · chaos
- Discrete-event engine: event heap · stations · 100 ms ticks
- REST API: validate · run · scenarios · share
- Sanitiser: untrusted diagrams
- Scenarios as data: start must fail, solution must pass
- Designs store: rate-limited
Explore the stages
Decisions that mattered
Make the simulator trustworthy
- Situation
- A toy simulator that is wrong teaches the wrong thing.
- What I decided
- Check queue waits against known queueing formulas, and make every run repeatable: same design and seed, same result.
- Result
- Waits land within 6-8% of theory, and any odd result can be reproduced and debugged.
Show why retries can kill a system
- Situation
- Many outages are not the first fault, but the retries that follow.
- What I decided
- Model it faithfully: one node slows down briefly while clients retry straight away into a deep queue.
- Result
- That system never recovers. With backoff, no retries, a short queue or newest-first service, it recovers fully. This became a challenge.
Live, fast and safe to share
- Situation
- Edits must show up instantly, and anyone can send a design.
- What I decided
- The server owns the simulation. Every shared design is cleaned and size-limited, and sharing is rate-limited.
- Result
- It feels live, and untrusted input never reaches the engine.
What I optimise for
- Same design and seed give the same result.
- The server decides, the browser only shows.
- Treat every incoming design as hostile.
- Teach by breaking things, then fixing them.
Known gaps and what is next
It models traffic and capacity, not product data flows like feeds, chat or video.
Next Add fan-out, publish-subscribe and blob-storage blocks.
Service times are smoother than real services.
Next Make realistic, spiky times the default.
Screen-reader use has not been tested by a person yet.
Next Run the scripted pass and fix what it finds.
What I would change today
- From the project's own gap analysis: it models request flow and capacity well, but not data-flow questions such as feeds, chat or video pipelines. Those need new building blocks (fan-out, pub/sub, blob storage).