Ashok G

System Design · Draft

19 — Worked designs

These three examples apply the whole track, in rising order of difficulty.

The process

  1. Requirements. What it does, and what scale and latency it needs.
  2. Estimates. Requests, storage and bandwidth.
  3. API. The main operations.
  4. Data model. Access patterns first, then storage.
  5. Topology. The boxes and how requests flow.
  6. Deep dives. The hardest parts.
  7. Failure. What breaks, and what happens then.

A URL shortener

Requirements. Create short links, and redirect quickly. 100 million redirects a day, and 1 million new links.

Estimates. Reads outnumber writes 100 to 1. Peak reads are about 3,500 a second. At 500 bytes per link, storage grows about 180 GB a year.

API. POST /links creates a link, and GET /{code} redirects.

Short codes. Seven base-62 characters give 627≈3.562^7 \approx 3.5 trillion codes.

Random codes would collide: by the birthday paradox, a 50% chance of some collision arrives after only about 2.2 million codes. So random generation needs a uniqueness check. An alternative encodes a unique counter, which can’t collide, but the codes are guessable. Encoding a shuffled counter avoids that.

Data. Lookups are by key only, so a key-value store fits, though a relational database would also handle this volume.

Caching. Popular links dominate (chapter 6), so a cache serves most redirects, and a CDN can answer many before they reach the service.

Redirect type. A 301 is cached by browsers, which reduces load but hides clicks. A 302 keeps every click visible.

Failure. Reads survive a database outage through the cache. Analytics go through a queue, so that tracking never slows a redirect.

A news feed

Requirements. Users follow others, and see recent posts. 100 million daily users, reading far more often than posting.

The central decision is when to do the work.

ApproachHow it worksTrade-off
Fan-out on writeOn posting, add it to each follower’s feedFast reads, but slow, costly writes
Fan-out on readFetch from followed accounts at read timeCheap writes, but slow reads
HybridFan out for most, merge celebrities at read timeBest of both, but more complex

A celebrity with 100 million followers makes pure fan-out on write unworkable: one post means 100 million writes. So the hybrid is the usual answer. Each user has a precomputed feed in a cache such as Redis, holding post IDs. Celebrity posts are merged in when the feed is read.

Flow.

  1. A post is saved, and an event goes to a log stream.
  2. Fan-out workers look up followers, and add the post ID to their feeds.
  3. A read fetches feed IDs, merges celebrity posts, and loads post details from a cache.

Partitioning. Posts by ID, and social graph by user (chapter 9).

Consistency. Feeds can lag by seconds, which is fine. A user’s own post should appear for them at once, so it’s added at read time.

Failure. If fan-out falls behind, feeds lag, and the system stays up. Rising consumer lag is the signal to watch. A cold feed can be rebuilt on demand.

A chat system

Requirements. One-to-one and group chat, low latency, messages kept in order, delivery to offline users.

Connections. HTTP polling is wasteful, so clients hold a WebSocket. Now servers are stateful: a user’s connection lives on one server. So a session registry, usually in Redis, records which server holds each user.

Sending a message.

  1. The sender’s server stores the message durably.
  2. It acknowledges the sender.
  3. It looks up the recipient’s server, and forwards the message.
  4. If the recipient is offline, it sends a push notification.

Ordering. Timestamps from different machines can’t set order (chapter 11). A per-conversation sequence number, assigned at one place, gives each conversation a clear order, and partitioning by conversation ID keeps that simple.

Delivery. Networks retry, so messages can arrive twice. A client-generated ID lets the receiver ignore duplicates (chapter 13). Each client tracks the last sequence number it saw, and asks for anything newer on reconnecting.

Storage. Messages are written once, and read in order by conversation, which suits a wide-column store like Cassandra, partitioned by conversation and sorted by time.

Groups. Small groups can fan out on write. Huge channels are closer to the news feed problem.

Failure. When a chat server dies, its clients reconnect elsewhere, and catch up by sequence number. Mass reconnection is a real risk, so clients need jittered backoff (chapter 14), or a recovering server gets stampeded.

Patterns across all three

ThemeWhere it appears
Read-to-write ratio drives designAll three
Precompute or compute at readFeed fan-out, caching
Hot keysViral links, celebrities, huge channels
Asynchronous workAnalytics, fan-out, notifications
IdempotencyLink creation, message delivery

A design checklist

  1. Numbers. Read-to-write ratio, data volume, latency target and peak load.
  2. Data. Access patterns, storage engine and partition key.
  3. Topology. Stateless compute, with caches before read bottlenecks and queues before slow writes.
  4. Consistency. Which data needs strong guarantees, and which can be stale.
  5. Failure. What happens when each dependency slows down tenfold.
  6. Overload. Timeouts, retry budgets, circuit breakers and load shedding.
  7. Code boundaries. Domain logic separated from storage and transport.
  8. Concurrency. Where state is shared, and who owns it.
  9. Operations. Metrics, alerts, rollback and capacity.

The takeaway

Design is mostly a sequence of trade-offs driven by a few numbers. The same ideas recur at every scale, and the simplest design that meets the real requirements is usually the right one.