Capacity estimation
The whiteboard arithmetic that turns hand-waving into a design — no calculator, under two minutes, out loud.
Why they make you do arithmetic
Capacity estimation looks like a party trick and is actually the load-bearing part of the interview. Nobody cares whether you said 30 TB/day or 40 TB/day. What the arithmetic does is force every architectural decision that follows to have a reason. "We'll need a CDN" is a guess. "Peak egress is around a terabit per second, so serving from origin would need forty 25-gig links; we'll put a CDN in front and target a 95% hit ratio" is a design. The numbers are how you stop the interview from being an opinion exchange.
The second reason is negative: estimation is the fastest way to discover that you don't need the thing you were about to draw. Roughly half the value of the exercise is in the moments when the number comes back small and you get to say "so a single Postgres box covers this for three years." That sentence scores higher than any amount of Kafka.
The rounding conventions that make it tractable
You have no calculator and you are talking while you compute. Every convention below exists to keep the arithmetic to one significant figure and a power of ten.
| Quantity | True value | Use this | Error, and which way |
|---|---|---|---|
| Seconds in a day | 86,400 | 100,000 = 105 | Rate estimates come out ~14% low. Irrelevant next to a 3× peak multiplier. |
| Seconds in a month | 2,592,000 | 2.5 × 106 | Under 4% off. |
| Seconds in a year | 31,536,000 | 3 × 107 | ~5% low. (The famous mnemonic: π × 107.) |
| Days in 5 years | 1,825 | 2,000 | ~10% high — conservative, which is the right direction for storage. |
| 1 KB / MB / GB / TB / PB | 210, 220, 230, 240, 250 | 103, 106, 109, 1012, 1015 | 7% low at GB, 10% at TB. Say "I'm using powers of ten" once and move on. |
| Peak-to-average traffic | varies | 2-3× (10× if event-driven: ticket sales, live sport, New Year) | State which one you chose and why. |
| Fraction of DAU concurrent at peak | varies | 10-25% | Only matters for connection-oriented systems. |
| Storage replication overhead | varies | 3× for replicas, 1.4× for erasure coding | Forgetting this is the single most common estimation miss. |
The order — always the same eight steps
Two habits make this fluent. First, write the assumptions on the board before the arithmetic — "10 sessions/user/day, 3× peak, 5-year horizon" — so the interviewer can correct an input instead of watching you compute the wrong thing for two minutes. Second, say the units out loud every line. Almost every estimation error in an interview is a units error: bits versus bytes, per-day versus per-second, one photo versus one photo plus its four derived sizes.
Numbers worth memorizing
| Operation | Time | Anchor |
|---|---|---|
| L1 cache reference | 1 ns | the unit everything else is measured in |
| Branch mispredict | 3 ns | |
| L2 cache reference | 4 ns | |
| Mutex lock/unlock, uncontended | 20 ns | |
| Main memory reference | 100 ns | 100× slower than L1 — this is why cache locality wins |
| Compress 1 KB | 2 µs | compression is nearly always cheaper than the network hop it saves |
| Read 1 MB sequentially from RAM | ~50 µs | ≈ 20 GB/s |
| SSD random read (NVMe, with queueing) | ~100 µs | ~16 µs is the flash; the rest is the software stack |
| Read 1 MB from NVMe SSD | ~300 µs | ≈ 3 GB/s |
| Round trip within one datacenter | 0.5 ms | your budget for a service-to-service hop |
| Read 1 MB sequentially from spinning disk | ~10 ms | ≈ 100 MB/s |
| Disk seek (HDD) | ~10 ms | why random I/O on HDD is a design error, not a tuning problem |
| Round trip US coast to coast | ~50 ms | 4,800 km at 200,000 km/s in fibre = 24 ms each way. Physics, not engineering. |
| Round trip US to Europe | ~80 ms | |
| Round trip US to India / Australia | ~200 ms | the reason "just put it in one region" fails a global product |
| Component | Throughput to assume | Note |
|---|---|---|
| Single Postgres/MySQL box, indexed and warm | 5,000-10,000 simple reads/s; 1,000-5,000 writes/s | Writes are fsync-bound. A single box with an NVMe WAL goes higher; do not claim more than 10k writes/s without saying why. |
| Read replica | Adds another 5,000-10,000 reads/s each | Replicas scale reads, never writes. Say this every time you add one. |
| Redis, single instance | ~100,000 ops/s; up to ~1,000,000 pipelined | Single-threaded for command execution — one hot key cannot be scaled by adding RAM. |
| Application server, real JSON handler with a DB call | 1,000-5,000 rps | Use 1,000 for sizing. It is conservative and defensible. |
| nginx / envoy serving static or proxying | 50,000+ rps per box | |
| Kafka broker | 100 MB/s-1 GB/s sustained | Sequential disk writes; the bottleneck is usually the NIC. |
| One 10 / 25 Gbps NIC | 1.25 / 3.1 GB/s | Divide by 8. Bandwidth is quoted in bits, storage in bytes — this is the classic slip. |
| One commodity server, 2026 | 64-128 cores, 256 GB-2 TB RAM, tens of TB NVMe | Bigger than most candidates assume. Vertical scaling gets you further than the folklore suggests. |
| Thing | Bytes |
|---|---|
| char / boolean / int / bigint or timestamp / UUID | 1 / 1 / 4 / 8 / 16 |
| A "skinny" row — a few ids and a timestamp | ~100 B |
| A typical metadata row with short text | ~500 B - 1 KB |
| A chat message (text + delivery metadata) | ~200 B |
| A structured JSON log line | ~500 B - 1 KB |
| A thumbnail / a web-sized image / a phone photo | ~20 KB / ~200 KB / 1-5 MB |
| One minute of 1080p video | ~50 MB |
Worked example 1 — a photo-sharing service
Assumptions stated first, on the board, before any arithmetic: 200 M daily active users; each opens the app 10 times a day and each open loads one feed page of 20 images; 10% of users post one photo per day; 5-year retention; peak is 3× average.
TRAFFIC
DAU 200,000,000
feed opens / user / day 10
feed reads / day 2 x 10^9 // 200M x 10
seconds / day 100,000
avg feed reads / sec 20,000 // 2e9 / 1e5
PEAK feed reads / sec 60,000 // x3 — this sizes the fleet
posters / day 20,000,000 // 10% of 200M
avg uploads / sec 200 // 2e7 / 1e5
PEAK uploads / sec 600
read : write ratio 100 : 1 // 2e9 vs 2e7 -> read-heavy, cache hard
BLOB STORAGE
bytes / photo 1.5 MB // 1.2 MB original + 4 derived sizes
per day 20e6 x 1.5e6 = 3 x 10^13 B = 30 TB/day
per year 30 TB x 365 = 10,950 TB ~ 11 PB/year
over 5 years ~55 PB raw
with 3x replication ~165 PB
with erasure coding 1.4x ~77 PB // worth 88 PB of savings — say this out loud
METADATA STORAGE
bytes / photo row 500 B // ids, timestamps, caption, url, counters
per day 20e6 x 500 = 10 GB/day
per year 3.65 TB/year
over 5 years ~18 TB // ONE box. Do not shard this on day one.
BANDWIDTH (egress)
bytes / feed page 20 images x 100 KB = 2 MB
per day 2e9 x 2e6 = 4 x 10^15 B = 4 PB/day
avg egress 4e15 / 1e5 = 4 x 10^10 B/s = 40 GB/s = 320 Gbps
PEAK egress ~960 Gbps ~ 1 Tbps // serving this from origin is not a plan
with a 95%-hit CDN origin sees ~48 Gbps // two or three 25G links. Feasible.
CACHE
hot set: metadata for the last 7 days of photos
rows 20e6 x 7 = 1.4 x 10^8
memory 1.4e8 x 500 B = 70 GB // 3-node Redis + replicas. Trivial.
SERVERS
peak req/s 60,000
per app server 1,000 rps
bare minimum 60
x2 for headroom + AZ loss tolerance ~120 app servers
Every one of those lines is a sentence you say while writing it. The interviewer is not checking your multiplication — they are checking that you know which quantity comes next and that you noticed the two interesting results: metadata is small enough for one machine, and blob egress is large enough to make the CDN non-negotiable.
Worked example 2 — a chat service
Same eight steps, wildly different shape of answer — which is exactly why it is worth doing twice. Assumptions: 500 M DAU; 40 messages sent per user per day; average message reaches 3 recipients (a mix of 1:1 and small groups); 20% of DAU are connected simultaneously at peak.
TRAFFIC
DAU 500,000,000
messages sent / day 500e6 x 40 = 2 x 10^10
avg sends / sec 200,000 // 2e10 / 1e5
PEAK sends / sec 600,000
avg recipients / message 3
deliveries / day 6 x 10^10
avg deliveries / sec 600,000
PEAK deliveries / sec 1,800,000 // the real workload is delivery, not send
STORAGE (if you keep history)
bytes / message 200 B // text + ids + timestamps + delivery state
per day 2e10 x 200 = 4 x 10^12 B = 4 TB/day
per year 1.46 PB/year
x3 replication ~4.4 PB/year // LSM store, partition by conversation
STORAGE (if you keep only the undelivered)
~1% undelivered at any time, held ~1 day
4 TB x 1% = 40 GB // five orders of magnitude cheaper
CONNECTIONS
concurrent at peak 500e6 x 20% = 100,000,000 sockets
memory / socket ~10 KB // kernel buffers + per-user state
total connection RAM 1e8 x 1e4 = 10^12 B = 1 TB
sockets / gateway box 1,000,000 // tuned kernel, event-driven runtime
gateway boxes 100, call it 150 with headroom
BANDWIDTH
peak 1.8e6 deliveries/s x 200 B = 3.6 x 10^8 B/s
= 360 MB/s ~ 3 Gbps // compare: 1 Tbps for the photo service
ROUTING TABLE (which gateway holds each live socket?)
100e6 entries x 50 B = 5 GB // fits in one Redis. Do not use a database.
How each number cashes out as a design decision
| The number you computed | What it rules out | What it rules in |
|---|---|---|
| 60,000 peak reads/sec | A single database primary serving reads | Cache tier in front, read replicas behind, ~120 stateless app servers |
| 100:1 read:write ratio | Optimising for write throughput; normalised schemas with joins on the read path | Denormalised read models, aggressive caching, fan-out-on-write (see the case-studies chapter) |
| 30 TB/day of blobs | Storing images in the database. Ever. | Object storage + CDN; the DB holds a 500-byte row with a URL |
| 18 TB of metadata over 5 years | Sharding into 100 shards on day one | One primary + replicas, with a shard key chosen now and applied later |
| ~1 Tbps peak egress | Serving from origin, single-region | CDN with an explicit hit-ratio target; cache keys designed for hit ratio |
| 100 M concurrent sockets | Stateless HTTP polling; a stateless gateway tier | Persistent connections, sticky routing, a socket-location registry, graceful drain on deploy |
| 5 GB routing table | A database lookup on every message delivery | In-memory table, replicated, rebuilt from connection state on restart |
| 600,000 peak writes/sec | A relational primary; synchronous cross-region replication | LSM-tree store partitioned by conversation id, tunable quorum (see the sharding and consistency chapters) |
Recognizing it in an unseen problem
- Every design prompt needs this, whether or not it is asked for. Do it immediately after requirements and before the first box goes on the board — it is what makes the boxes defensible.
- The prompt gives you a user count, a "how would this scale to X", or a product with obvious media (photos, video, voice) — media means the blob path and the metadata path have wildly different sizes and must be estimated separately.
- A naive answer estimates average load. The senior move is peak load, with the multiplier stated as an assumption, plus a sentence about what drives the peak for this specific product.
- Distinguish "big number" from "hard problem": 18 TB of metadata is a big number and an easy problem; 100 M concurrent connections is a smaller number and a much harder one. Say which of your numbers are merely large.
- The pitfall: computing storage and never computing bandwidth or memory. Storage is the cheap one. Egress bandwidth and RAM are where the money and the architecture actually live.
- If a number comes back small, say so and simplify the design. "That's 100 writes a second, so one Postgres primary with a replica handles this for years" is a stronger answer than any distributed store.