The Design Playbook

Design a URL shortener

It looks like a toy: paste a long link, get a short one. Yet inside this toy live estimation, hashing, caching, and a redirect decision worth real money. One hour from now you could design it on a whiteboard without blinking.

Step 1 — requirements
  1. Functional: given a long URL, return a short code; given the code, redirect to the long URL fast.
  2. Scale: 100M new URLs per day, read:write ratio ≈ 10:1 — redirects massively outnumber creations.
  3. Retention: keep records for 5 years. Non-functional: redirects must be near-instant, and the service must not lose mappings.
Step 2 — the envelope

Writes: 100M per day ÷ 86,400 ≈ 1,160 per second — trivial for one database. Reads: 10× that ≈ 11,600 per second — worth a cache, still not scary. Storage: ~500 bytes per record × 100M ≈ 43 GB per day; over 5 years ≈ 78 TB. Conclusion: a small fleet of stateless web servers, one serious database, and a cache in front. No sharding required — yet.

Step 3 — the sketch

The API is two endpoints: POST /api/shorten takes a long URL and returns a code; GET /{code} looks the code up and issues a redirect. The data model is one table: code, longUrl, createdAt, expiresAt. Behind the API: stateless web servers, a cache holding the hottest codes, and one database that owns the truth. That is the whole system.

Generating the code, honestly

Option one: a unique counter (from the database or a dedicated service) encoded in base62 — 0–9, a–z, A–Z. Seven characters give 62⁷ ≈ 3.5 trillion codes, no collisions ever, and codes are short. Option two: hash the long URL (MD5 or similar) and truncate the first 7 characters — no central counter, but collisions are possible, so you must check the database and retry with a suffix. The counter is simple and central; the hash is stateless and needs collision handling. Both are defensible — the defense is the point.

The redirect is a business decision

A 301 redirect is permanent — browsers cache it and may never ask your server again: your load drops, but your click analytics go blind. A 302 is temporary — every click passes through you: you count everything, and you carry the load. Neither is wrong; pick on purpose. If the product is analytics, it is 302. If it is a pure redirect utility, 301 is a gift. And whichever you choose, the cache sits between the load balancer and the database, holding the hot 20% of codes that drive 80% of redirects.

WRITE — create a short linkClientAPIvalidate & orchestrateCode generatorbase62Databasecode → long URLPOST /shortenmint codestoreREAD — open a short linkClientAPICachehot linksDatabaseGET /abc123lookupon miss302 redirect ← long URL
Write path: POST → code generation → database. Read path: GET → cache → (miss) → database → 302 redirect.
The principle

A tiny system, and every big idea is present: estimation told you the shape, hashing made the codes, caching carried the reads, and the trade-offs — counter versus hash, 301 versus 302 — were decided by arguments, not answers.

Quick check

Why does a URL shortener with 100M writes a day still not need sharding — and what is the one thing that would change your mind? (1,160 writes/s and 78 TB over five years fit one well-provisioned database with replication; you shard when writes per second or total size cross what one machine can absorb — the numbers decide, not the vibes.)

Takeaway

Run the playbook end to end and the “hard interview question” becomes routine: scope two endpoints, estimate 1,160 writes and 11,600 reads, draw four boxes, then spend your energy where the real arguments live — code generation and redirect semantics.

The Design Playbook