Two engineers are arguing about whether the new activity feed needs a queue. One says it will fall over without one. The other says a queue is premature complexity. The argument runs for forty minutes, and nobody writes down a number.
The number takes ninety seconds. Five million users, each loading the feed twice a day, is ten million reads per day, which is about 120 reads per second on average and maybe 350 at peak. That is a single well-indexed Postgres instance and a cache, not a queue. The argument is over, and it was never really about queues.
That is what estimation is for. Not precision: you are not predicting the future, you are finding out which order of magnitude the problem lives in, because the design changes completely between 100 requests per second and 100,000, and not at all between 118 and 137.
The four numbers, in this order
Every estimate is the same four steps. Do them in order, because each one feeds the next.
- Trafficqpsusers x actions per user per day, converted to per second
- Payloadbandwidthbytes per request, in and out
- Storagecapacitywrites per day x bytes per write x retention
- Fan-outmultiplierinternal calls per external request
Traffic starts from users and behavior, never from a QPS figure someone remembers: "five million users, twice a day" is a claim you can argue with. Payload decides whether that traffic is cheap: 350 requests per second of 2MB responses is 700MB per second of egress, a CDN conversation before it is anything else. Storage is write rate times row size times retention, plus indexes, which often cost as much as the data. And fan-out multiplies everything: one request that triggers eight internal calls means eight times the internal QPS.
Rounding rules that keep it in your head
The point of this arithmetic is that you can do it while talking. That requires deliberately sloppy numbers.
| Quantity | Round to | Why it works |
|---|---|---|
| Seconds in a day | 100,000 (really 86,400) | 16% high, and always in the safe direction |
| 1 million per day | ~12 per second | the single most useful conversion here |
| 1 KB | 1,000 bytes | powers of two matter for memory, not for estimates |
| 1 million rows at 1 KB | 1 GB | scales cleanly: 1 billion rows is 1 TB |
| A month | 30 days | and a year is 400 days when you want headroom |
Two of those deserve committing to memory. One million events per day is about twelve per second, and one million rows of a kilobyte each is a gigabyte. Most estimates are one of those two, scaled.
Work in a single unit and convert once at the end. Mixing megabytes per second with gigabytes per day mid-calculation is where the factor-of-1000 errors come from, and a factor of 1000 is the only kind of error that matters here.
Average is a lie you tell yourself
An average is what the system does when nobody is looking.

The gold spike is the pattern that actually breaks things: small in volume, enormous in rate. Provision against the peak, bill against the average, and treat every batch, retry storm and on-the-minute poll as its own worst case.
A worked estimate
A team wants to add notifications: when someone you follow posts, you get an entry in your notification feed.
Traffic. 2 million daily active users, each posting 0.5 times a day, gives 1 million posts per day, which is about 12 posts per second average and call it 35 at peak.
Fan-out is the real answer here, because each post writes one notification per follower.

That is the moment the design changes. Twelve posts per second is a synchronous insert. 7,000 notification writes per second, triggered by a user action that must return in 200ms, is a queue and a worker pool.
Storage follows. 200 million notifications a day at 200 bytes is 40GB per day, so retention becomes a product decision rather than an afterthought: 30 days is a 1.2TB table you can partition and drop by day, and forever is a bill nobody approved.
And the tail account is not an edge case, it is the one that pages you. Every fan-out estimate needs its worst case named out loud, which is exactly what the fan-out interview question is probing for.
Sanity checks before you commit
Three quick tests catch most bad estimates.
Compare it to something physical. 40GB per day is a laptop SSD every ten days; 700MB per second is a saturated 10 gigabit link. That catches errors pure arithmetic hides.
Ask whether this is one machine. Under about 1,000 QPS of simple work, with a working set that fits in RAM, is one machine plus a replica. Tens of thousands of QPS, or a working set larger than memory, is a fleet, a cache tier, and a partitioning strategy. Which side of that line you land on is the most consequential output of the whole exercise.
Check which way you rounded. If every decision made the number smaller, you did not estimate, you hoped.
This arithmetic tells you how much work arrives. The next lesson is the constants that tell you how long it takes to do, and Simon Eskildsen's napkin math collection is the best public reference set for both.
Key takeaways
- Estimation exists to find the order of magnitude, not the answer. The design differs between 100 and 100,000 QPS, and not at all between 118 and 137.
- Four numbers, in order: traffic, payload, storage, fan-out. Each one feeds the next, and fan-out is the one people skip.
- Two conversions carry most of the work: 1 million per day is about 12 per second, and 1 million kilobyte rows is 1 GB.
- Design against the peak (2 to 3 times average for consumer traffic, more for internal tools), and treat scheduled bursts as their own worst case.
- Name the tail case explicitly. The account with 20 million followers is invisible to every average you computed.
- Sanity-check against a physical object, then answer one question: is this one machine, or a fleet?
Checkpoint · lesson 1 of 6
You can now:
- Turn a product description into QPS, bytes, and storage in under two minutes
- Separate average load from peak load, and design against the peak
- Sanity-check an estimate against something you already know the size of