Skip to the notes
JSGroundwork
JSGroundwork handwritten · web dev
✎Playground→⌘Problems↻Review🔥Progress

Chapters

24 chapters
⌕
Beginner7›
B1The mental modelB2Client-server basicsB3Vertical vs horizontalB4Databases in designB5Caching fundamentalsB6APIs & communicationB7Walkthrough: URL shortener
Intermediate9›
I1Load balancing in depthI2CDNsI3Message queues & asyncI4Consistency modelsI5Rate limiting & throttlingI6Designing for availabilityI7Storage systemsI8Search systemsI9Walkthrough: feed/chat
Advanced8›
A1CAP theorem in depthA2Sharding at scaleA3Distributed consensusA4Fault toleranceA5Observability at scaleA6Capacity estimationA7Case studiesA8Tradeoff thinking
/ search[ ] chaptert top

System Design levels

1Beginner2Intermediate3Advanced

Ready to read

JSJavaScript⑂Git◎Interview prepΣDSA in JSSDSystem Design
More topics15›
</>HTML{ }CSS⚛ReactNNext.jsNeNest.jsTSTypeScriptNoNode.js🐳DockerDBSQL & Databases✓Testing🔒Web Security☁Cloud & DevOps◈GraphQL◆Redis☸Kubernetes
100%
A6

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.

QuantityTrue valueUse thisError, and which way
Seconds in a day86,400100,000 = 105Rate estimates come out ~14% low. Irrelevant next to a 3× peak multiplier.
Seconds in a month2,592,0002.5 × 106Under 4% off.
Seconds in a year31,536,0003 × 107~5% low. (The famous mnemonic: π × 107.)
Days in 5 years1,8252,000~10% high — conservative, which is the right direction for storage.
1 KB / MB / GB / TB / PB210, 220, 230, 240, 250103, 106, 109, 1012, 10157% low at GB, 10% at TB. Say "I'm using powers of ten" once and move on.
Peak-to-average trafficvaries2-3× (10× if event-driven: ticket sales, live sport, New Year)State which one you chose and why.
Fraction of DAU concurrent at peakvaries10-25%Only matters for connection-oriented systems.
Storage replication overheadvaries3× for replicas, 1.4× for erasure codingForgetting this is the single most common estimation miss.
One number does half the work A day is 100,000 seconds. Every "per day" figure becomes a "per second" figure by moving the decimal point five places. 2 billion requests a day is 20,000 a second, and you did that in your head while still talking.

The order — always the same eight steps

1. DAU given or assumed 2. avg req/sec ÷ 100,000 s 3. PEAK req/sec × 2-3 4. storage/day writes × bytes 5. storage / 5 yr × 2,000 × replicas 6. bandwidth bytes/s → Gbps 7. cache RAM hot set × row size 8. servers peak ÷ per-box
Step 3 sizes the fleet, step 5 chooses the storage engine, step 6 decides whether you need a CDN. Do them in order and each answer is an input to the next; skip one and you will be caught out by the interviewer who asks "so how many machines?"

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

