URL shortener architecture.
The canonical read-heavy system: roughly a hundred reads for every write, and a redirect that has to be fast enough that nobody notices it happened.
The components
Every row below is read from the graph that produced the diagram above, so the two cannot disagree.
| Component | Tier | Why it is there |
|---|---|---|
| Client | Client | Follows a 301 to the long URL |
| CDN | Edge | Caches hot redirects at the edge |
| Load balancer | Edge | Supporting component |
| Shorten API | Application | Allocates a key, writes once |
| Redirect API | Application | Read-heavy, ~100:1 vs writes |
| Key generator | Application | Pre-allocated ranges, no coordination per write |
| Redis | Data | key to URL, the hot path |
| Postgres | Data | Source of truth |
| Click events | Data | Async, never on the redirect path |
| Warehouse | Data | Supporting component |
Design decisions worth arguing about
A diagram shows what was chosen. It does not show what it cost, and that is usually the part that matters in a review or an interview.
Counter ranges beat hashing the URL
Hashing the long URL and taking the first seven characters is the obvious approach and it forces you to handle collisions on every single write, which means a read before each insert. Handing each write node a pre-allocated block of counter values instead removes coordination from the hot path entirely: a node burns through its range locally and only talks to the allocator when it runs out. The cost is that keys are no longer a function of the URL, so shortening the same link twice gives two different keys unless you add a lookup, and gaps appear in the key space whenever a node dies holding an unused range.
301 or 302 changes what you can measure
A 301 is permanent, so browsers and intermediaries cache it and subsequent visits never reach your servers. That is excellent for load and fatal for analytics, because your click counts quietly become click-once counts. A 302 keeps every visit coming back to you and keeps the numbers honest, at the price of serving traffic you could have avoided. Most products want 302 for the analytics and then push the caching problem to a CDN they control.
Analytics must not be on the redirect path
Writing a click row synchronously ties your redirect latency to your analytics database, and it means an analytics outage becomes a redirect outage. Publishing to a log and letting a consumer aggregate into the warehouse keeps the two failure domains apart. What you give up is immediacy: the dashboard is now seconds or minutes behind, and you need a story for what happens to events buffered in a process that gets killed.
The cache is the system
With a hundred-to-one read ratio and a strongly skewed key distribution, a small cache absorbs almost everything. That makes the database sizing question much less interesting and the cache invalidation question much more interesting, particularly around deletes and expiry: a removed link that stays in cache is a link you have failed to remove, which matters when the reason for removal was abuse.
How it changes with scale
The write path barely moves as you grow, because shortening is rare. The read path is entirely about cache hit rate and edge distribution: once the CDN is serving the popular keys, your origin sees only the long tail, and the long tail is where the database work is. The first thing that actually hurts is the analytics pipeline, because click volume grows with reads rather than writes.
Where it breaks first
Cache eviction under a traffic spike. A viral link is a single hot key, and if it falls out of cache every request behind it lands on the same database row at once. The symptom is a redirect p99 that jumps from single-digit milliseconds to hundreds while the average looks fine, which is why this is a problem you find in percentiles and not in a mean.
Draw this yourself
Open the Diagram tab and describe the system. The agent emits a semantic graph rather than coordinates, so you can edit the components and the layout re-solves instead of drifting.
When the shape is right, Implement in code turns the canvas into a markdown specification, every component, every relationship and the notes, and starts a real turn in the Code tab with it.
Questions about this design
More templates
- Chat Application System DesignLong-lived connections change everything: the hard part is not storing messages, it is knowing which of your servers is holding the socket you need to write to.
- Payment System DesignThe only system on this list where being approximately right is indistinguishable from being wrong, and where the provider, not you, holds the truth.
- RAG Pipeline ArchitectureAlmost every RAG system that disappoints is failing at retrieval, not generation, and the architecture is what decides whether you can tell.
- E-commerce Platform ArchitectureFour services with four completely different consistency requirements, which is the whole reason this is not one application.