System Design · Design Question
Design a URL Shortener
A URL shortener turns a long web address into a compact link; TinyURL and Bitly are real examples.
Concrete example: https://short.ly/aB3xY9 redirects to a long product URL. Creation is one write; millions of redirects hit cache first because reads are ~100× writes.
1. Clarify requirements
Functional
- Given a long URL, return a short URL (custom alias optional).
- GET short URL → 301/302 redirect to long URL.
- Optional: expiry, basic analytics (click count).
Non-functional
- Low latency redirects (p99 under a few ms at edge/cache).
- High read:write ratio (redirects dominate).
- Short codes hard to guess; no collisions served wrongly.
Back-of-envelope (example)
2. API
3. Short code design
Alphabet: [a-zA-Z0-9] = 62 characters. Length 7 → 62^7 ≈ 3.5e12
possibilities — enough for years at 100M/month.
| Approach | How | Trade-off |
|---|---|---|
| Hash + truncate | hash(longUrl), take 7 chars, retry on collision | Simple; need collision handling |
| Base62(counter) | Distributed ID → encode | No collision; IDs predictable unless salted |
| Random 7 chars | Generate, insert if free | Simple; retries under load |
Interview favorite: pre-generate a Snowflake-like ID, then Base62-encode it. For custom aliases: unique constraint; reject if taken.
4. Data model
5. High-level design
6. Deep dives
Redirect path
This is the hottest path. Cache code→URL in Redis with a TTL. Use 301 if you want browsers/CDNs to cache the redirect; 302 if every click must hit you for analytics. Many systems use 302 + async click event to a queue.
Collisions
Analytics
Do not increment click_count synchronously on the redirect hot path at
huge scale. Emit click(code, ts, meta) to Kafka/SQS; aggregate
asynchronously.
7. Scaling
- Stateless URL services behind LB.
- Redis cluster for redirect cache; DB read replicas if cache miss storm.
- Shard DB by code hash when single primary cannot hold data/writes.
- CDN optional in front for 301 caching of popular codes.
8. Edge cases
- Expired codes → 410 Gone; purge cache.
- Malicious long URLs → abuse detection, malware blocklists.
- Hot codes (viral) → cache + CDN; protect DB.
- Same long URL shortened twice → return existing code or create new (product choice).
- Custom alias races → unique constraint; loser gets 409.
9. Failure modes
- Cache outage causes a DB miss storm → rate-limit misses and use read replicas.
- DB write succeeds but cache fill fails → redirect still works by reading DB.
- Analytics queue is down → keep redirects available; buffer or drop non-critical click events.
Interview talking points
- Lead with: State the read-heavy workload, then optimize the redirect path.
- Clarify write QPS vs redirect QPS; redirects dominate.
- A 7-character Base62 code is usually enough; handle collisions explicitly.
- API: POST create, GET redirect, optional stats/delete.
- Cache code→long_url on the hot path; async analytics.
- 301 vs 302 depends on caching vs counting every click.
- Scale: LB + Redis + shard DB by code when needed.
- Custom aliases = unique constraint + reject on conflict.