Case studies
Case study: URL shortener
The classic warm-up: ID generation, read-heavy caching, 301 vs 302 redirects, and analytics without slowing the hot path.
A URL shortener turns a long link into a short code, such as sho.rt/x7Kp2Qa, and sends anyone who opens that code to the long link. It is the standard interview warm-up. The product is small, but the design still covers estimates, unique IDs, a read-heavy cache and an async analytics path.
The key idea is asymmetry. Redirects happen about 100 times as often as links are created. So the redirect path gets a strict latency budget, and everything else can be slower.
Requirements
Functional
- Create a short link for a long URL, with an optional custom alias and expiry.
- Redirect a short code to its long URL.
- Count clicks per link.
Non-functional
- Redirects: p99 under about 50 ms in one region, with very high availability.
- Codes must be unique.
- Links live for years, 5 or more by default.
- Creates can take hundreds of milliseconds.
The numbers
Assume 100 million new URLs per month and a 100:1 read-to-write ratio.
Seconds per month ≈ 2.6 million (30 days × 86,400 s)
Writes (avg): 100M / 2.6M ≈ 40 creates/s
Writes (peak): 40 × 3 ≈ 120 creates/s
Reads (avg): 40 × 100 ≈ 4,000 redirects/s
Reads (peak): 4,000 × 3 ≈ 12,000 redirects/s
Links (5 years): 100M × 12 × 5 = 6 billion
Storage: 6B × ~500 B ≈ 3 TBCreates at about 120 per second need no special write pipeline. Redirects at about 12,000 per second need horizontal scale and a cache.
Design at a glance
The create path leases or encodes a code, inserts the row and returns the short URL. The redirect path checks the cache, falls back to the database on a miss, emits a click event and returns the redirect. The click event is sent after the redirect decision, so analytics never blocks the user.
Concepts in this topic
| Step | Concept | What it answers |
|---|---|---|
| 1 | Redirect status codes | Does the browser call you again on the next click? |
| 2 | Short code generation | How do you mint unique codes at high volume? |
| 3 | Click counting off the hot path | How do you count clicks without slowing redirects? |
API and data model
POST /api/v1/links
Idempotency-Key: …
{ "url": "https://example.com/a/very/long/path", "alias": null, "expiresAt": null }
→ 201 { "code": "x7Kp2Qa", "shortUrl": "https://sho.rt/x7Kp2Qa" }
GET /x7Kp2Qa
→ 302 Location: https://example.com/a/very/long/pathEvery redirect is a primary-key lookup on code. Keep the links table as the mapping source of truth, and keep click totals somewhere else.
CREATE TABLE links (
code varchar(10) PRIMARY KEY,
long_url text NOT NULL,
owner_id bigint,
created_at timestamptz NOT NULL DEFAULT now(),
expires_at timestamptz,
is_active boolean NOT NULL DEFAULT true
);Key-value stores partitioned by hash(code) fit this access pattern well. Codes from a counter or hash look random enough that hash sharding spreads the load.
Caching
Use cache-aside on code → {long_url, expires_at, is_active}, with a TTL of hours plus jitter. On create or disable, delete the key instead of overwriting it. A viral code can cause a stampede when its key expires, so guard it with single-flight or early refresh (see cache stampede).
Failure modes
| Failure | Effect | Mitigation |
|---|---|---|
| Redis down | Slower redirects, more database load | Fall back to the database; add read replicas |
| Database primary down | Creates fail | Redirects keep working from the cache and replicas |
| Queue down | Analytics lag | Drop the event with a metric; never block the 302 |
| Viral single code | One cache node or shard overloaded | Replicate hot keys, use a CDN, coalesce requests |
Common mistakes
- Counting clicks on the links row. Every redirect then becomes a write to your busiest table.
- Choosing 301 for a product that sells analytics. Browsers may stop calling you at all.
- Tying redirect success to analytics. If the analytics queue is slow, redirects should still work.
Key takeaways
- Redirects dominate, so design the read path first: cache, scale out, optionally use a CDN.
- Seven base62 characters cover billions of links with room to spare.
- Use 302 or 307 when you need click counts or expiry.
- Send click events asynchronously and never let them block a redirect.
Further learning
Alex Xu's ByteByteGo material walks through the same URL-shortener design with clear diagrams for key generation and the redirect path.
ByteByteGo blog (Alex Xu)Explain complex systems with simple terms, from the authors of the best-selling system design book series. Join over 1,000,000 friendly readers. Click to read ByteByteGo Newsletter, a Substack publication.blog.bytebytego.com
The System Design PrimerLearn how to design large-scale systems. Prep for the system design interview. Includes Anki flashcards. - donnemartin/system-design-primerGitHubA concise walkthrough of the four-step interview framework, useful before you practice this case.
Related
Redirect status codes: 301 vs 302
A redirect's status code tells browsers and CDNs whether they may keep the answer, which decides whether your server ever sees the next click.
Short code generation
Three ways to mint unique short codes (hash the URL, count and encode, or hand out random codes) compared by collisions, guessability and coordination.
Click counting off the hot path
Why a redirect should never update a counter on the links table, and how an event queue and batch workers count clicks instead.
Caching
Where to put a cache, how to read and write through it, how to size it from hit-rate math, and how to keep it from serving stale data or melting down.
Back-of-the-envelope estimates
Turn users and features into QPS, storage and bandwidth numbers in two minutes, and use them to decide what the design actually needs.