Skip to content

System design

Wire up clients, servers, caches, queues and databases, then run real traffic through them. Each case teaches one trade-off.

Case studies

  1. Case 14 targetsA URL shortenerTurn long links into short ones and redirect millions of clicks a day: a read-heavy service where a cache does most of the work.
  2. Case 24 targetsAn API that survives a spikeA public API normally handles 2,000 requests a second, until a launch quadruples it for a few seconds. Serve what you can, fast, instead of serving everyone slowly.
  3. Case 36 targetsA news feedEvery post is copied into ten followers’ feeds. Keep posting fast and feeds quick to load, and decide how stale a feed may be.
  4. Case 44 targetsA chat serviceSeven requests in ten send a message. Writes can’t be spread over read replicas, so the database has to be split by conversation.
  5. Case 56 targetsA replicated key-value storeStore keys on shards with replicas, keep serving when a primary dies, and keep stale reads rare.
How the simulator works
  • Every request is simulated one by one, with seeded randomness, so the same design and seed always give the same numbers.
  • Each server is a queue in front of a few workers: when requests arrive faster than the workers finish them, the queue grows and latency climbs long before the server is “full”.
  • The cache is a real LRU or LFU cache that evicts keys, spread over nodes by consistent hashing; shards are placed the same way. The rate limiter is a token bucket.
  • Replicas apply writes a little late, so a read there can return an old value: that is what “stale reads” counts. Writes that skip the cache leave stale copies in it too.
  • Costs are rough monthly prices per machine, to compare designs rather than to budget.