This chapter covers turning a one-line product description into a number — requests per second, terabytes, dollars, machines — computed out loud in a few minutes, without notes or a calculator.
Every such estimate is a chain of assumptions multiplied together. One link in that chain almost always dominates the answer, and naming that link is the real deliverable. When you finish this chapter you should be able to produce an estimate, name the assumption it is most sensitive to, and check the result for plausibility even when there is nothing to check it against.
The input is a small set of assumptions you state: how many people use the thing, how often each of them does something, how many bytes one of those somethings is, and how long you keep it.
The output is one number plus a sentence naming the binding constraint — the resource that runs out first and therefore decides the design.
“1.5 PB per year” on its own is not an output. “1.5 PB per year, so a single machine is out by three orders of magnitude and the design question is really sharding — splitting the data across many machines” is. An estimate that produces a figure and no constraint has done nothing.
Every constant this chapter uses is written down in Latency numbers and what each one forbids and collected in Numbers worth memorizing cold. Chapter 01 is the architecture these numbers price.
The words and units used on every line
These terms and units appear on nearly every line below and are not defined again.
People and traffic.
- DAU — daily active users. How many distinct people use the product on a given day.
- QPS — queries per second. The rate at which requests arrive. “Queries” is historical: it counts every request, not only the ones that reach a database.
- Latency is how long one request takes. Throughput is how many requests finish per second. These are different quantities, and a system can be good at one and bad at the other.
- The average rate is the day’s total divided by the day’s seconds. The peak rate is what arrives in the busiest second. You buy machines for the peak.
- Replication factor is how many complete copies of the data you keep on different machines. Retention is how long you keep it before deleting it.
Units of size. Byte units go up in thousands throughout this chapter — never 1,024.
| Unit | Bytes |
|---|---|
| kilobyte (KB) | 1e3 |
| megabyte (MB) | 1e6 |
| gigabyte (GB) | 1e9 |
| terabyte (TB) | 1e12 |
| petabyte (PB) | 1e15 |
| exabyte (EB) | 1e18 |
Units of time. The same idea, going down. us is the plain-ASCII spelling of the microsecond symbol.
| Unit | Seconds |
|---|---|
| millisecond (ms) | 1e-3 |
| microsecond (us) | 1e-6 |
| nanosecond (ns) | 1e-9 |
Bits are not bytes. Network links and video streams are rated in bits per second: Mbps is megabits per second, Gbps is gigabits, Tbps is terabits. One byte is 8 bits, so divide by 8 to convert a bit rate into a byte rate and multiply by 8 to go back. Forgetting that factor of 8 is the most common units slip in this chapter, and Estimation 4 is built on getting it right.
Notation. 1e5 is Python’s spelling of 1 x 10^5 = 100,000. This chapter uses it throughout because it is harder to miscount than a row of zeros. 100 k means 100,000 and 500 k means 500,000.
1. The three numbers you actually need
Almost every estimate you will be asked for is one of three templates, or a product of two of them. Announce which one you are computing before you compute it.
| Template | What it decides | |
|---|---|---|
| QPS | DAU x actions/day / 1e5, then a stated peak multiplier | Fleet size, whether you need a queue |
| Storage | items/day x bytes/item x retention x replication | One box vs sharding vs tiering |
| Bandwidth | QPS x bytes/response | CDN, egress cost, saturating a machine’s network card |
Three things in that table need unpacking before you can use it.
The 1e5 in the first row is the 86,400 seconds in a day rounded to one significant figure. Dividing by it turns a per-day count into a per-second rate by moving the decimal point five places, which you can do in your head. Rounding discipline is where that approximation is justified and its error bounded.
Shard and tier, in the third column, are the two answers the storage template usually points at. To shard is to split one dataset across several machines that each hold a disjoint slice. To tier is to move rarely read data onto cheaper, slower storage.
Memory is a fourth template, almost always working set x bytes/entry — the working set being the portion of the data actually being read at any moment. It is usually asked as “does the cache fit?” — see And does it fit.
Say which one you are computing before you compute it. Naming the path (“let me size the write path”) signals that you know these are different questions.
Every estimate is a chain of assumptions
One idea underlies everything else in the chapter: an estimate is not a measurement with error bars, it is a product of guesses, and your job is to know which guess is carrying the answer.
All three templates above are products. DAU x actions/day. items/day x bytes/item x retention x replication. QPS x bytes/response. Nothing is added; everything is multiplied.
That single structural fact gives you the whole sensitivity analysis for free, because in a product, a factor that is 2x too high makes the answer exactly 2x too high, no matter how large or small that factor is in absolute terms. A million-fold number you know precisely damages the answer less than a small number you guessed.
So rank your assumptions not by size but by spread — the ratio between the highest and the lowest value you would not be embarrassed to defend. The widest spread is the assumption that dominates, and it is the one to say out loud and to offer to redo.
The code below does that ranking mechanically. Its input is one (low, high) pair per assumption; its output is the assumption names sorted by high / low. Read the printed column rather than the function body — what matters is which name comes out on top.
def spread(factors):
"""Rank assumptions by how much each one can move the answer.
`factors` maps a name to (plausible_low, plausible_high). The estimate is
a product, so each factor scales the answer by its own range and the
ratio high/low IS that factor's contribution to the total uncertainty.
Absolute magnitude is irrelevant: a factor of 1e9 known exactly
contributes 1.0x, and a factor of 0.2 that might be 1.0 contributes 5x.
"""
return sorted(((name, hi / lo) for name, (lo, hi) in factors.items()),
key=lambda pair: -pair[1])
# The photo-storage estimate of section 4, with an honest range on each input.
photo = {
"users": (2.5e8, 1.0e9), # "500 M" is a placeholder, so 2x either way
"uploads/user/day": (0.05, 1.0), # 0.2 assumed, and this is the softest number
"bytes/upload": (1e6, 4e6), # 2 MB after transcode, depends on the codec
"retention days": (1825, 1825), # 5 years, stated by the question
"replicas": (3, 3), # a design choice, not a guess at all
}
for name, ratio in spread(photo):
print(f"{name:18} {ratio:5.1f}x")
# uploads/user/day 20.0x <- the answer is this assumption
# users 4.0x
# bytes/upload 4.0x
# retention days 1.0x
# replicas 1.0x
assert spread(photo)[0][0] == "uploads/user/day"
The uploads-per-user figure is twenty times softer than anything else in the chain, so it is the number the estimate is really about.
Now multiply the ratios together. The end-to-end estimate spans 20 x 4 x 4 = 320x, which sounds like it invalidates the exercise. It does not. The conclusion that estimate reaches — that this is an object-storage problem with a small metadata database beside it, and that keeping three full copies of cold data is indefensible — is true at every point across that 320x range.
Check the endpoints rather than asserting it. Each corner multiplies the same five factors, once at every low value and once at every high value:
low corner 2.5e8 users x 0.05 uploads x 1e6 B x 1,825 days x 3 copies
= 6.8e16 B = 68 PB
high corner 1.0e9 users x 1.0 uploads x 4e6 B x 1,825 days x 3 copies
= 2.2e19 B = 22 EB
Object storage with tiering is the answer at both ends. An estimate is robust when its conclusion survives its own uncertainty, not when its number is accurate.
Two habits follow, and both are cheap:
- State the assumption and its spread in the same breath. “I’ll say 0.2 uploads per user per day — that is the number I am least sure of, it could easily be 1, and if it is then everything below moves by 5x.”
- Never quote more than one significant figure. A chain with 320x of spread in it cannot support “1.095 EB”. It supports “about an exabyte”, and the extra digits actively mislead the person listening.
QPS, worked end to end
Every problem in the track opens with the QPS template. Memorize this walk from product description to fleet-sizing number.
The assumptions. These five lines are the entire input, and everything after them is arithmetic:
- a photo feed
- 200 M daily active users
- each user opens the app 5 times a day
- each user posts 0.1 times a day (one post every ten days)
- peak traffic is 2x the average
The dominant assumption is openings per day. The DAU figure and the openings figure are both placeholders invented to make the arithmetic concrete, but a user count is the kind of thing a company knows, where “5 opens a day” could as easily be 3 or 10. Flag that one out loud.
The arithmetic. The left column is per-day totals; the right column is the per-second rate each becomes after dividing by the 1e5 seconds in a day.
reads 2e8 users x 5 opens = 1e9 requests/day -> / 1e5 s = 10,000 /s average
writes 2e8 users x 0.1 posts = 2e7 requests/day -> / 1e5 s = 200 /s average
peak multiplier 2x (consumer traffic, one diurnal hump)
reads 10,000 /s x 2 = 20,000 /s
writes 200 /s x 2 = 400 /s
read:write = 1e9 / 2e7 = 50:1
The findings. Three fall out, and the ratio is the one that matters:
- 50:1 read-heavy, so the design question is caching and read fan-out, not write throughput. That single ratio decides more of the architecture than either absolute number.
- 400 writes/s is nothing. One Postgres primary handles it. Say so and stop sizing the write path.
- 20,000 reads/s is a fleet, not a box, so now the interesting question is what sits in front of the database.
Then convert back before you buy anything:
1e9 requests / 86,400 s = 11,574 /s average
11,574 /s x 2 = 23,148 /s peak
The 1e5 version was for deciding; the 86,400 version is for provisioning.
The peak multiplier is a stated assumption, not a derived constant, and it deserves its own sentence out loud. It says how much busier the busiest second is than the average second.
Use 2x for smooth consumer traffic with one diurnal hump — one broad rise and fall across the waking day. Use 3x for event-driven or bursty workloads, where a notification or a broadcast concentrates arrivals into minutes. Say which you picked and why.
Some systems legitimately skip the multiplier entirely, because a tier behind a queue absorbs spikes by design rather than by capacity. Ch 14 drops it for its video-transcoding tier for exactly that reason and still provisions at roughly 1.5x the mean. What is not acceptable is applying the multiplier silently or omitting it silently.
2. Rounding discipline
You cannot do 500,000,000 x 287 on a whiteboard. Some approximations cost almost nothing, one carries most of the leverage, and in exactly one place rounding is forbidden.
A significant figure is a digit that carries information. Rounding to one of them means keeping the leading digit and replacing the rest with zeros, so 287 becomes 300 and 86,400 becomes 100,000.
The rule is round every input to one significant figure, and these are the substitutions to know by reflex:
seconds/day 86,400 -> 1e5 (you are 16% high; say so once, move on)
seconds/month 2.6e6 -> 2.5e6 (two figures on purpose: the exact 2.592e6
rounds to 3e6 at one figure, and that is
16% high, where 2.5e6 is only 3.5% low)
1 million/day 10/s (exact 11.6)
1 billion/day 10,000/s (exact 11,574)
1 KB = 1e3 B, 1 MB = 1e6 B, 1 GB = 1e9 B (never 1,024 in an estimate)
1e5 seconds/day is the approximation you will use most. It turns every per-day figure into a per-second figure by moving the decimal point five places.
Its error is easy to state, so state it: 1e5 / 86,400 = 1.16, meaning the divisor you used is 16% larger than the real one. That is far below the error in your input assumptions, and saying that you know it is there is worth more than the accuracy you gave up.
When to convert back
The 1e5 shortcut stops being acceptable at a precise moment, and using it one step too long is how a fleet ends up undersized.
1e5 is a drill convention, not a reporting convention, and the difference is where this trips people. Divide by 1e5 while you are thinking out loud. Divide by 86,400 the moment a number leaves your mouth as a result — a fleet size, a monthly bill, a capacity headroom.
1 B/day -> via 1e5: 10,000/s the number you reason with
-> exact: 11,574/s the number you size a fleet with
Be careful about which 16% is which. The 16% is the error on the divisor; the shortfall on the quotient is its reciprocal, 10,000 / 11,574 = 0.864. So 1e5 understates load and a fleet sized on it is 13.6% short, not 16%. That is fine for “do I need a queue?” and not fine for “how many boxes do I buy?”
This is why chapters in this track legitimately differ. The ones drilling a decision divide by 1e5; the ones costing a fleet divide by 86,400. Say which one you are doing. An undeclared convention is the actual defect, not the choice.
The one place not to round
There is exactly one place not to round, and it is powers of two, when the answer is a bit width — how many binary digits you allocate to a field in an identifier.
Those are not estimates at all. They are exact capacities, and being 16% off changes whether a design works. 2^32 is 4.3 billion, so a 32-bit counter overflows after 4.3 billion values. 2^41 milliseconds is 69.7 years, so 41 bits of millisecond timestamp last a human lifetime.
Those specific values decide designs, as Data model the 64 bit layout derived shows when it packs a unique identifier into 64 bits.
2^10 ~ 1e3 2^20 ~ 1e6 2^30 ~ 1e9 2^32 = 4.3e9
2^40 ~ 1e12 2^41 ms = 69.7 years 2^64 = 1.8e19
3. Latency numbers, and what each one forbids
The table below is the standing set of hardware constants the chapter uses. Every estimate below draws its physical numbers either from this table or from the collected list in Numbers worth memorizing cold, and from nowhere else. When an estimate says “at 1 GB/s sequential”, “at 500 us per hop”, “at 20 TB per drive” or “at $0.02/GB of egress”, the constant came from one of those two places.
The split between the two is deliberate. This table is the timings. §6 repeats them and adds the capacities, prices and stated defaults that are not latencies at all.
§6 is not a strict subset, though, and the difference is worth knowing before you drill it. Two of its latency rows appear only there — the ~0.5 ms cache read and the ~10 ms database read — and both are stated assumptions about a loaded system rather than hardware constants, which is exactly why they are kept out of this table.
One abbreviation appears throughout: RTT is round-trip time, one message sent plus its answer coming back.
Read the vintage note before you use the table. It descends from Jeff Dean’s 2009 numbers (the Stanford talk they come from, and the widely circulated list that copied them). The ones set by physics — the speed of memory, the speed of light in fibre — have not moved in the years since. Two rows are marked with a ⚠ and they fail for different reasons: the SSD row (a solid-state drive, flash storage with no moving parts) gives a per-operation latency that gets misread as a limit on the whole device, and the disk-seek row is correct for the device it names and is routinely quoted for a device that has no seek at all.
The table itself is famous. What matters is the third column — the design rule each row implies — which is the part candidates skip. Read every row as an assumption about hardware, kept deliberately separate from the assumptions about workload that each estimate states for itself. An interviewer will challenge a workload assumption and accept a hardware constant, so you want to know at every moment which of the two you just used.
| Operation | Time | What it forbids |
|---|---|---|
| L1 cache reference | 1 ns | — |
| Branch mispredict | 3 ns | — |
| Main memory reference | 100 ns | 1e7/s per core, so in-memory is ~free at any web scale |
| Compress 1 KB (snappy) | 2 us | Compress before crossing a network, always |
| Send 1 KB over 1 Gbps | 10 us | — |
| SSD random read (4 KB, one at a time) | 100 us ⚠ | The latency of one read. Do not turn it into a device ceiling — see below |
| Read 1 MB sequential from memory | 250 us | — |
| Round trip within a datacenter | 500 us | ~2,000 sequential hops/s. Chains of 10 internal calls cost 5 ms before any work |
| Read 1 MB sequential from SSD | 1 ms | |
| Disk seek (spinning hard disk only) | 10 ms ⚠ | 100/s per spindle. Applies to HDD; an SSD has no seek. This is why B-tree depth matters — see below |
| Read 1 MB sequential from disk | 20 ms | |
| Round trip, US-East <-> Europe | ~75 ms | The shorter transatlantic path, and the figure ch 01 uses for its EU users |
| Packet CA -> Netherlands -> CA | 150 ms | You cannot serve Europe from California. This single number forces multi-region |
Two rows carry vocabulary that the table cell had no room for.
“Spinning rust” is the engineers’ nickname for a hard disk with rotating platters, the device the 10 ms seek row describes.
A B-tree is the balanced, high-fanout index a relational database keeps on disk, and a lookup walks it from root to leaf — so its depth is the number of seeks one row costs, which is why 10 ms per seek shows up in query latency (sql/03).
The last two rows are different routes, not one route and its double. ~75 ms is a round trip on the short transatlantic path, US-East to Europe and back. 150 ms is a round trip from California, which crosses the continent before it crosses the ocean. The ratio is about 2x by coincidence of geography, and reading the second as “the first, there and back” is a mistake that then gets applied to every other pair of cities.
The two rows that will burn you
Two rows get misread more often than all the others combined, and both misreadings turn a workable design into an apparently impossible one.
Two terms first. IOPS means input/output operations per second, the count of separate reads or writes a storage device completes each second. Queue depth is how many requests the device is working on at the same time, so queue depth 1 means you wait for each read to finish before issuing the next.
100 us is a latency, and dividing 1 into it gives 10,000 IOPS — which is a queue-depth-1 number and roughly 50x below a real NVMe device. NVMe is the modern interface by which solid-state drives attach directly to the processor bus, and such a drive does 500 k–1 M random 4 KB IOPS because it services hundreds of requests concurrently; the per-request latency stays around 100 us while throughput scales with queue depth.
Use 100 us to answer “how long does one read take?” and never to answer “how many can this box do?” Treating it as a device ceiling is how a storage estimate concludes that a design is impossible when the real device is fifty times faster than the arithmetic assumed.
10 ms is a seek, and only a spinning hard disk — an HDD, or hard disk drive — actually seeks. A seek is the physical movement of a read head to a track on a rotating platter. A solid-state drive has no head and no platter, so quoting 10 ms for SSD-backed storage inflates every input/output argument by two orders of magnitude.
Two conclusions from the table are worth memorizing as sentences rather than as rows:
- Memory is 100,000x faster than a disk seek (100 ns against 10 ms), which is why every read-heavy design ends in a cache.
- A cross-continent round trip is 150 ms and the speed of light is not negotiable — the only fix is to put data closer, which is what a CDN and multi-region are for.
A third conclusion needs a table of its own.
Sequential versus random, and why the block size matters
Sequential means reading bytes that are next to each other on the device. Random means jumping to an unrelated location for each read. Sequential wins, but by how much depends entirely on a number people forget to quote: the block size, the number of bytes one random read fetches.
Sequential throughput comes straight off the table’s own rows. Random throughput is one block per operation, so it is block size / latency: an SSD’s 100 us gives 1 / 100e-6 = 10,000 IOPS, and a disk’s 10 ms seek gives 1 / 10e-3 = 100 IOPS.
| Sequential | Random, 1 KB blocks | Random, 4 KB blocks | |
|---|---|---|---|
| SSD | 1 MB in 1 ms = 1,000 MB/s (1 GB/s) | 10,000 IOPS x 1 KB = 10 MB/s | 10,000 IOPS x 4 KB = 40 MB/s |
| HDD | 1 MB in 20 ms = 50 MB/s | 100 IOPS x 1 KB = 0.1 MB/s | 100 IOPS x 4 KB = 0.4 MB/s |
| SSD advantage | — | 1,000 / 10 = 100x | 1,000 / 40 = 25x |
| HDD advantage | — | 50 / 0.1 = 500x | 50 / 0.4 = 125x |
Quote the block size or the ratio is unfalsifiable. Moving from a 1 KB block to a 4 KB page carries four times the bytes per operation, which cuts the sequential advantage by exactly 4x on both devices.
Either way the direction of the inequality is what makes log-structured storage engines exist — engines that never update a page in place but append every write to the end of a file and merge the files later (Lsm trees vs b trees) — since they convert random writes into sequential ones on purpose.
4. Six worked estimations
Six estimates, each of a different shape, each ending in a named constraint rather than a number. There is also a short seventh, “does the cache fit?”, worked as a coda to Estimation 5 because it is the same cache seen through the memory template.
Between them the four templates of The three numbers you actually need are all worked at least once:
| Estimation | Template it works |
|---|---|
| 1 — Twitter-scale tweet storage | storage (pure byte arithmetic; it computes no rate at all) |
| 2 — Photo service, 5-year storage | storage |
| 3 — Chat: connection memory | memory |
| 4 — Video streaming egress | QPS and bandwidth, on the same workload |
| 5 — A cache’s hit-rate economics | memory, in the “does it fit?” coda |
| 6 — ID generation rate | QPS |
Do them end to end, out loud, on a timer. Each should take under three minutes.
Every one follows the same three-part structure:
- The assumptions, stated out loud and written down — because an assumption in your head is the defect The estimation checklist is about.
- The arithmetic, rounded ruthlessly, with units carried on every line.
- The finding — which resource binds, and therefore what the design conversation is actually about.
Each one also names the assumption that dominates: the one whose plausible range is widest, which therefore owns the answer.
Estimation 1 — Twitter-scale tweet storage
How many bytes does a text-and-image social product accumulate? “How much storage” is almost never a question about the text.
Assumptions.
- 500 million tweets a day
- 300 bytes of text per tweet
- 1 tweet in 10 carries a 1 MB image
The dominant assumption is the image fraction. It could plausibly be 5% or 30%, a 6x range, where a published tweet count is usually good to well within 2x and the text size barely moves anything at all. Since the media term turns out to be 99.7% of the total, whatever happens to that fraction happens to the answer.
Arithmetic.
text 5e8 tweets x 300 B = 1.5e11 B = 150 GB/day
media 5e8 tweets x 0.10 x 1e6 B = 5.0e13 B = 50 TB/day
---------
total 5.015e13 B = 50.2 TB/day
retention 5 years x 365 days = 1,825 days
50.2 TB/day x 1,825 x 3 copies = 2.75e17 B = 275 PB
Finding: the text is 0.3% of the bytes. This is an object-storage problem with a small metadata database bolted on, not a database problem. Anyone who spends the interview on the tweet table’s schema has mis-sized the system.
The media does not belong in the same store either. It goes to blob storage — a service that keeps whole files under a name and is cheap per byte — with a CDN (a content delivery network, a rented fleet of servers worldwide that caches those files near users) in front of it. Only the URL lives in the database.
Check. The line worth checking is 275 PB, and it needs no calculator:
50 TB/day x 365 days = 18 PB/year
18 PB/year x 5 years = 90 PB
90 PB x 3 copies = 270 PB
Landing within 2% of the long form by a completely different route is what makes the number safe to say out loud.
Estimation 2 — Photo service, 5-year storage
The same storage question at exabyte scale. The useful part is the follow-up about how you store it, not the total.
Assumptions.
- 500 million users
- 0.2 uploads per user per day
- 2 MB per photo after transcoding
- five years of retention
- three replicas
The dominant assumption is uploads per user per day. As the sensitivity analysis above showed, it dominates by 20x and everything else in the chain is comparatively firm.
Arithmetic.
per day 5e8 users x 0.2 uploads x 2e6 B = 2.0e14 B = 200 TB/day
per year 200 TB/day x 365 days = 7.3e16 B = 73 PB/year
5 years 73 PB/year x 5 = 3.65e17 B = 365 PB
3 replicas 365 PB x 3 = 1.095e18 B = 1.1 EB
Finding: an exabyte is the answer, and the follow-up question is the real one. You do not keep 3 full replicas of cold photos.
Erasure coding is the alternative. Cut each object into fragments, compute a few extra parity fragments from them, and spread the set across machines so that any sufficiently large subset reconstructs the original. It survives the same number of failures as replication while storing roughly 1.5x the original bytes instead of 3x.
Do that subtraction carefully, because it is the single easiest slip to make here:
3 replicas 365 PB x 3 = 1,095 PB
1.5x erasure 365 PB x 1.5 = 547.5 PB
saved 1,095 - 547.5 = 547.5 PB (not the 365 PB raw figure)
Tiering — moving data that is rarely read into a cheaper, slower storage class — saves more again, though which tier is a real decision rather than a reflex. Cold storage costs about a fifth as much per byte but charges a fee every time you read something back, so it wins only below 0.4 reads per PB per month (Garbage collection scrubbing and lifecycle derives that threshold and asks you to quote it exactly). The estimate’s job was to make tiering obviously necessary rather than a nice-to-have.
Check. Sanity-check the exabyte before you trust it, because nobody has an intuition for that unit. Turn it into a physical object instead:
drives 1.095e18 B / 2e13 B per drive = 54,750 drives (~20 TB/drive, §6)
racks 54,750 drives / 600 per rack = 91 racks (~600 drives/rack)
Carry the conversion constants, not just the words. A U, or rack unit, is the standard 1.75-inch height increment that rack-mounted equipment is built in, so a 42U cabinet is 42 slots tall and holds ten 4U enclosures at 60 bays each — 10 x 60 = 600, which is where that divisor comes from. A reader who does not know what a U is cannot run the next check, which is a check against the 600.
Ninety racks is one row of a data hall. That is a plausible size for a global photo service and an implausible size for a startup, which is exactly the kind of judgement the number was computed to support.
Run the next check on the check. “A thousand racks and several buildings” is the answer people reach for here, and it fails a bound you already believe: a thousand racks for 55,000 drives implies 55 drives in a rack, which is a tenth of what a storage rack holds. An order-of-magnitude error in a sanity check is worse than no sanity check, because it is the number you will say with confidence.
Estimation 3 — Chat: connection memory, and a trap
This estimate deliberately produces a wrong answer, to show what is often the most useful thing an estimate can do: prove that the resource you measured is not the one that sizes the tier.
Assumptions.
- 20 million simultaneously open connections
- about 10 KB of memory each — the operating system’s socket buffers, the encryption state, and the application’s own per-user data
The dominant assumption is not the 10 KB. That is firm to about 2x, and it stops mattering the moment you learn memory is not the constraint. The dominant one is connections per box, which plausibly spans 100 k to 500 k, a 5x range that sets the fleet outright.
Arithmetic.
2e7 connections x 1e4 B = 2e11 B = 200 GB
200 GB / 64 GB per box = 3.1 -> ~3 boxes
That 64 GB is the bottom of the commodity range, not the 128 GB Numbers worth memorizing cold tells you to default to, so say so out loud. At the default it is 200 / 128 = 1.6, call it 2 boxes. Taking an end of a range silently is how two people size the same tier differently and never find out why.
Finding: three boxes is the wrong answer, and knowing why is the point. Memory is not the binding constraint on a tier that holds connections open. Four other things are, and each is a real ceiling:
- File descriptors. A file descriptor is the small integer the operating system hands out for every open socket, and both the process and the kernel cap how many can exist.
- Kernel memory. The kernel spends its own memory and scheduling attention per socket, over and above the application’s 10 KB.
- TLS handshake CPU. Setting up each encrypted connection costs processor time — TLS being transport layer security, the encryption behind HTTPS — and reconnections arrive in floods rather than smoothly.
- Blast radius, meaning how much damage the loss of one machine does. A box holding 6.7 M live connections (
20 M / 3) drops all of them at once, and they all reconnect at once.
Real deployments land at 100 k–500 k connections per box — and that is a stated default, not a measurement. It is an order-of-magnitude anchor of exactly the kind Numbers worth memorizing cold collects, it spans 5x, and it is the number that sets this fleet outright. Say it out loud and abandon it the instant a load test on your own stack gives you a real one.
20 M / 500 k per box = 40 boxes
20 M / 100 k per box = 200 boxes
The estimate tells you which resource is not the constraint. That is a legitimate and often more useful outcome than a fleet size: “memory says three boxes, which tells me memory isn’t what sizes this tier — it’s connection-handling and failure domain.”
Estimation 4 — Video streaming egress
This estimate prices the bytes leaving a video service. At video scale the bandwidth bill is not a line item in the business — it is the business.
Two words first. Bitrate is how many bits of video are consumed per second of playback. Egress is the industry word for bytes leaving your network, which is the direction cloud and CDN providers charge for.
Assumptions.
- 1 billion views a day
- 5 minutes of average watch time per view
- 3 Mbps average bitrate
The dominant assumption is watch time. View counts are usually known, but “5 minutes” could plausibly be 2 or 15, and it multiplies the entire answer.
Arithmetic. Watch the / 8 on the third line: the bitrate is in bits and everything downstream is in bytes.
bytes/view 5 min x 60 s/min = 300 s of playback
300 s x 3e6 bits/s = 9.0e8 bits
9.0e8 bits / 8 = 1.125e8 B = 110 MB (rounded down)
per day 1e9 views x 1.1e8 B = 1.1e17 B = 110 PB/day
CDN egress 1.1e17 B / 1e9 B per GB = 1.1e8 GB
1.1e8 GB x $0.02/GB = $2.2 M/day = $800 M/year
the same total as bandwidth, QPS x bytes/response:
1e9 views/day / 1e5 s = 10,000 views/s
10,000 /s x 1.1e8 B = 1.1e12 B/s
1.1e12 B/s x 8 bits/B = 8.8e12 bits/s = 8.8 Tbps average
Those last three lines are the same estimate run through the bandwidth template instead of the storage one, and it is worth doing both because they answer different questions. The per-day route gives you the bill. The QPS x bytes/response route gives you the pipe, and 8.8 Tbps sustained is 880 completely saturated 10 Gbps network cards before any peak multiplier. That is the arithmetic proof that the origin cannot serve this traffic and the CDN is not an optimization.
Finding: egress is the business. At this scale a 10% reduction in bitrate from a better codec — the algorithm that compresses and decompresses video — is worth $80 M a year, which is why video companies employ codec teams.
It also reframes the design: the origin serves almost nothing, the CDN serves everything, and cache hit ratio at the CDN edge is the single most valuable metric in the system.
Check. Verify the $2.2 M a day before quoting it, because a number that large is where a units slip hides. 110 PB is 110 million GB, and 110 million times two cents is 2.2 million dollars. The check took one multiplication and it is the difference between a defensible figure and a memorized one.
Say which way you rounded while you are there. 1.1e8 is a rounded-down 1.125e8, so every figure in the block is 2.2% low, and the unrounded chain gives $2.25 M/day and $821 M/year. That gap is far inside the spread on watch time and not worth carrying — but “$800 M” offered as though it were exact is a different claim from “$800 M, and I rounded down to get there”.
Estimation 5 — A cache’s hit-rate economics
This estimate prices what a cache buys. One asymmetry people get backwards: the load a cache removes and the factor by which it removes it behave completely differently as the hit rate climbs.
A cache is a small, fast store that holds recently requested answers. A hit is a request it can answer; a miss is one that falls through to the database. Write the hit rate as h, a fraction between 0 and 1.
Assumptions.
- 100,000 reads per second
- 10 ms for a database read
- 0.5 ms for a cache read
The dominant assumption is the hit rate itself, which is why the estimate is run at three values of it rather than one.
The 10 ms is a stated assumption, not the disk-seek row of Latency numbers and what each one forbids reused. A database read is not one seek. It is a buffer-pool miss — the buffer pool being the database’s own in-memory cache of disk pages, so a miss is a row that was not already in RAM — plus a network hop plus query execution on a primary that is also serving everyone else. 10 ms is a defensible round number for that on a loaded system.
On an SSD-backed primary with a warm buffer pool, meaning one already holding the pages this workload touches, it could be 1 ms. That 10x range makes it the widest assumption in this estimate after the hit rate. Quoting the table’s 10 ms here and calling it a hardware constant is exactly the misuse that row is marked with a ⚠ for.
Arithmetic. Mean latency is the hit path and the miss path weighted by how often each happens.
flowchart LR
R["Read request"] --> C{"In cache?"}
C -->|"hit, fraction h"| H["Cache read<br/>0.5 ms"]
C -->|"miss, fraction 1-h"| D["Database read<br/>10 ms"]
H --> M["Mean latency<br/>h(0.5) + (1-h)(10)"]
D --> M
assume 100 k QPS read, DB read 10 ms, cache read 0.5 ms
hit rate 0.80: mean latency = 0.8(0.5) + 0.2(10) = 2.4 ms
DB load = 100k x 0.20 = 20 k QPS
hit rate 0.90: mean latency = 0.9(0.5) + 0.1(10) = 1.45 ms
DB load = 100k x 0.10 = 10 k QPS
hit rate 0.99: mean latency = 0.99(0.5)+0.01(10) = 0.595 ms (say 0.6)
DB load = 100k x 0.01 = 1 k QPS
Both of those columns are straight lines in h. Latency is 10 - 9.5h and load is 1e5 - 1e5h, so “linear in the hit rate” and “linear in the miss rate” are the same sentence about the same line, and neither one is the finding.
An affine function is a straight line — anything of the form a + b h, a constant plus a multiple of the variable. Any affine function of h is an affine function of 1-h, because substituting h = 1 - (1-h) gives (a + b) - b(1-h), the same line with the constants relabelled. There is no asymmetry there to get backwards.
Finding: the real asymmetry is between the load and the factor. Load is QPS x (1-h), a line that falls to zero. The reduction factor is 1/(1-h), a hyperbola that diverges: at h = 0.80 the cache is doing 5x, at 0.99 it is doing 100x, at 0.999 it is doing 1,000x.
Conflating the two is the classic slip. As h -> 1 the factor goes to infinity while the thing it is a factor on goes to zero, which is why the last fraction of a percent is simultaneously the most valuable and the least material.
The clean way to say it is in nines:
h = 0.80 -> miss 0.200 -> 20,000 QPS
h = 0.90 -> miss 0.100 -> 10,000 QPS 2x better
h = 0.99 -> miss 0.010 -> 1,000 QPS 10x better
h = 0.999 -> miss 0.001 -> 100 QPS 10x better again
Each additional nine of hit rate divides database load by ten. That is why the last points are worth disproportionate effort — and why a cache stampede, in which a popular key expires and every request for it misses at the same instant (Step 4 cache and the ceiling that forces it), is so violent: dropping from 99% to 90% is one nine backwards and 10x the database load instantly.
And does it fit?
Everything above prices a hit rate you have not yet shown you can buy. The memory template of The three numbers you actually need — working set x bytes/entry — is what answers that. It is the fourth template and the one interviewers phrase as “does the cache fit?”, so work it in the same breath as the hit rate rather than assuming the answer.
Assumptions. Take the product of Estimation 1: 500 M items a day, 300 bytes of text each, and a hot window of one day, since almost all reads on a feed are of recent items.
Arithmetic.
assume hot window 1 day = 5e8 entries, ~1 KB each in the cache
(300 B of text plus the key, pointers and serialization overhead)
working set 5e8 entries x 1e3 B = 5e11 B = 500 GB
usable/box 128 GB x 0.70 = 89.6 GB (§6 default, 70% full
to leave eviction headroom)
boxes 500 / 89.6 = 5.6 -> 6 boxes
Finding: six boxes, so it fits — and that is the finding, because it means the 99% hit rate above is a real option rather than an aspiration.
The dominant assumption is the hot window, not the entry size: one day against one week is a 7x swing straight into the box count, where the 1 KB is firm to about 2x.
Note also the roughly 3x between the 300 bytes an item weighs on disk and the kilobyte it weighs in the cache. Keys, pointers and serialization overhead are the term people leave out, and leaving it out is how a cache that was sized to fit does not.
The general shape is worth keeping: if the working set fits, the cache is a hit-rate problem; if it does not, it is a sharding problem, and no amount of eviction tuning converts the second into the first.
Estimation 6 — ID generation rate
The shortest estimate here. It shows an under-used outcome: proving that something is not worth designing.
Assumptions. They come from the Snowflake identifier scheme, in which each machine stamps every identifier with a millisecond timestamp plus a 12-bit counter that resets each millisecond:
- each node can mint
2^12= 4,096 identifiers per millisecond - the scheme allows 1,024 nodes
Both are exact by construction rather than guessed, which is why this estimate has no dominant soft assumption at all.
Arithmetic.
Snowflake: 12 bits of sequence per millisecond per node
4,096 /ms x 1,000 ms/s = 4.1 M IDs/s/node
4.1 M/s x 1,024 nodes = 4.2 B IDs/s
Finding: four billion IDs per second is roughly 42,000x the 100,000/s peak this track’s busiest designs assume. That is 4.19e9 / 1e5 per second — note that 1e5 here is a rate, not the seconds-per-day constant used elsewhere in this chapter.
Even against the 1 million messages a second assumed in ch 20 it is 4,190x. That is the answer to “will the ID generator be a bottleneck?” — it will not, by four orders of magnitude, so say so and spend the time elsewhere. The full layout is derived in Data model the 64 bit layout derived.
When an estimate comes out four orders of magnitude clear, the correct output is a sentence, not a design.
5. The estimation checklist
Everything above compresses into the order you execute it in, plus one skill the six drills could not teach on their own: deciding whether an answer is plausible when there is nothing to compare it against.
flowchart TD
A["What am I sizing?"] --> B["State the assumption OUT LOUD<br/>DAU, actions/day, bytes/item"]
B --> B2["Give each one a plausible low and high<br/>Widest ratio owns the answer — say which"]
B2 --> C["Round to 1 significant figure"]
C --> D["Compute per-day, then / 1e5 for per-second"]
D --> E["Apply a STATED peak multiplier<br/>2x smooth · 3x bursty · none if queued"]
E --> E2["Reporting a fleet or a bill?<br/>redo at 86,400"]
E2 --> F["Apply replication and retention"]
F --> G{"Does the answer<br/>change the design?"}
G -->|no| H["Say so and move on.<br/>Do not polish it"]
G -->|yes| I["Name the constraint it just exposed"]
style B fill:#1d3557,color:#fff
style B2 fill:#1d3557,color:#fff
style G fill:#bc6c25,color:#fff
style I fill:#2d6a4f,color:#fff
The colour key is local to this diagram — nothing in this flowchart is a component, every box is a step you perform, so ch 01’s colour key for the track’s architecture diagrams does not apply. Here, blue marks the two boxes that are about saying something out loud, which are the two people skip; orange marks the one real decision; green marks the output of the whole procedure. The seven uncoloured boxes are mechanical.
Walk the diagram once, box by box. Each is a rule one of the sections above derived, and the arrows are the order to apply them in.
- “What am I sizing?” Naming the template — requests per second, stored bytes, or bandwidth — is what stops you computing something nobody asked for.
- “State the assumption OUT LOUD.” This is the box candidates skip and the one that costs them the round, since an unspoken input makes every number after it unfalsifiable.
- “Give each one a plausible low and high.” This turns a list of assumptions into a sensitivity analysis. It is Every estimate is a chain of assumptions’s whole result, it costs about ten seconds because you are ranking ratios rather than computing anything, and skipping it is how a candidate produces a number without ever saying what the number is really about.
- “Round to 1 significant figure.” A chain with several soft factors in it cannot support more precision than that.
- “Compute per-day, then
/ 1e5for per-second.” The shortcut of Rounding discipline, and how the arithmetic stays doable in your head. - “Apply a STATED peak multiplier.” Capacity is bought for the busiest second, and the multiplier is an assumption like any other.
- “Reporting a fleet or a bill? Redo at 86,400.” The instant the number leaves your mouth as a result rather than as a thought, redo the division exactly.
- “Apply replication and retention.” The two multipliers that turn a daily byte count into a fleet-sized one.
- “Does the answer change the design?” The only box that is a question about you rather than about arithmetic. If no, say so and move on — an estimate that would lead to the same architecture at 10 TB and at 100 TB has already told you everything it can. If yes, name the constraint it just exposed, because that name, not the number, is the output of the whole procedure.
The most common failure is not arithmetic — it is computing a number nobody needed. If the storage estimate would not change your design whether it came out 10 TB or 100 TB, do not compute it; say “storage is not the constraint here, the read QPS is” and spend the minute there instead.
The second most common failure is silently inventing an assumption. “500 M DAU” is fine. “500 M DAU” said in your head and never out loud is not, because when the interviewer’s mental model says 5 M, every number after that is wrong and neither of you knows why.
Enumerating the candidate constraints
The last box of the flowchart says “name the constraint”, and Estimation 3 hands you four real ones — file descriptors, kernel memory, TLS handshake CPU, blast radius. But a list of four answers is not a method, and the next tier you size will have different ones.
The method is a fixed walk, short enough to say under pressure, and you run it every time regardless of which resource the question asked about. Each line is a candidate resource, the arithmetic that sizes it, and the ceiling you compare against:
CPU cycles per request x QPS, against cores
memory working set x bytes/entry, against RAM per box
network bytes QPS x bytes/response, against the network card
disk bytes items/day x bytes x retention x replication, against capacity
disk IOPS random reads/s, against 100/s per spindle or 500 k on NVMe
descriptors/connections concurrent sockets, against the per-box ceiling
blast radius what one machine's death takes with it
Seven lines, each of which is one of the templates already drilled, pointed at a different resource. The binding constraint is whichever your arithmetic exhausts first — and it is very often not the one the question named, which is precisely the shape of Estimation 3, where the memory answer was right and irrelevant.
The last two lines are the ones people omit, because they are the two that are not throughput at all. A descriptor limit is a configured ceiling rather than a physical one. Blast radius is a reliability budget that can force a fleet three times larger than any capacity number justifies.
Walking all seven costs about fifteen seconds and is the difference between “the answer is 3 boxes” and “memory says 3, and here is what actually binds”.
Sanity-checking a number you cannot verify
At some point you will have an answer, no way to look it up, and thirty seconds. You will never have a reference figure to compare against in an interview, so the check has to come from somewhere other than the source of the number. Four methods cover almost every case, and each takes one line.
Recompute it by a different route. The strongest check is arithmetic that shares no step with the original. Estimation 1 reached 275 PB through a five-year daily total; the check goes 50 TB a day is about 18 PB a year, five years is 90 PB, three copies is 270 PB. Two routes that agree are very unlikely to be wrong in the same direction, and the whole check took eight seconds.
Convert the answer into a physical object. Units like the exabyte and the petabyte carry no intuition, so trade them for something that does: drives, racks, buildings, dollars, people. 1.1 EB became about 55,000 drives and, at ~600 drives to a fully populated rack, about 90 racks — one row of a data hall. $2.2 M a day becomes $800 M a year, which is a number you can compare against a company’s revenue.
If the object is absurd — four buildings for a startup, or one machine for a global service — the arithmetic is wrong or an assumption is. Carry the conversion constant, not just the unit: “racks” only checks anything if you know how many drives are in one, and it was the missing 600 that made “a thousand racks” sound reasonable.
Check the answer against a bound you already believe. You know a commodity machine has tens of gigabytes of memory, that one database primary handles thousands of writes a second, and that a human types a few characters a second. Any estimate that quietly requires a machine to do a hundred times more than one of those bounds is wrong, and you can say which bound it violated.
Check the direction of every ratio before the magnitude. Reads should outnumber writes on a feed. Media bytes should dwarf text bytes. Storage should grow and never shrink. A peak should exceed an average.
A ratio pointing the wrong way is a units slip or an inverted division, and it is far more common than a mis-multiplication. Fix the direction first, because a number with the wrong sign of reasoning behind it cannot be repaired by better arithmetic.
The failure these guard against is a specific one, and it is worth naming so you can hear yourself doing it: the units slip. Megabytes written where gigabytes were meant. Bits where bytes were meant — a bitrate in megabits divided by 8 is a byte rate, and forgetting that division is a factor of eight. A per-day figure compared against a per-second capacity.
Every one of those produces a confident, well-formatted, thousand-fold-wrong answer, and none of them is caught by redoing the same multiplication more carefully. Carry the units on every line, the way each block above writes 1.1e17 B before it writes 110 PB, and the slip becomes impossible rather than unlikely.
6. Numbers worth memorizing cold
Every constant the chapter spends is gathered here in one place, so you can drill them. Everything here is either a physical fact, an exact conversion, or a stated default — and where it is a default rather than a fact, the row says so, because a default silently taken is how two people estimating the same system reach different fleet sizes.
| Quantity | Value |
|---|---|
| Seconds per day | 86,400 -> 1e5 to reason, 86,400 to provision |
| Seconds per month | 2.6e6 -> 2.5e6 to reason (the one place two figures are kept: 3e6 would be 16% high) |
| 1 M/day | 10/s via 1e5 · 11.6/s exact |
| 1 B/day | 10,000/s via 1e5 · 11,574/s exact |
| Memory reference | 100 ns |
| SSD random read | 100 us latency. Device throughput is 500 k-1 M IOPS at depth, not 1e4 |
| Datacenter round trip | 500 us |
| Disk seek (HDD only) | 10 ms -> 100/s per spindle. An SSD does not seek |
| Same-continent RTT | ~30-80 ms |
| Transatlantic RTT, US-East <-> Europe | ~75 ms — the short ocean crossing. It sits inside the same-continent band above, so the band alone will not tell you whether an ocean is involved: name the route, not the number (Latency numbers and what each one forbids) |
| Cross-continent RTT, California <-> Europe | 150 ms — a different route from the row above, not that row doubled: it crosses the continent before it crosses the ocean |
2^32 | 4.3e9 |
2^41 ms | 69.7 years |
| Char (ASCII, the basic Latin character set) | 1 B |
| UUID (universally unique identifier) | 16 B binary, 36 B as text |
| Typical web page | ~2 MB |
| 1080p video bitrate | ~3-5 Mbps. Default to 3 Mbps for an average across devices and say so |
| CDN egress | ~$0.02/GB |
| Commodity box | 64-256 GB RAM, 8-64 cores, 1-10 Gbps network card. Default to 128 GB / 16 cores / 10 Gbps and say so — silently taking an end of a range is how two chapters reach opposite fleet sizes |
| Hard drive | ~20 TB per drive. A stated default, and it moves with the year |
| Rack of drives | ~600 drives fully populated (42U of 4U, 60-bay enclosures). A stated default — it is what turns a petabyte count into an object you can picture |
| Cache read, same datacenter | ~0.5 ms, which is mostly the round trip and not the lookup |
| Database read, indexed row | ~10 ms on a loaded primary; ~1 ms on SSD with a warm buffer pool. A stated assumption, not a hardware constant — it is not the 10 ms disk-seek row above |
The rows below are workload defaults rather than physical facts. They exist because the hardest part of a first estimate is not the arithmetic but inventing an input you can defend.
Every one of them is a default to be stated out loud and abandoned the moment a real number appears. Take one silently and you have committed exactly the error the commodity-box row warns about. They are order-of-magnitude anchors, and a product that is genuinely 3x off any of them is a product whose owner already knows the real figure.
| Workload default | Value |
|---|---|
| DAU as a fraction of registered users | 10-30% for a consumer product, default 20%. A social feed sits high, a utility app low |
| Sessions per user per day (consumer) | 3-10, default 5. This is the number the QPS estimate is usually most sensitive to |
| Creation actions per user per day | 0.05-1, default 0.1 — people read one to two orders of magnitude more than they write, which is where the 50:1 to 100:1 read:write ratio comes from |
| Concurrent connections per box (long-lived sockets) | 100 k-500 k. A stated default, and a 5x range that sets a connection tier’s fleet size outright — memory does not (Estimation 3) |
| B2B tool (software sold to companies, not consumers) | DAU 50-70% of seats (people use work software on work days), 1-2 long sessions/day, and a peak multiplier of 3x or more rather than 2x, because the day’s traffic lands in an 8-hour window in a handful of time zones instead of spreading across 24 |
7. Interviewer pushback
Four challenges are what this material actually draws, and each is answered below the way it should be spoken. Every answer names an assumption and its consequence rather than defending a number.
“Where did 500 million daily actives come from?”
I made it up, and I said so — it is a placeholder that makes the arithmetic concrete. What matters is the sensitivity: at 50 M this fits on a few dozen machines and the design is boring; at 5 B the storage tiering and multi-region become the whole conversation. If you have a real number I will redo it, but the shape of the design only changes at order-of-magnitude boundaries.
“Your seconds-per-day is 16% off.”
Deliberately. My input assumptions are wrong by at least 2x, so carrying 86,400 through would be false precision. If we get to a point where 16% changes a decision, that is the moment to use real numbers — and it usually means we are sizing a fleet for cost, not deciding an architecture.
“You said three boxes for 20 million connections. Would you deploy that?”
No, and that gap is the useful part of the estimate. Memory is not what sizes a connection tier — descriptors, TLS handshake CPU, and failure blast radius are. One box holding 6.7 M connections means a single restart reconnects 6.7 M clients simultaneously, which is a self-inflicted thundering herd — a flood of simultaneous identical requests that the system caused itself. I would target 100-500 k per box, so 40-200 boxes, and now the interesting design question is the reconnect-storm policy, not the RAM.
“Do I need the peak multiplier if I have autoscaling?”
Yes, for two reasons. Autoscaling reacts on the order of minutes and traffic spikes on the order of seconds, so the peak sizes your headroom, not your steady state. And the stateful tiers — databases, connection holders, anything with a warm cache — cannot scale on that timescale at all. The multiplier is really a question about which tiers can absorb a spike and which have to be provisioned for it.
Cheat sheet
The chapter compressed to what you would want in front of you five minutes before an interview.
| Four templates | QPS = DAU x actions / 1e5 · storage = items/day x bytes x days x replicas · bandwidth = QPS x bytes/response · memory = working set x bytes/entry |
| Defaults when you have nothing | DAU 20% of registered · 5 sessions/day · 0.1 creates/user/day · 128 GB/box · 20 TB/drive · 600 drives/rack. Say every one of them out loud |
| The key approximation | 86,400 -> 1e5 to reason, 86,400 to provision. Say which |
| Peak | a stated assumption: 2x smooth, 3x bursty, none if a queue absorbs it. Sizes headroom, not steady state |
| The three latencies | memory 100 ns · datacenter round trip 500 us · cross-continent 150 ms |
| Cache leverage | DB load QPS x (1-h) falls linearly to zero; the reduction factor 1/(1-h) diverges. Each extra nine divides load by ten. “Linear in h” and “linear in 1-h” are the same claim, so neither is the point |
| Binding constraint | walk CPU · memory · network · disk bytes · disk IOPS · descriptors · blast radius, and take whichever runs out first |
| Every estimate is a product | so a factor that is 2x wrong makes the answer 2x wrong. Rank assumptions by their high-to-low spread, and name the widest one out loud |
| Robustness | an estimate is sound when its conclusion survives its own uncertainty, not when its number is accurate |
| Sanity check | redo it by a different route · turn it into drives, racks or dollars · test it against a bound you already believe · check every ratio points the right way |
| The real output | not a number — the name of the binding constraint |
| Biggest mistake | computing something that does not change the design |
| Second biggest | an assumption you never said out loud |
| Third | a units slip: bits for bytes, MB for GB, per-day against per-second. Carry the units on every line |
Next: 03 — A Framework For System Design Interviews — where these numbers go in the 45 minutes.