The whole interview hinges on one decision: how you generate the short key. Get that right (and justify it), handle the read-heavy scale with caching, and you've passed. Everything else — API, storage, redirects — follows a standard template. Below is the 45-minute walkthrough I use to teach this problem.
A URL shortener (think Bitly/TinyURL) maps a long URL to a short one and redirects on lookup. It's deceptively simple, which is exactly why it's a favorite: interviewers use it to see whether you reason about scale and trade-offs rather than just code.
The structure to follow
| Step | Time | Goal |
|---|---|---|
| 1. Clarify requirements | ~5 min | Pin functional + non-functional scope |
| 2. Estimate scale | ~5 min | Justify storage, QPS, read/write ratio |
| 3. API design | ~3 min | Two endpoints, nothing fancy |
| 4. Data model | ~4 min | Pick the store and the schema |
| 5. Core algorithm | ~10 min | How you generate the short key — the crux |
| 6. Scale it | ~10 min | Caching, read path, replication |
| 7. Trade-offs | ~5 min | Name what you'd change at 10× |
1. Requirements
Functional: shorten a long URL → short URL; redirect short URL → original; optional custom aliases; optional expiration. Non-functional: highly available (a dead redirect is a dead link), low-latency redirects (<100 ms), and read-heavy — reads dominate writes by ~100:1. Call that ratio out loud; it drives the whole design.
2. Back-of-the-envelope
Assume 100M new URLs/month.
- Writes: 100M / (30 × 86,400) ≈ ~40 writes/sec.
- Reads at 100:1 ≈ ~4,000 reads/sec.
- Storage: 100M/month × 12 months × 5 years × ~500 bytes/record ≈ ~3 TB over 5 years. Trivial for any modern store — so storage isn't the constraint; read latency and availability are.
3. API
POST /shorten { longUrl, customAlias?, expiry? } -> { shortUrl }
GET /{shortKey} -> 301/302 redirect to longUrl
Use 302 (temporary) if you want every hit to come back to you (analytics); 301 (permanent) if you want browsers/CDNs to cache and spare your servers. That choice is a real trade-off interviewers like to hear discussed.
4. Data model
A single table is enough: short_key (PK), long_url, created_at, expiry, user_id?. The access pattern is a pure key-value lookup by short_key, so a KV store (DynamoDB/Redis-backed) or a well-indexed relational table both work. Lead with the access pattern, then pick the store — not the other way around.
5. The crux: generating the short key
This is where the interview is won or lost. Three common approaches:
| Approach | How | Pros | Cons |
|---|---|---|---|
| Hash (e.g., MD5) + truncate | Hash the long URL, take first N chars, base62-encode | Stateless, deterministic | Collisions — need a check-and-retry loop |
| Random key + collision check | Generate random base62 of length 7 | Simple, unguessable | Read-before-write on collision; degrades as space fills |
| Counter + base62 (recommended) | Auto-increment ID → base62-encode it | No collisions, short keys, O(1) | Keys are sequential/guessable; the counter is a write bottleneck |
Base62 (a-zA-Z0-9) gives 62^7 ≈ 3.5 trillion keys in just 7 characters — plenty. The counter approach is cleanest; the obvious follow-up is "how do you scale the counter without a single bottleneck?"
Answer: hand each application server a pre-allocated range of IDs from a central ticket service (e.g., server A gets 1–1,000,000, server B gets 1,000,001–2,000,000). Each server then mints keys locally with zero coordination, refilling its range when it runs low. This removes the per-write hot spot — and naming it is the moment that separates a strong candidate from an average one.
6. Scaling the read path
Since it's 100:1 read-heavy:
- Cache aggressively. Put the hottest
short_key → long_urlmappings in Redis. URL access follows a power law (a few links go viral), so a cache hit rate above ~90% is realistic; that's what keeps redirects under 100 ms. - Replicate reads. Read replicas / multi-region replicas absorb the read fan-out and keep redirects fast and available worldwide.
- Push to the edge. With 301s, CDNs cache the redirect itself, offloading a large share of traffic before it ever reaches you.
7. Trade-offs to name unprompted
- 301 vs 302 — CDN caching vs analytics fidelity.
- Sequential keys are guessable — fine for public links, a problem if URLs are sensitive; mitigate by interleaving/encrypting the counter.
- Custom aliases complicate the clean counter scheme (now you need a uniqueness check) — call that out.
- Expiration / cleanup — a background job sweeping expired rows, or TTLs in the KV store.
Why this problem keeps showing up
It's small enough to finish in 45 minutes but rich enough to expose whether you can estimate scale, identify the real bottleneck (the counter and the read path, not storage), and discuss trade-offs without prompting. Master this template and "design Instagram / WhatsApp / a pastebin" become variations on the same moves.
Written by Amit Singh — Senior SDE at Amazon, Claude Certified Architect, and founder of AlgoEngineer. We teach this framework live, with mock system-design interviews, in our System Design course.