OperationTimeAnchor
L1 cache reference1 nsthe unit everything else is measured in
Branch mispredict3 ns
L2 cache reference4 ns
Mutex lock/unlock, uncontended20 ns
Main memory reference100 ns100× slower than L1 — this is why cache locality wins
Compress 1 KB2 µscompression 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 datacenter0.5 msyour budget for a service-to-service hop
Read 1 MB sequentially from spinning disk~10 ms≈ 100 MB/s
Disk seek (HDD)~10 mswhy random I/O on HDD is a design error, not a tuning problem
Round trip US coast to coast~50 ms4,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 msthe reason "just put it in one region" fails a global product
ComponentThroughput to assumeNote
Single Postgres/MySQL box, indexed and warm5,000-10,000 simple reads/s; 1,000-5,000 writes/sWrites are fsync-bound. A single box with an NVMe WAL goes higher; do not claim more than 10k writes/s without saying why.
Read replicaAdds another 5,000-10,000 reads/s eachReplicas scale reads, never writes. Say this every time you add one.
Redis, single instance~100,000 ops/s; up to ~1,000,000 pipelinedSingle-threaded for command execution — one hot key cannot be scaled by adding RAM.
Application server, real JSON handler with a DB call1,000-5,000 rpsUse 1,000 for sizing. It is conservative and defensible.
nginx / envoy serving static or proxying50,000+ rps per box
Kafka broker100 MB/s-1 GB/s sustainedSequential disk writes; the bottleneck is usually the NIC.
One 10 / 25 Gbps NIC1.25 / 3.1 GB/sDivide by 8. Bandwidth is quoted in bits, storage in bytes — this is the classic slip.
One commodity server, 202664-128 cores, 256 GB-2 TB RAM, tens of TB NVMeBigger than most candidates assume. Vertical scaling gets you further than the folklore suggests.
ThingBytes
char / boolean / int / bigint or timestamp / UUID1 / 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.

⚠ The three misses that cost candidates the most (1) Forgetting replication and derived data — a 55 PB answer that ignores 3× replicas is off by a factor of three, and the fix is one sentence. (2) Confusing bits and bytes when quoting bandwidth; 40 GB/s is 320 Gbps, not 40 Gbps. (3) Estimating average load and then sizing the fleet from it. Systems fail at peak, so the fleet is sized from peak, and the peak multiplier is an assumption you must state.

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.
Say it like this → "The interesting result is that chat is a connection problem, not a bandwidth problem — 3 Gbps at peak is nothing, but 100 million concurrent sockets means the gateway tier is stateful and I need a routing layer to find a user's socket. And notice that 'do we retain history?' swings storage from 40 GB to 4.4 PB a year. That is a product decision with a five-order-of-magnitude infrastructure consequence, so I'd want it answered before I draw the storage layer."
clients 200 M DAU CDN edge 95% hit ratio origin + object store 55 PB, erasure coded ~960 Gbps ~48 Gbps the hit ratio is the whole design: at 80% the origin needs ~190 Gbps, at 99% it needs ~10 Gbps — so cache-key design is a capacity decision
The CDN is not there to reduce latency here; it is there because the origin physically cannot emit a terabit per second. Quote the hit ratio as an assumption, because the origin number is entirely a function of it.

How each number cashes out as a design decision

The number you computedWhat it rules outWhat it rules in
60,000 peak reads/secA single database primary serving readsCache tier in front, read replicas behind, ~120 stateless app servers
100:1 read:write ratioOptimising for write throughput; normalised schemas with joins on the read pathDenormalised read models, aggressive caching, fan-out-on-write (see the case-studies chapter)
30 TB/day of blobsStoring images in the database. Ever.Object storage + CDN; the DB holds a 500-byte row with a URL
18 TB of metadata over 5 yearsSharding into 100 shards on day oneOne primary + replicas, with a shard key chosen now and applied later
~1 Tbps peak egressServing from origin, single-regionCDN with an explicit hit-ratio target; cache keys designed for hit ratio
100 M concurrent socketsStateless HTTP polling; a stateless gateway tierPersistent connections, sticky routing, a socket-location registry, graceful drain on deploy
5 GB routing tableA database lookup on every message deliveryIn-memory table, replicated, rebuilt from connection state on restart
600,000 peak writes/secA relational primary; synchronous cross-region replicationLSM-tree store partitioned by conversation id, tunable quorum (see the sharding and consistency chapters)
⚠ Do not over-invest in precision Spending eight minutes of a forty-five-minute interview on arithmetic is a failure mode of its own. The target is roughly three minutes: state assumptions, compute peak QPS, storage, bandwidth, and one memory figure, then say "these are order-of-magnitude; the ones that change the design are peak QPS and total storage" and move on. If the interviewer wants a number refined they will ask. And if they hand you a number — "assume 10 M users" — take it and stop negotiating; they are trying to save you time.

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.
←previousObservability at scale↑ CovernextCase studies→