How would you design a URL shortener like bit.ly?
Store a mapping from a short code to the long URL. Generate codes with a counter encoded in base62 (or random codes with a collision check), keep the mapping in a key-value store, put a cache in front for fast redirects, and return a 301 or 302 redirect. Add rate limits, expiry and analytics as extras.
Explain it simply
A URL shortener is a giant phone book. Each short code like aT3x9 is a name, and the long URL is the number. When someone opens short.ly/aT3x9, the server looks up the name and sends the visitor to the long URL. The hard parts are making codes unique, answering lookups in a few milliseconds and handling millions of clicks.
Example
Start by clarifying requirements, then estimate:
- Functional: create a short link, redirect, optional custom alias, optional expiry.
- Non-functional: redirects under 50 ms, very high availability, reads far more than writes (say 100:1).
- Scale guess: 100 million new links a month is about 40 writes per second and 4,000 redirects per second. Over 5 years that is 6 billion links. At about 500 bytes each, that is around 3 TB.
- Code length: base62 (a to z, A to Z, 0 to 9) with 7 characters gives 62^7, about 3.5 trillion codes, which is plenty.
APIs:
POST /api/links { "url": "https://...", "alias": "optional" } -> { "short": "https://sho.rt/aT3x9Kq" }
GET /aT3x9Kq -> 301 or 302 with Location: https://...How it works
Generating codes. Option 1: a global counter (or ranges of IDs handed to each server so they do not collide) converted to base62. It is unique and short but predictable. Option 2: hash the URL (for example MD5), take 7 characters and check for collisions. Option 3: random 7 characters with a unique constraint in the database, retrying on conflict.
Storage. One table: code (primary key), long_url, user_id, created_at, expires_at. A key-value or wide-column store (DynamoDB, Cassandra) scales easily because every lookup is by key. PostgreSQL with an index also works fine for moderate scale.
Reading fast. Put Redis or Memcached in front. Popular links stay in cache, so most redirects never touch the database. A CDN can even cache redirects at the edge.
Redirect type. 301 (permanent) is cached by browsers, which saves load but hides repeat clicks from analytics. 302 (temporary) sends every click through you, which is better for analytics.
Real-world use
Marketing links, SMS links with limited characters, and QR codes all rely on this. Real services also scan URLs for malware, block abuse with rate limits and store click analytics (country, device, referrer) through an async queue so redirects stay fast.
Common mistakes
- Jumping to microservices before stating requirements and numbers.
- Using a hash without handling collisions.
- Writing analytics synchronously during the redirect, which slows every click.
- Forgetting custom aliases can collide with generated codes.
What to say in the interview
"First I'd confirm requirements and estimate: this is read-heavy, around 4,000 redirects a second. I'd generate 7-character base62 codes from a distributed ID range so they are unique without coordination. The mapping lives in a key-value store keyed by code, with Redis in front for hot links. Redirects return 302 if we need analytics, and click events go into a queue for async processing. Then I'd cover abuse protection, expiry cleanup and replication for availability."
Follow-up questions
- How do you stop people guessing other users' links?
- What happens if Redis goes down?
- How would you shard the database?
- How would you delete expired links efficiently?
Go deeper
Read these once the basics above feel easy.
Use a ticket server or Snowflake-style IDs (time plus machine ID plus sequence) so many app servers generate IDs without a single bottleneck. Shard by a hash of the code so load spreads evenly.
Discuss multi-region replication with eventual consistency (a new link might not resolve in another region for a moment), a bloom filter to cheaply reject codes that do not exist, and stream processing (Kafka plus a column store) for real-time analytics dashboards.