System Design Primer · llms.txt
Written for this site, not part of The System Design Primer. The sources it draws on are listed under Further reading.
Before you draw boxes, you need a rough idea of how big the problem is. Is it 50 requests per second or 500,000? Gigabytes or petabytes? The answer decides whether one database will do, whether you need a CDN, and whether the whole working set fits in memory. Back-of-the-envelope estimation is the habit of getting those numbers in a couple of minutes, with round figures and stated assumptions.
In an interview, this usually happens right after you clarify requirements, in the step the primer calls outlining use cases, constraints, and assumptions. Nobody expects precision. Interviewers want to see that you can turn a vague product description into numbers, keep the units straight, and then let those numbers drive design choices.
The primer's appendix already has the reference tables: the powers of two table and latency numbers every programmer should know. This chapter is about the method that uses them.
Most estimation mistakes are unit mistakes. A few conversions do most of the work.
| Quantity | Exact | Shortcut |
|---|---|---|
| Seconds per day | 86,400 | ~10^5 |
| Seconds per month | ~2.6 million | ~2.5 × 10^6 |
| Seconds per year | ~31.5 million | ~3 × 10^7 |
| 1 million requests per day | ~11.6 per second | ~10 per second |
| 1 billion requests per day | ~11,600 per second | ~10^4 per second |
| 1 Gbps link | 125 MB/s | ~100 MB/s usable |
| KB, MB, GB, TB, PB | 2^10, 2^20, … | 10^3, 10^6, 10^9, 10^12, 10^15 |
Some habits that keep the arithmetic easy:
Work through the same steps every time, and write each assumption on the board as you make it. If the interviewer disagrees with one, you change one line instead of starting over.
Say you're asked to design a photo-sharing app where people post photos and scroll a feed of photos from people they follow. Here are the assumptions, all invented for this example:
QPS.
The split between the last two matters. Image fetches are 20 times more frequent than feed calls, and they are static bytes, so they belong on a CDN rather than your API servers.
Storage.
45 PB of images means object storage. 18 TB of metadata is more than you'd want on one database node, so plan to shard it from the start.
Bandwidth.
No single origin serves 360 Gbps sensibly. That one number justifies the CDN.
Cache. Suppose most feed views are of photos posted in the last week. Seven days × 10 GB of metadata a day = 70 GB. That fits in the memory of a couple of cache nodes (plus replicas), so you can cache all recent metadata and skip the eviction-policy debate.
Servers. Assume one API server handles about 1,000 feed requests per second, since each call is mostly cache lookups. This is an assumption you would measure. 7,500 ÷ 1,000 ≈ 8 servers at peak; run around 12 so you can lose some and still deploy safely.
Now a different shape of problem: the part of a ride-hailing app that tracks where drivers are and answers "which drivers are near this rider?" Assumptions, again invented:
Notice that the input is concurrent users, not DAU. Location updates are driven by how many drivers are online at once, so that's the number to ask for.
Write QPS. 1,000,000 drivers ÷ 4 seconds = 250,000 updates per second at peak. On average, 400,000 ÷ 4 = 100,000 per second.
Read QPS. 20 × 10^6 sessions × 12 queries = 2.4 × 10^8 a day ÷ 10^5 = 2,400 per second on average, and about 7,200 at peak with a peak factor of 3.
This system is write-heavy: around 40 writes for every read. That turns the usual "add read replicas and a cache" advice on its head.
Ingress. 250,000 × 200 bytes = 5 × 10^7 bytes per second = 50 MB/s, about 400 Mbps. Modest.
Memory for current locations. 1,000,000 drivers × 40 bytes = 40 MB of raw data. Per-key overhead in an in-memory store and a geospatial index can multiply that several times; call it 200 bytes per driver, so 200 MB. It fits easily on one machine.
So the dataset is tiny, and the hard part is the write rate. If you assume one in-memory store node handles on the order of 100,000 simple writes per second (another assumption to verify), 250,000 per second needs at least three nodes, and you'd want more for headroom. Shard by geography, such as city or map cell. You're sharding for write throughput and locality, not capacity, and a "nearby drivers" query then usually touches one shard.
Location history. If you keep every update for trip records and analytics: 100,000 per second × 40 bytes = 4 MB/s. Multiplied by 10^5 seconds, that's about 400 GB a day (the exact figure is 4 MB × 86,400 ≈ 346 GB). Over a year, roughly 126–146 TB depending on which figure you use. That doesn't belong in the database serving live lookups. Append it to a log such as Kafka (see event streaming) and write it out in batches to cheaper storage.
An estimate with a misplaced exponent is worse than none, because you'll defend it. Before moving on, check it a second way.
Estimates are only useful if they change a decision. These per-node figures are rough starting points that vary a lot with hardware and workload. State them as assumptions, then compare.
| If you find… | Rough threshold (assumption) | It suggests… |
|---|---|---|
| Peak QPS for stateless servers | ~1,000–10,000 simple requests/s per server | Server count behind a load balancer |
| Many more reads than writes | Read:write of 10:1 or more | Read replicas and caching |
| Write rate above one primary | Low thousands of writes/s is a safe assumption for one relational primary | Sharding or a write-optimized store |
| Data beyond one node | More than a few TB per database node | Sharding; blobs go to object storage |
| Egress in tens of Gbps | More than a few servers' network cards | A CDN |
| Hot set fits in RAM | Tens to hundreds of GB | Cache all of it, or use an in-memory store as the primary |
| Latency budget | ~0.5 ms per round trip inside a data center | How many sequential calls one request can make |
The two examples above came out quite differently. The photo app is dominated by bytes: storage and egress. The location service is dominated by small, frequent writes. The estimation step told you that before you designed anything.
500 × 10^6 ÷ 10^5 = 5,000 per second on average, and 15,000 at peak. The exact average is 500,000,000 ÷ 86,400 ≈ 5,790, so the shortcut is about 14% low. That's smaller than the uncertainty in the peak factor itself, so it rarely changes a decision.
50,000 views per second × 2 MB = 100 GB/s, or about 800 Gbps on average instead of 120 Gbps. That's nearly seven times the bandwidth bill and slower loads for users. Response size multiplies directly into egress, which is why image resizing is usually worth its storage cost.
Location updates are sent on a timer while a driver is online, so the write rate depends on how many are online at the same moment, not how many use the app in a day. A driver who works a ten-hour shift sends 9,000 updates. One who opens the app for ten minutes sends 150. DAU would hide that difference.
1 million videos × 50 MB = 50 TB a day, plus 9 million photos × 2.5 MB = 22.5 TB, for about 72.5 TB a day, nearly three times the photo-only figure. Videos are a tenth of uploads but about 70% of the bytes. Call that out, because it suggests separate storage tiers, transcoding work and a different CDN profile for video (see video streaming).
Probably not. 3 TB across three replicas is about 1 TB per copy, and 500 writes per second is well within what a single relational primary usually handles. You'd say so, and say what would change your mind: faster growth than assumed, or a write rate approaching a few thousand per second. Choosing a shard-friendly key early keeps that move cheap later.
Previous: Design an online chat · Next: Consistent hashing