A URL shortener is deceptively simple on the surface but forces you to make real decisions about ID generation, caching strategy, and read-heavy scaling. The core challenge: generate globally unique short keys at scale, serve redirects in under 50ms, and handle a 100:1 read-to-write ratio without melting your database.
Practice this design with AI
Get coached through each section in a mock interview setting
A URL shortener maps long URLs to short aliases and redirects users. The interesting engineering is in the read-heavy access pattern and the uniqueness constraint on short keys.
Start with 100M DAU. About 1% create a short URL on any given day, so ~1M writes/day.
Key takeaway: this is a read-heavy system with modest storage needs. The engineering challenge is latency, not throughput.
Three endpoints cover the core functionality. Auth via API key in the X-API-Key header. Rate limiting headers on every response.
/api/v1/urls/{alias}/api/v1/urls/{alias}/api/v1/urls/{alias}Skip the List URLs and Update URL endpoints in an interview unless asked. They add API surface without revealing architectural insight.

High-Level Architecture
Here is how a request flows through the system.
1. Client POSTs to the load balancer with a long URL. 2. Load balancer routes to one of N stateless app servers. 3. App server generates a unique short key (Base62-encoded ID from a distributed counter in Redis). 4. App server writes the mapping to the primary database (PostgreSQL). 5. App server writes the same mapping to the Redis cache. 6. App server returns the short URL to the client.
1. Client hits short.ly/x7Kp2 through the load balancer. 2. App server checks Redis for the alias. Cache hit? Return a 302 redirect immediately. 3. Cache miss? Query PostgreSQL, populate the cache, then redirect. 4. Asynchronously increment the click counter (fire-and-forget to avoid adding latency to the redirect).
The entire read path should complete in one Redis lookup for the common case. That is how you hit the 50ms p99 target.

Detailed Component Design
Three components deserve a deep look: the ID generator, the cache layer, and the database.
Why these choices matter: the entire design optimizes for the read path. Writes are infrequent and can tolerate higher latency. Reads must be fast, so the cache sits in front of everything.

Data Model & Database Design
PostgreSQL is the right choice here. The data is relational (alias uniquely maps to a URL), the dataset is small, and you get ACID for free. NoSQL adds complexity without benefit at this scale.
Why a separate url_stats table?

Deep Dives
Deep Dive 1: Base62 vs. Hashing for Key Generation
Problem: You need globally unique, short, URL-safe keys at ~1M/day.
Approach A - Hash and truncate: Take MD5(long_url), grab the first 7 characters. If it collides, append a counter and rehash. This is what bit.ly originally did.
Approach B - Sequential ID + Base62: Use an atomic counter (Redis INCR), encode the integer as Base62.
Tradeoff: sequential IDs are guessable. If that matters, apply a bijective shuffle (like a Feistel cipher) to the ID before Base62 encoding. Same uniqueness guarantee, but the output looks random.
Deep Dive 2: 301 vs. 302 Redirects
Problem: Which HTTP status code should the redirect use?
301 (Moved Permanently): The browser caches the redirect. Subsequent clicks never hit your servers. Great for reducing load, terrible for analytics - you cannot count clicks you never see.
302 (Found / Temporary): The browser always checks with your server first. Every click is tracked, but your servers handle every request.
Use 302 by default. Analytics are the entire business model for most URL shorteners. If a specific use case needs maximum performance and does not care about click tracking, offer 301 as an opt-in flag on the API.
Deep Dive 3: Handling Hot Keys
Problem: A viral tweet contains your short link. Suddenly one key gets 100K requests/second.
Approach: The Redis cache already handles this for the read path. But if a single Redis key gets hammered, even Redis can struggle (hot key problem).
Solution: replicate hot keys across multiple Redis nodes. Use a client-side approach: append a random suffix (e.g., alias:1 through alias:10) and distribute reads across these replicas. The app server picks a random suffix on each request.
Alternatively, put a CDN in front of the redirect endpoint. Configure the CDN to cache 302 responses for 5-10 seconds. This absorbs traffic spikes without losing long-term analytics accuracy.
Key Trade-offs:
Our AI interviewer will test your understanding with follow-up questions
Start Mock Interview