Requirements
- Shorten a long URL, redirect a short URL, optional custom alias and expiry, click analytics.
- Read-heavy, roughly 100:1. Redirect p99 < 50 ms. High availability matters more than strict consistency.
- Estimate: 100M new URLs/month ≈ 40 writes/s, ≈ 4k redirects/s average. Over 5 years that's 6B URLs × ~500 B ≈ 3 TB.
High-level design
Key decisions
Short code generation
| Option | Pros | Cons |
|---|---|---|
| Hash (MD5/SHA) + take 7 chars | Stateless | Collisions to handle; same URL → same code |
| Counter → Base62 | No collisions, short | Needs a distributed counter; codes are guessable |
| Pre-allocated ID ranges per node | Fast, no coordination per request | Gaps when a node dies (fine) |
| Snowflake-style IDs | Time-ordered, decentralised | Longer codes |
7 Base62 characters give 62⁷ ≈ 3.5 trillion codes, which is plenty.
Redirect: 301 (permanent, cached by browsers, which cuts load but loses analytics) vs 302 (every click reaches you). Most shorteners with analytics use 302.
Analytics off the hot path: emit the click event asynchronously. Never write to the analytics DB inside the redirect request.
Deep dives
- Hot links: cache at the CDN and in Redis. Use LRU with long TTL, since URLs are immutable.
- Abuse: rate-limit creation per API key or IP, and scan targets for malware.
- Expiry: lazy delete on read, plus a background sweeper.
Sources & further learning
Videos, courses, docs and books I recommend for this topic.
Related topics
How to Approach Any System Design
A repeatable framework — requirements, estimates, API, data model, high-level design, deep dives, trade-offs — plus the numbers every architect should know.
Caching Strategies
Cache-aside, read/write-through, write-behind; TTLs, invalidation, stampede protection and multi-layer caches (browser → CDN → app → DB).
Sharding & Partitioning
Split data across nodes so storage and throughput scale horizontally — choosing shard keys, range vs hash, hot spots and rebalancing.