Courses / System Design Interview / Chapter 2
Module 1 · Foundations · Chapter 2 of 39
Back-of-the-Envelope Estimates
Builds on What System Design Is: From One Server to Millions. Next up: Abstractions That Hide the Network.
Part 1 · Why Estimate
Numbers decide the design
8:10 pm on the night of a cricket final. On a food delivery app, the checkout button spins and then fails for nearly two of every three orders. The dashboards are puzzling: the checkout servers’ CPUs sit at 25%. The team had sized checkout for an ordinary Friday dinner, with room to spare. Tonight orders arrive twice as fast, and the payment gateway, swamped by every other app, takes 3 seconds a call instead of 1. All 200 checkout threads are waiting on it. Nothing is broken. Two numbers multiplied together a week earlier would have shown that 200 threads could never cover tonight.
An estimate turns vague words like "lots of users" into numbers you can design against: requests per second, storage, bandwidth, servers and money. Engineers make them before a launch, before a sale or a final, in design reviews, and when a cloud bill jumps. You need the right order of magnitude (the nearest power of ten), not an exact answer, because designs change at each factor of ten. As a rough guide:
| What the estimate says | What the design probably needs |
|---|---|
| Up to about 1,000 requests a second, under 1 TB | Two app servers behind a load balancer, one relational database with a standby, backups |
| Reads in the thousands to tens of thousands a second | More app servers, a cache for hot reads, perhaps read replicas; writes still go to one primary |
| Writes past roughly 10,000 a second, or tens of TB in one database | Partition (shard) the data, or use a store built to spread writes |
| Photos, video or other large files, or users on several continents | A blob store and a CDN for the files; more regions for latency or regional outages |
Each band starts from a different number: reads bring a cache, writes bring sharding, file sizes bring a blob store. The rest of the chapter sizes the food app for its final night, one number at a time.
Rounding and stating assumptions
Estimates happen in design reviews, chat threads and capacity plans, usually without a spreadsheet, so make the arithmetic easy. Round every input to one or two significant figures and work in powers of ten: 86,400 becomes 10⁵, and 2.6 million becomes 2.5 × 10⁶.
Then write each assumption down before you use it: "20 million daily users, 30 requests each." A reviewer can correct one input without redoing your work, and when real traffic arrives you can see which assumption was wrong. Five habits keep the arithmetic honest:
- Write the formula first, then put numbers in, keeping the unit on every value.
- Keep reads and writes separate. They often differ by 10× or 100× and are handled by different parts.
- Round in whichever direction keeps the maths easy, and note which way you rounded.
- Sanity-check the result against something you know: does 20 PB sound like a startup or a giant?
- Work out only the numbers that change a decision; replace the rest with a stated assumption.
- 1Usersdaily active users, and how many are online at the peak
- 2Actionswhat each user does per day, reads and writes counted separately
- 3Requests per secondthe average first, then the peak
- 4Storageitems per day × size × days kept × copies
- 5Bandwidthrequests per second × size, converted to bits for network links
- 6Servers, memory and poolspeak load ÷ what one machine handles; threads and connections in flight
- 7Moneyone line: which part of the bill dominates
Where real numbers exist, use them: last month’s peak requests per second, bytes per request from the logs, a load test of one server. The formulas stay the same; only the inputs get better.
Part 2 · Numbers to Carry
Units and sizes
Every storage and bandwidth estimate starts from units and the size of one item. Computers count bytes in powers of two, but powers of ten are easier to multiply, and 2¹⁰ = 1,024 is within 3% of 10³, so each unit is roughly a thousand times the last.
Strictly, 1,024 bytes is a kibibyte (KiB). The gap grows to about 13% at a petabyte, less than the usual error in an estimate. Indian numbers need one more conversion: 1 lakh = 10⁵, 10 lakh = 10⁶, 1 crore = 10⁷ and 100 crore = 10⁹. Convert before multiplying; switching systems halfway is a classic slip.
These item sizes are worth knowing by heart. The food app’s own items sit in the middle of the table; when unsure, round up and say so.
| Thing | Rough size |
|---|---|
| One character of English text (ASCII) | 1 byte |
| One character of Hindi or Tamil text (UTF-8) | 3 bytes |
| An integer id or a timestamp | 8 bytes |
| A UUID | 16 bytes, or 36 characters as text |
| A rider’s location ping, with ids and time | about 100 bytes |
| An order with its items and status history | about 2 KB |
| A dish photo sized for a phone screen | about 100 KB |
| A photo straight from a phone camera | about 3 MB |
| One minute of HD video at 5 Mbps | about 40 MB |
Two habits make the table useful. Count the metadata (ids, timestamps, indexed fields): in a 100-byte ping, the position itself is only 16 bytes. And check media against a bitrate: 5 Mbps is 5 ÷ 8 ≈ 0.6 MB a second, so a minute is about 40 MB.
Time: the 10⁵ trick
With sizes in hand, the next conversion is time. A day has 86,400 seconds. Round it to 10⁵ and any per-day count becomes a per-second rate by dropping five zeros. The answer comes out about 14% low, well inside estimation error.
| Per day | Per second, exact | Per second, rounded |
|---|---|---|
| 1 million | 11.6 | ≈ 12 |
| 10 million | 116 | ≈ 120 |
| 100 million | 1,157 | ≈ 1,200 |
| 1 billion | 11,574 | ≈ 12,000 |
The first row is the one to memorise: a million a day is about a dozen a second. Every other row is the same fact with zeros added. For the food app, 2 million orders a day is about 23 a second, and 600 million requests a day about 7,000. Working backwards is just as quick: 500 requests a second is roughly 40 to 50 million a day.
People sleep and eat at the same times, so most traffic lands in about 16 waking hours and much of it in a few busy minutes. Dividing by the whole day gives the average; the next part turns it into the peak you design for.
The latency ladder
Rates say how much work arrives; the latency ladder says how long each piece takes, which depends on where the data lives. You need the ladder in your head, not exact figures. To make the gaps feel real, the last column stretches time so that 1 nanosecond becomes 1 second.
| Operation | Rough time | If 1 ns were 1 second |
|---|---|---|
| Read from the CPU’s L1 cache | about 1 ns | 1 second |
| Read from main memory (RAM) | about 100 ns | under 2 minutes |
| Random read from an SSD | about 100 µs | about a day |
| Round trip inside one data centre | about 0.5 ms | about 6 days |
| Seek on a spinning hard disk | about 10 ms | about 4 months |
| Round trip, Mumbai to Delhi | roughly 30 ms | about a year |
| Round trip, Mumbai to the US east coast | about 200 ms | about 6 years |
The lessons fall out of the ladder. Memory is about 1,000× faster than an SSD, which is why caches exist: the food app’s menu cache answers in under a millisecond from the same data centre, while a database query that misses it costs tens of milliseconds. One cross-continent round trip costs as much as 400 inside a data centre, so keep chatty calls inside one region and put data near users.
Real hardware varies. A fast NVMe drive beats 100 µs, and a busy network is far slower than 0.5 ms. Quote these figures as orders of magnitude, which is all an estimate needs.
Light in optical fibre travels about 200 km per millisecond. Every 1,000 km between a user and a server adds at least 10 ms to each round trip, before any server does any work.
Part 3 · Traffic, Storage and Bandwidth
Requests per second, average and peak
With units and time settled, the most common estimate is QPS (queries per second, used loosely for requests per second). Build it from users and their actions rather than guessing the final number:
average QPS = daily active users × requests per user per day ÷ 86,400 peak QPS = average QPS × peak factor
Split reads from writes: the food app’s users browse dozens of menu pages for every order they place, and the two sides are handled by different parts. Then multiply by a peak factor, the ratio of the busiest minutes to the daily average. These are assumptions to state, not laws:
| Traffic pattern | Peak factor to assume |
|---|---|
| An ordinary consumer app across a day | 2 to 3 |
| Food delivery around lunch and dinner | 3 to 5 |
| A cricket final or a festival sale | 5 to 10 |
| Exam results or train bookings at opening time | 10 to 100, for a few minutes |
Choose the factor from the product’s own peaks, and measure it once you have history, per minute rather than per hour: an hourly average hides a first-minute spike that can be several times higher. For the food app, 600 million requests a day averages about 7,000 a second, an ordinary Friday dinner peaks near 4× that, and a final night doubles the Friday rush. The average describes almost no real minute, so design for the peak, not the daily average.
Sizing the final night
Now put the pieces together for the night that broke checkout. A week ahead, while there is still time to add machines, the team asks one question: how many app servers does the final need?
Predict before you watch: on the final night, does the food app need nearer 8, 80 or 800 app servers?
- Nearer 80: about 6,000 requests a second on average, 8× that at the peak, and about 600 a second per server kept 60% busy.
App servers for the final: ≈ 80 (an ordinary Friday needs about 40: add the rest a week ahead)
Read it as steps
Watch for the peak factor row: one stated assumption turns 10 servers of average traffic into 80.
Sized for the average, the app would get 10 servers; the final needs 80, and an ordinary Friday about 40. With the exact 86,400 the peak is about 56,000 a second, and 80 servers would run near 70% busy instead of 60%: still inside the target, which is why the rounding is safe here.
The same inputs size the writes, and they hold a surprise:
| Write stream | Working | At the final-night peak |
|---|---|---|
| Orders placed | 2 M a day ÷ 86,400 × 8 | ≈ 185 /s |
| Order writes (items, payment, status changes) | 185 /s × about 10 rows each | ≈ 1,900 /s |
| Rider location pings | 300,000 riders online ÷ one ping per 5 s | 60,000 /s |
The biggest write stream is not orders but rider pings, about 30 times the order writes. Each stream will end up in a different place, and the capacity page later decides where.
Storage and how long to keep it
Requests come and go; storage stays. Every write adds to it, and it rarely shrinks:
storage = new items per day × size per item × days kept × copies
Copies is the factor people forget: many stores keep three copies of every piece of data, and backups add more. A database adds two further factors: indexes, often 30 to 50% on top of the rows, and headroom, because disks should stay under about 70% full. Multiplying by 1.5 and dividing by 0.7 roughly doubles the disk.
Of the four factors in the formula, days kept (the retention) is the one the team chooses. The food app’s product team asks to keep every rider ping for five years, "in case we need it".
Kept for 30 days instead: ≈ 16 TB (520 GB × 30: a few machines, not a petabyte)
Read it as steps
Watch for the days kept row: it is the one factor in the storage formula the team chooses, and it moves the answer 60×.
Kept five years, pings need nearly a petabyte, more than ten times everything else the app stores. Kept 30 days, they fit on a few machines. Set retention per kind of data: raw pings for 30 days, a short summary of each trip’s route for years, orders for as long as accounting needs them.
Growth compounds on top. If pings grow 50% a year, the fifth year alone adds more than the first three combined, so state a growth rate or plan to re-estimate every quarter.
Bandwidth
Storage counts bytes kept; bandwidth counts bytes moving each second, and it is one more multiplication:
bandwidth = requests per second × bytes per request
= bytes per day ÷ 86,400Traffic coming in (ingress) is mostly writes and usually small: the food app’s 60,000 pings a second at 100 bytes are 6 MB a second, about 48 Mbps. Traffic going out (egress) is mostly reads, and media makes it large. Dish photos show how:
| Step | Working | Result |
|---|---|---|
| Photo bytes per day | 20 M users × 40 dish photos × 100 KB | 80 TB |
| Average rate | 80 TB ÷ 86,400 s | ≈ 0.9 GB/s |
| In bits | 0.9 GB/s × 8 | ≈ 7.4 Gbps |
| At the final-night peak | 7.4 Gbps × 8 | ≈ 60 Gbps |
Links are sold in bits per second, so multiply bytes by 8; mixing the two puts you off by 8×. Sixty gigabits a second would keep three to six network cards running flat out at the origin, which is why photos go through a CDN, a network of servers near users that keeps copies of files. If the CDN answers 95% of requests, the origin serves only about 3 Gbps at the peak.
The balance flips for products that mostly upload, such as phone backups or video uploads: there ingress is the big number, so estimate both directions. Cloud providers bill egress per GB, so this number also lands on the bill, which the money page prices.
Part 4 · Machines, Pools and Money
Servers and memory
Once the peak is known, servers are a division. The hard part is the per-server figure, which depends on how much work each request does, so pick a plausible number and state it: for example, one app server handles about 1,000 requests a second of light JSON work with a cache lookup.
servers = peak QPS ÷ requests per server ÷ target utilisation
= 48,000 ÷ 1,000 ÷ 0.6
= 80 serversThe target utilisation (60% here) is the busiest you let a machine get at the peak, and it buys two things. Room for failure: if 10 of the 80 servers die, the rest run at 69%. And short queues: in a simple queueing model, a request’s time in a server is its service time ÷ (1 − utilisation), 2.5× at 60% but 20× at 95%. A fleet sized at 95% to save money tips over when one machine fails.
Cache memory is a size estimate. The food app’s 300,000 restaurant menus, without photos, average about 20 KB each: 6 GB, which fits in one cache node’s memory, so cache all of it, with a replica for failover. When a dataset is too big, a small share of items usually gets most reads, often summed up as 80/20: cache the hottest 20% and most reads still hit.
What one machine can handle
Every sizing ends in the same division: the load you estimated ÷ what one component can take. These figures are rough, for modern hardware and simple operations; heavy queries or large payloads can cut them by 10×.
| Component | Rough capacity | What it decides |
|---|---|---|
| App server | 1,000 to 10,000 simple requests/s | How many servers sit behind the load balancer |
| Relational primary | Thousands to tens of thousands of simple writes/s; a few to tens of TB | Whether you must shard |
| Cache node | About 100,000+ simple operations/s | How many cache nodes; usually few |
| Log partition | About 10 MB/s, tens of MB/s at best | How many partitions a stream needs |
| Connection server | About 100,000 mostly idle WebSockets, more with tuning | How many servers hold live connections |
| Disk | NVMe SSD 100,000+ IOPS; network block volume thousands | Local or network disks for a busy database |
| Network card | 10 to 25 Gbps | How many servers a bandwidth figure needs |
IOPS means input/output operations per second; a log partition is one ordered slice of a replicated log, covered in Message Queues. Here is the table deciding where the food app’s data lives:
orders 1,900 writes/s at the peak → inside one primary’s thousands 2 M × 2 KB × 365 ≈ 1.5 TB a year, ≈ 3 TB on disk per copy pings 60,000 writes/s; matching needs only the latest position → an in-memory store keyed by rider id, 2 or 3 nodes menus all 6 GB in one cache node, plus a replica photos 60 Gbps at the peak → a CDN, not the app servers
Notice what did not happen: no sharding. A standby replica covers the primary’s failure, and moving orders older than a year to cheaper storage keeps each copy near 3 TB, so a restore at 500 MB a second takes under two hours instead of most of a day. The plan holds until order writes near 10,000 a second.
Threads and connections: Little’s law
Requests per second is a rate, but many limits are counts: threads, database connections, open sockets. Little’s law links the two: the number in flight equals the arrival rate times the time each one spends inside. It explains the night checkout broke.
Watch for the thread badge: each checkout holds a thread until the gateway replies
Read it as steps
Little’s law at work: the same arrival rate needs three times the threads when each call takes three times as long.
On an ordinary Friday, 92 orders a second × 1 s kept 92 threads busy, under half the pool. On the final, 185 a second × 3 s needed 555. A slow dependency shows up first as a full pool, long before any CPU looks busy, and a timeout caps the time inside, so it also caps the pool you can ever need.
The same law counts connections. Customers keep a live tracking screen open for about 40 minutes per order, so at 185 orders a second about 185 × 2,400 ≈ 450,000 screens are open: five connection servers at 100,000 sockets each, eight with headroom.
A bigger gateway pool works because the gateway can take more calls at once. A relational database handles a few hundred connections well, not thousands: when its pool fills, fix the slow query and fail waiting requests fast, or a bigger pool only moves the queue into the database.
The fifth estimate: money
Requests, storage, bandwidth and servers are four estimates. The fifth is money, and often one line is enough: which part of the bill dominates. Run it before any launch that changes what users download. Use rough list prices and check current ones: they change, and large customers pay less.
The food app’s product team wants a 10-second video of each dish that plays by itself as users scroll a menu. Here is the cost check, assuming 20 autoplays per user a day at 1 Mbps and egress at the low end of the range:
| Line | Working | Per month |
|---|---|---|
| Storing dish photos | 10 M dishes × 1.1 MB (original plus two sizes) ≈ 11 TB, × $0.02 per GB | ≈ $220 |
| Serving dish photos today | 80 TB a day × 30 = 2.4 PB, × $0.05 per GB | ≈ $120,000 |
| Serving autoplay videos | 20 M × 20 × 1.25 MB = 500 TB a day, × 30, × $0.05 per GB | ≈ $750,000 |
Two things stand out. Serving costs far more than storing: the photos cost about 500× more to deliver than to keep. And autoplay would multiply the delivery bill about seven times before it earns a rupee. The design follows the bill: tap-to-play or Wi-Fi-only videos at a lower bitrate, and a CDN rate negotiated before the launch, not after.
Part 5 · Using Estimates
Checking an estimate
Before an estimate goes into a design doc or a capacity plan, check it for the usual errors. Most are not arithmetic slips but missing factors, and each one costs a predictable multiple:
| Mistake | How far off | Fix |
|---|---|---|
| Designing for the daily average | 2 to 100× too low at the peak | Multiply by a stated peak factor |
| Mixing bits and bytes | 8× | Bytes for storage, bits for network links |
| Forgetting replication | About 3× | Multiply storage by the number of copies |
| Forgetting indexes and headroom | About 2× too little disk | Multiply by 1.5 and divide by 0.7 |
| Keeping everything forever | Often 10× or more | Set retention per kind of data |
| Dropping units | 1,000× or more | Write the unit next to every number |
| Pricing storage but not egress | Several times too low for media | Price what you serve as well as what you keep |
| False precision | Time lost, nothing gained | One or two significant figures |
Three checks catch most of the rest. Test the extremes: if your answer means each user uploads 1 GB a day, an input is wrong. Compare with production once it exists: last month’s real peak beats any assumed factor. And use the result: an estimate that changes no decision was not worth making.
Everything depends on something. Pick a number, give the reason, and invite a correction. A stated assumption is a decision that others can check; "it depends" only delays one.
In the interview
In a 45-minute design round, estimates get about three of the 40 design minutes, so work out only the numbers that change a decision and say each assumption before you use it.
- Say
- "A day is about 10⁵ seconds, so 600 million requests a day is about 6,000 a second; at an 8× peak that is 48,000, about 80 servers kept 60% busy."
- Say
- "About 1,900 writes a second fits one relational primary, and a year of orders is about 3 TB on disk, so no sharding yet; a standby covers failover."
- Say
- "Serving photos costs about 500× more than storing them, so the CDN and its rate matter more than the storage tier."
- Avoid
- Multiplying 86,400 by 365 to four digits on the board. One significant figure leads to the same design and saves minutes.
- Avoid
- Sizing from the daily average. 6,000 requests a second looks like 10 servers; the 8× peak needs 80.
Wrap-up
| Term | Meaning |
|---|---|
| QPS | Requests (queries) per second; count reads and writes separately |
| Peak factor | How many times the average the busiest minutes reach |
| Order of magnitude | The nearest power of ten; designs change at each one |
| Retention | How long data is kept before it is deleted or archived |
| Target utilisation | How busy a machine may get at the peak, about 60–70% |
| Little’s law | In flight = arrival rate × time each request spends inside |
| Egress | Data leaving your servers for users, usually billed per GB |
| Headroom | Spare capacity kept for spikes, failures and growth |
Next: Abstractions That Hide the Network looks at what happens when the calls you just counted cross a network that can be slow, lose messages or run a request twice, like the gateway calls that piled up on the final night.
Part 6 · Check yourself
Quick check
Q1For the final, a teammate proposes 51 servers at 95% busy instead of 80 at 60%, to save money. Why is that a bad plan?
Think it through, then check.
- Time in a server is service time ÷ (1 − utilisation): 20× at 95% against 2.5× at 60%.
- Lose 3 servers and the other 48 each take 1,000 a second, their full load; queues grow without limit.
- Twenty-nine servers for one night cost far less than an outage in the busiest hour of the year.
Q2A release adds a 100 ms fraud-check call inside each order’s transaction. At 1,900 writes a second, the 50-connection pool runs dry while database CPU sits at 30%. Why?
Think it through, then check.
- Little’s law: connections held 10 ms meant about 19 busy; held 110 ms, about 210 are needed, against 50.
- The connections sit idle in open transactions, so the CPU stays low. Make the fraud call before the transaction, with a timeout.
Q3For the final, the team wants rider pings every second instead of every 5. What happens to the write rate and the 30-day store?
Think it through, then check.
- 300,000 writes a second, 5× more: 3 in-memory nodes at full load, about 5 with headroom.
- If every day moved to 1 s, the 30-day store grows from about 16 TB to about 78 TB.
- Cheaper: ping every second only near the restaurant or the customer.
Q4Product wants every order kept in the orders database forever. Shard by customer now, or move orders older than a year to an archive store?
Think it through, then check.
- Orders add about 3 TB per copy each year while writes stay far below one primary’s limit: size, not load, is the pressure.
- Archiving keeps the primary small and restores fast; old orders read slowly from a second store. Sharding adds cross-shard queries years early. Archive now; pick a shard key for later.
Q5From What System Design Is: From One Server to Millions: a food app runs on one server. It expects 300 requests a second next month and 3,000 a year later. What do you add, and why?
Think it through, then check.
- 300 a second fits one server, but one failure takes everything down: a separate database with a standby, and two stateless app servers behind a load balancer. Availability triggers it.
- 3,000 a second: 3,000 ÷ 600 = 5 app servers, plus a cache for read-heavy menus. A few hundred writes a second still fit one primary.
All chapters · Previous: What System Design Is: From One Server to Millions