← blog

Capacity Estimation Drills: From Product Sentence to Server Count

Part 4 of the napkin-math series — full worked capacity drills: a messaging app and a market-data platform taken from one-sentence descriptions to QPS, bandwidth, storage, server counts, and the shard verdict, plus an interactive drill machine to practice on.

Knowing capacity math and performing it are different skills. I learned that the humiliating way — I understood the loads, the ceilings, and the database separately, and still went blank when a real conversation demanded all three, in order, out loud, in ninety seconds. Nothing was missing except the reps.

Musicians don’t learn scales by reading about scales. This post is the practice room: the answer script, two fully worked drills narrated the way you’d actually speak them, and a drill machine with fresh scenarios so you can rep until the sequence is muscle memory.


The script: six sentences that answer any capacity question#

Every full answer walks the same six beats — this is the whole series compressed into a talk track:

BeatThe sentence shapeFrom
① Assume“Let me assume N users doing k actions/day — flag me if you want different numbers.”Part 1
② Rate“That’s total per day ÷ 100K seconds ≈ X per second average.”Part 1
③ Peak“Human traffic bunches into ~⅓ of the day, so ~3× busy-hour; synchronized events are 10×+ and get handled as events.”Part 1
④ Size“× bytes per event → Y MB/s, ×8 vs the NIC → verdict; × 100K → Z TB/day → decay verdict.”Part 1
⑤ Ceilings“Against one node’s ceiling (the constant), that’s n nodes, ×1.5 headroom.”Part 2
⑥ Data verdict“Shapes: rebuildable → RAM, history → columnar partitions, truth → one Postgres — and here’s the shard check.”Part 3
Say the beats in order. Each one earns the next.

The magic isn’t in any beat — it’s that the sequence never changes. When the sequence is automatic, all your attention is free for the numbers.

Drill one, fully narrated: a messaging app#

“We’re building a chat app. 50M daily actives. Size the backend.”

Watch the six beats do their work. Read this once silently, then once out loud — the difference will surprise you.

① Assume. “Say each user sends ~10 messages a day, ~200 bytes each, and each message is read by the ~5 people in the conversation. Stop me if the shape’s different.”

② Rate. “50M × 10 = 5×10⁸ messages/day ÷ 10⁵ seconds ≈ 5K messages/sec average.”

③ Peak. “Chat follows waking hours — most traffic in a third of the day, so ~3×: 15K/sec provisioned. New Year’s midnight is a different beast, ~10×, and it’s a planned event: pre-scale that evening, let a queue absorb the burst, shed typing-indicators before message delivery. You don’t buy 10× hardware for one night a year.”

④ Size. “5K/s × 200 B = 1 MB/s — the NIC laughs. Per day: 1 MB/s × 10⁵ = 100 GB/day, ~36 TB/year. Ingest boring, retention real.”

⑤ Ceilings. “Writes: 5K/s average fits one Postgres raw; the 15K busy-hour needs batching — comfortably inside the ~100K batched ceiling. Reads: every message read ~5× → 25K reads/sec (75K busy) — cache the recent conversations and that decimates; replicas take the rest. Fan-out delivery: 25K sends/sec ÷ 40K per WebSocket node — one node’s worth of sends, but connections bind instead: say 20M concurrently online ÷ 25K conns/node = 800 nodes × 1.5 = ~1,200 WebSocket nodes. The edge fleet dwarfs the database — that’s normal for chat, and saying so shows you know which problem this product actually is.”

⑥ Data verdict. “Shapes: presence/typing = rebuildable, RAM only. Message history = append-heavy, day-partitioned, old partitions tiered out. The truth tables — users, conversations, receipts — one Postgres with replicas. Shard check: 15K writes/sec batched? fine. Working set — this week’s conversations — well under RAM? fine. No shard yet; the day it comes, the key is conversation ID, because every query is ‘this conversation’s messages’ and that keeps each query on one shard.”

Ninety seconds, spoken. Every sentence is a move from the first three parts, already rehearsed.

Drill two: the question I once fumbled for real#

“You’re pulling ticker data for 10,000 markets from an exchange over WebSockets, ~5 updates/sec per market, ~200 bytes each. Walk me through the system.”

This exact question — nearly verbatim — is the one I fumbled. Here’s the two-minute version I should have said:

① – ② Rate. “10K markets × 5/s = 50K messages/sec inbound, regardless of how many connections carry it.”

③ Peak. “Market data is correlated — news hits every market at once, so bursts of 5–10× for seconds at a time: 250–500K msg/s burst. The pipeline must absorb that; shedding market data mid-burst corrupts downstream state.” (Machine senders, but synchronized by the world — the third traffic shape.)

④ Size. “50K × 200 B = 10 MB/s, ×8 = 80 Mbit — under 1% of a NIC. A day is 10 MB/s × 10⁵ = 1 TB/day. The wire is boring; the day is not.”

