Case study: URL shortener
Design a URL shortener end to end: code generation without collisions, a cache-carried redirect path, and what happens when the cache or the database fails.
35 min9 steps
By the end you can
- Estimate write rate, read rate, storage and the code length a shortener needs
- Choose a code-generation scheme and justify it with collision and enumerability arguments
- Design a redirect path whose load the cache carries, and predict database load from the hit rate
- Predict how the design behaves when the cache restarts or the database leader fails
Pin down what you are building
Create, redirect, delete links
- Custom aliases; expiry; ownership
Read-heavy
- Durable acknowledged links; bounded redirect latency; abuse controls
Work out the numbers
Creates/s
1,160
100M/day; ~2,300 peak
Redirects/s
116k
100 clicks/link; ~230k peak
Five-year links
183B
100M / 365 / 5
Storage
91 TB
183B / 500 B; ~275 TB replicated
Code length
7
62^7 / 3.5T; 62^6 / 57B
Cache
100 GB
200M / 500 B
Egress
58 MB/s
116k / 500 B
Design the API
POST /v1/urls; DELETE /v1/urls/{code}
- longUrl, customAlias, expiresAt; Idempotency-Key; 201/409/429; owner deletion
GET /{code}
- 301/302 Location; 404/410; 302 with explicit cache policy for analytics
Model the data
Shard by code
- code, longUrl, userId, createdAt, expiresAt; point lookup
Owner index
- SQL secondary index or maintained KV index; conditional alias uniqueness
Draw the high-level design
Put the steps of a redirect that misses the cache in order.
- 1The API server reads the row from the URL store
- 2The API server writes the row to the cache
- 3The load balancer forwards GET /aZ3kQ9x to an API server
- 4The API server looks up url:aZ3kQ9x in the cache and misses
- 5The API server answers 302 with the long URL in Location
Deep dive: generating codes
Allocated counters
- Per-server ID ranges; keyed bijection before base-62; no per-request coordination
Random or truncated hash
- Insert-if-absent + collision retry; URL hashing couples ownership/deletion
Alternatives and their trade-offs
Key-value store
- N=3, R=W=2; maintain owner index
No cache
- Fresh/simple; full read load saturates store
Hash truncation
- Deduplication; collisions and shared ownership
Failure scenarios you can run
Inject
Cache restarts empty
Observe
Miss burst raises tail latency
Result
Coalescing refills hot set
Inject
Viral link: 30%
Observe
Hot link stays cached
Result
Long-tail misses load the store
Inject
Store leader dies: ~500ms
Observe
Cache hits survive; misses/creates fail
Result
300ms detection + 200ms election; semi-sync/fencing
Wrap up and self-assess
Risks
- Abuse, hot cache keys, invalidation on delete
Protect
- Creation quotas, URL scanning, kill switch; size for cold-cache misses
Then score yourself per stage: requirements, estimates, API, data model, high-level design, deep dive and wrap-up, from 1 (named the parts) to 4 (justified every choice with numbers and failure behaviour).
Answer this step to continue.