In a system design interview or a design document, the diagram is where the reasoning becomes visible. A box labeled "cache" is easy to draw; showing why it is there, how much traffic it absorbs and what happens when it is cold is what makes the diagram worth reading.
This guide walks through a classic exercise, a URL shortener, from requirements to a finished system design diagram. The point is not the shortener itself but the method: requirements and numbers first, then the API and the data, then the boxes, with each decision written where people can see it.
Step 1: Requirements
Functional:
- Create a short link for a long URL, optionally with a custom alias and an expiry date.
- Redirect a short link to its long URL.
- Count clicks per link.
Non-functional:
- Redirects must be fast and highly available; creating links can tolerate more latency.
- The system is read-heavy: links are created once and clicked many times.
- Short codes should not be easy to enumerate.
Step 2: Back-of-the-envelope numbers
Assume 100 million new links per month and 100 redirects per link created:
| Quantity | Estimate |
|---|---|
| Writes | 100M / 2.6M seconds per month ≈ 40 per second on average |
| Reads | 100 × 40 ≈ 4,000 redirects per second; plan for 10× peaks, 40,000 per second |
| Links after 5 years | 100M × 12 × 5 = 6 billion |
| Storage | 6 billion × ~500 bytes ≈ 3 TB, before replicas |
| Code length | 7 characters of base62 give 62⁷ ≈ 3.5 trillion codes |
The numbers already shape the design: reads dominate, so the read path gets a cache; 3 TB will not stay on one small database forever, so the data model must shard cleanly; and 7 characters leave plenty of room.
Step 3: API
POST /api/links
{ "url": "https://example.com/a/very/long/path", "alias": "launch", "expiresAt": "2027-01-01" }
→ 201 { "code": "launch", "shortUrl": "https://s.example/launch" }
GET /{code}
→ 302 Location: https://example.com/a/very/long/path
One decision deserves a line on the diagram: 301 or 302? A 301 is permanent, so browsers cache it and repeat visits never reach you. That saves traffic but loses click counts. A 302 sends every click through the redirect service, which is what analytics needs. This design uses 302.
Step 4: Data model and IDs
The core table is simple: code (primary key), long_url, owner_id, created_at, expires_at. Every read is a lookup by code, so either a key-value store or a relational database with a primary key works, and sharding by a hash of code spreads the load evenly.
Generating codes is the interesting part:
| Approach | Pros | Cons |
|---|---|---|
| Counter + base62 | No collisions | Sequential codes are easy to enumerate; needs coordination |
| Random 7 characters | Hard to guess, no coordination | Must check for collisions on insert |
| Hash of the URL | Same URL gets the same code | Collisions on truncation; aliases need special handling |
Here, the write API leases ranges of IDs from an ID range allocator, for example a million at a time, and scrambles each ID with a reversible permutation before base62 encoding, so codes are unique without a collision check and not sequential to an outsider.
Step 5: The diagram
Separate the paths that have different requirements. The write path is low volume and must be correct; the read path is high volume and must be fast; analytics can lag behind.
Diagram contents
- Users
- Load balancer
- ID range allocator
- Write API
- Redirect service
- Cache: code → URL
- Links DB (sharded by code)
- click events
- Analytics workers
- Analytics store
- HTTPS
- POST · 40/s
- GET · 4k/s, peak 40k/s
- lease ID range
- INSERT link
- GET code
- on cache miss
- click event
- consume
- counts
How to read it:
- Write path (top). The load balancer routes
POST /api/linksto the write API, which takes an ID from its leased range, encodes it and writes the link to the database. - Read path (middle).
GET /{code}goes to the redirect service, which checks Redis first. With a hot set that fits in memory, most redirects never touch the database. On a miss it reads the database and fills the cache. - Analytics (bottom). Every redirect emits a click event to Kafka, and analytics workers aggregate counts into the analytics store. The redirect never waits for analytics.
Draw it so the reasoning shows
- Put numbers on the edges.
40/son the write edge and4k/s, peak 40k/son the read edge explain the cache better than any label. - Write assumptions next to the boxes. "Cache hit ratio target 90%", "302 for analytics", "IDs leased in ranges of 1M".
- Separate synchronous and asynchronous. Solid edges for the request path, dashed for click events.
- Show what happens when something fails. If Redis is down, redirects fall back to the database at higher latency; write that on the diagram.
- Keep the estimates on the canvas. A small table of numbers next to the diagram turns it into a design document.
Deep dives worth preparing
- Caching. Hot links follow a long-tail distribution, so an LRU cache with a modest TTL covers most traffic. Invalidate on delete and expiry.
- Database scaling. Read replicas help less here than the cache does; sharding by code keeps writes and storage growing evenly.
- Expiry. Check
expires_aton read and clean up in the background, instead of a giant delete job. - Abuse. Rate-limit link creation per user and check target URLs against malware lists.
Build it in Archboard
- Sign in with Google or GitHub and create a diagram.
- Add a Network pack Internet node for users and a Cloud pack Cloud LB for the load balancer.
- Add the write API, the redirect service, the ID allocator and the analytics workers as Container nodes from the C4 Model pack, with the language in the technology field.
- Add a Data pack Redis for the cache, Postgres for the links database, a Topic for click events and a Data Warehouse for analytics.
- Label edges with operations and rates, make the click events dashed, and put the estimates in a Core Markdown block.
The example uses 11 diagram nodes, including the Markdown block with the estimates, within the Free plan. For the general method behind any architecture diagram, see how to draw a software architecture diagram.
FAQ
How detailed should a system design diagram be in an interview? Enough to show the read and write paths, the data store, the cache and one asynchronous piece. Go deeper only where the interviewer asks.
SQL or NoSQL for the links table? Either works for a primary-key lookup. Pick based on how you plan to shard and what your team runs well, and write the reason on the diagram.
Should I draw a CDN? Only if you cache redirects at the edge. With 302 and click counting, edge caching is limited, so many designs leave it out.
Why not 301 everywhere? A 301 saves traffic but browsers stop asking you, so you lose click data and cannot change the target for repeat visitors.
Where do the numbers go? On the canvas, next to the diagram, so anyone reviewing the design can check the assumptions behind the boxes.