⑤ Ceilings. “How many WebSocket connections to the exchange? Capacity says one is enough — 50K msg/s is a single connection’s workload if the consumer keeps up. Real answer: 4–10, set by the venue’s per-connection subscription caps and blast radius (one dropped socket shouldn’t stall every market), not by throughput. Consumer side: ~5 µs to parse a message → 200K parses/sec/core — one core absorbs the average, bursts want two or three. This is a two-node problem wearing a scary number.”

⑥ Data verdict. “Three shapes, three homes: live order books = rebuildable from a snapshot, RAM only, the database never sees them. Tick history = 1 TB/day append-only → columnar, day partitions, drop-partition retention. Orders and fills = money → one small Postgres, and it’s tiny — your own order flow is thousands of rows a day, not millions.”

The punchline I want you to steal: the scary number (50K/sec) was the cheap part, and the boring number (1 TB/day) was the design driver. Capacity math exists to make exactly that inversion visible before you build the wrong thing.

Now you: the drill machine#

Four fresh scenarios, the same beats, answers hidden until you commit. The rule that makes it work: say your answer out loud, with units, before revealing — reading silently trains recognition, speaking trains recall — and design meetings, incident calls, and every on-the-spot “can we handle it?” moment run on recall:

Drill machine

Scenario: 100M new short links per month, ~100 redirects per link over its lifetime, ~500 bytes stored per link.

Answer each question OUT LOUD — with units — before revealing. Reading silently is not the rep.

Q1. Write rate — how many new links per second?
Pick a scenario, answer each question out loud before revealing. When you finish a drill, run it once more end-to-end without stopping — the second pass is the one that sticks.
Drill machine
Pick a scenario, answer each question out loud before revealing. When you finish a drill, run it once more end-to-end without stopping — the second pass is the one that sticks.

Want more reps? Manufacture them from the wild: every product announcement is a drill (“Spotify has 600M users…”), every outage postmortem is a drill run backwards (“what load must have hit that ceiling?”). The numbers are everywhere once the sequence is automatic.

Anchors from production#

Step 4 of the method — is the shape sane? — needs reference points, and the best ones are famous real systems. These four are worth knowing cold, both as sanity anchors and as humility:

SystemThe numbersWhat it anchors
WhatsApp2M+ TCP connections on one box in 2012 (their blog post was titled “1 million is so 2011”); ~450M users on 32 engineersa tuned box holds ~100× the planning number for connections — it’s a memory game, and Erlang + FreeBSD tuning played it hard
Stack Overflow~209M requests/day on 9 web servers at 5–10% utilization — and they once accidentally served the whole site from one (“not saying it’s a good idea”)fleets exist for redundancy and headroom, not load; “how many servers” and “how hot do they run” are different questions
One big modern server128 cores, 1–8 TB RAM: ~500K req/s of nginx, ~70K IOPS of Postgres, rentable for ~$1.3K/monthbefore designing a fleet, check whether the whole problem fits in one machine that costs less than a junior engineer’s laptop budget
levels.io$1.5M/year business, 50M+ requests/month, one $40/month 4-core VPSthe napkin’s smallest honest verdict, running in production for a decade
Production records vs planning numbers. Provision on planning numbers; know the records so a big answer doesn't scare you into overbuilding.

Two lessons hide in that table. First, my per-node constants are deliberately planning numbers — conservative, untuned, safe to promise — while the records above are what obsessive tuning buys; provision on the former, and let the latter stop you from panicking when a division says “80 nodes.” Second, notice that the most famous verdicts in this genre are denominated in dollars, not QPS — “serverless costs 5–25× a rented server,” “the whole thing runs on $40/month.” Money is a unit like any other, and a napkin answer that ends in currency is often the one that actually changes the decision.

Where the series ends, and what it deliberately isn’t#

The four parts, as one sentence each:

  • Part 1 — the load: LOAD = rate × size; count zeros; ×8 once; 100K-second days; DAU→QPS; two ceilings per load; trust independent averages, jitter shared clocks; ask how fast data’s value decays.
  • Part 2 — the ceilings: five latencies cold; one core = 10⁶ µs/sec, derive the rest; fsync is the notary and batching the carpool; every fix relocates the bottleneck; provision at 60–70%.
  • Part 3 — the database: three shapes of data; indexes buy reads with write currency; copies scale reads, one primary takes writes; the working-set cliff; shard last, key forever.
  • Part 4 — the performance: six beats, always in order, out loud.

And the honest boundary: capacity estimation tells you how many — it does not tell you how. Consistency trade-offs, failure handling, protocol design, data modeling — that’s the other half of system design, and it runs on judgment instead of arithmetic. But every one of those conversations goes better when honest numbers are already on the table, because the numbers decide which problems are real.

Start every design with the napkin. It’s two minutes, and it’s the difference between architecting the system you have and the one you imagined.

Comments

Signed in with GitHub. Be kind.