Layered Architecture
Traditional n-tier architecture pattern
Scalable system design for creating short URLs, redirecting users, and collecting analytics.
Denser dots = higher throughput. Hover any edge for its full contract.
Rough, order-of-magnitude numbers. The point is to justify the architecture, not to be exact.
No system is born with queues and caches. Every box was added because something was about to break. Play a stage and the diagram above shrinks to what existed at that point in the story.
The simplest version of a URL shortener is barely more than a hash map with an HTTP front door: a client posts a long URL, one Application Server computes a code and writes the pair straight into the Database, and reading it back just reverses the lookup. A Load Balancer sits in front from day one — cheap to add now, expensive to retrofit later — even though at this stage it only ever has one target.
This survives a demo, but every redirect is a synchronous database round-trip, and a single Application Server caps how much traffic the whole system can take.
Scale pressure: Redirects outnumber creations roughly 100:1, and every single one was hitting the database directly — read latency and database load both climbed with traffic.
A Redis Cache goes in front of the database on every read, so the hot fraction of links — the ones actually getting clicked — never touch the database at all. A second Application Server joins behind the Load Balancer, which finally has real work to distribute.
Redirects get fast again, but URL creation still races the database for a free code under load, and nothing recycles the codes that fall out of use.
Scale pressure: Generating a collision-free code inline on every write means checking for conflicts under load — and at high write volume, retries start piling up.
A standalone Key Generation Service pre-computes unique Base62 codes ahead of time and hands them out on request, so assigning a code becomes a cheap, guaranteed-unique lookup instead of a check-and-retry loop against the database.
Creation is now fast and collision-free at any write rate — but every click and every new link still disappears without a trace, and expired links just sit in the database forever.
Scale pressure: Product wants click analytics, but nothing in the request path can afford to slow down for it — and the database is quietly filling up with links nobody will ever click again.
Every creation and redirect now fires an event onto a Message Queue without waiting for a response, and an Analytics Service consumes those events on its own schedule. A Cleanup Service periodically sweeps the database for expired links, deletes them, and returns their codes to the Key Generation Service's pool instead of wasting the keyspace.
This is the complete system: every synchronous hop stays on the critical path only when it has to, and everything else happens off to the side.
A diagram shows what exists; a trace shows what happens. Press play on a flow, or click any step, and the diagram above lights up the exact path that request takes.
From submitting a long URL to receiving a shareable short link.
From clicking a short link to landing on the original page.
The background path that reclaims codes from links nobody uses anymore.
Every box on the canvas, and the job it does. Click a card to locate it in the diagram; click the node itself for the full deep dive.
This system turns long URLs into short, shareable codes and redirects visitors back to the original destination. Traffic is heavily read-skewed — redirects vastly outnumber creations — so the architecture optimizes the read path with caching while keeping the write path simple and collision-free.
A client submits a long URL through the API Gateway, which authenticates the request and forwards it through a Load Balancer to an available Application Server. The server pulls a pre-generated, collision-free code from the Key Generation Service and writes the mapping to the Database. The new short URL is returned immediately, and the creation event is logged asynchronously for analytics.
A redirect request follows the same Gateway → Load Balancer → App Server route. The server checks Redis first; a cache hit returns the original URL in about a millisecond. On a miss, it falls back to the Database, returns the result, and repopulates the cache so the next request for that code is fast.
Every creation and redirect event is pushed onto a Message Queue without blocking the response, and an Analytics Service consumes those events for click counts, referrers, and geography. A separate Cleanup Service periodically scans the Database for expired links, deletes them, and recycles their short codes back into the Key Generation Service's pool instead of wasting the keyspace.
An architecture is a record of trade-offs. For every major choice here: what won, what lost, and why the constraints made it so.
Hashing a URL and truncating it reintroduces collisions, forcing a retry loop on every write. Random strings have the same problem at scale. A KGS pre-generates unique codes offline and hands them out on request, so assigning one is O(1) and collision-free by construction — the cost is one extra internal hop and a standby replica for availability.
The access pattern is almost entirely single-key lookups and inserts — shortUrl in, longUrl out. A NoSQL store gives that pattern horizontal scale and predictable low-latency reads without needing joins or transactions the system never uses. A relational database would work at small scale but becomes the first thing to bottleneck as write volume grows.
A 301 tells browsers to cache the redirect permanently and skip the server on future visits, which would break click analytics — a core requirement. 302 forces every click back through the backend so it can be counted, at the cost of a marginally larger response on repeat visits.
With a 100:1 read:write ratio, letting every redirect hit the database directly would make it the bottleneck long before the app tier. A small hot set — roughly 20% of links — drives most traffic, so an LRU cache absorbs the vast majority of reads and keeps p99 redirect latency near a millisecond.
Actively hunting down every link the moment it lapses adds constant background write pressure for no user-facing benefit — storage is cheap, and a stale, unvisited mapping costs nothing sitting idle. A periodic Cleanup Service reclaims codes during low-traffic windows instead, keeping the write path free of cleanup overhead.
Reading a system means sensing where it cracks under 10× load. These are the pressure points of this design, and how it holds.
A single link going viral can drive thousands of redirects per second at one short code, all landing on the same cache key and, on a cold cache, the same database partition.
Redis absorbs the steady-state load since a hot key simply stays resident. For the cold-start case, request coalescing at the app-server layer collapses concurrent misses into a single database read instead of one per request.
The KGS is a single logical component. If it becomes unreachable, application servers can't mint new short codes, so URL creation grinds to a halt even though redirects keep working fine.
A standby KGS replica takes over via leader election, and each app server pre-fetches a small batch of unused keys locally, so a brief KGS blip is invisible to users creating links.
If short codes were assigned sequentially — an auto-increment counter, say — instead of pseudo-randomly, writes and the newest, most-clicked links would all concentrate on the same shard.
Base62 codes from the KGS are effectively randomly distributed, and the database uses hash-based partitioning on the short code itself, spreading both writes and hot reads evenly across shards.
A sudden spike in redirects — a viral link, a bot crawl — can produce click events faster than the Analytics Service can consume them, growing an unbounded backlog on the Message Queue.
Logging is fire-and-forget and off the critical path by design — a growing queue delays dashboards, never redirects. The Analytics Service scales its consumer group horizontally to drain backlog, and Kafka's retention window buys time before any event is lost.
An attacker can script thousands of short-link creations per second to generate phishing URLs that borrow the service's domain reputation, or simply to burn through the key space faster than intended.
The API Gateway rate-limits creation requests per API key/IP, and newly created URLs are screened against a blacklist/safe-browsing check before the mapping is persisted.
Most of this architecture exists because any one box can die. Take a component offline and the diagram above shows the blast radius: what goes down, what degrades, and what the user feels.
No visible impact — the Load Balancer stops routing to this instance and every request goes to the surviving server instead.
Redirects are completely unaffected. Creating new short URLs keeps working too — for a while — until application servers exhaust the small batch of keys they pre-fetched locally.
Redirects keep working, but every request now falls through to the database — p99 redirect latency jumps from about 1ms to roughly 20ms.
New short URLs can't be created at all, and any redirect not already sitting in the cache fails outright — the service degrades to serving only its currently-hot links.
Users notice nothing at all — creation and redirection both keep working exactly as before. Click analytics and dashboards just go stale until the queue recovers.
If you can answer these without scrolling back up, the architecture is yours. Try each one out loud before revealing.
Every great system starts as a sketch on a whiteboard. The ability to zoom out and see the whole picture (how services connect, where data flows, what breaks and why) is what separates engineers who build features from engineers who build systems. This diagram is more than boxes and arrows. It's a map of decisions, trade-offs, and intentional design.
Studying architectures isn't just about passing interviews. It's about training your intuition. The more systems you take apart, the better you get at sensing where a monolith will crack, where a queue belongs, or when a cache is hiding a deeper problem. You start seeing patterns instead of chaos.
So keep reading, keep tracing those edges, keep asking "why this way and not that way." The engineers who truly understand large systems are the ones who never stop being curious about how things fit together. That curiosity is the only ingredient that really matters.
Atharva Arbat@arbat_atharva
Traditional n-tier architecture pattern
A server-side rate limiting system that protects APIs from abuse using sliding window counters and Redis-backed distributed state.
A ranked prefix-completion system that returns the top-K suggestions within the keystroke latency budget using an in-memory precomputed trie and an offline popularity build pipeline.