Capacity math for system design
Start from daily active users and requests per user, and everything else, peak load, storage, cache size, server count, cost, falls out as a straightforward calculation. Change one assumption and the numbers move through the rest.
It covers traffic, storage, cache, bandwidth, compute, database sharding, message queues, latency budgets, availability math, and monthly cost, plus a diagram tool for sketching the architecture itself: drag on labeled component icons (or a plain box, container, or freestanding text), connect them, pick a shape, group and color them, animate the data flow between them with a traveling packet, a notification pulse, a fill bar, and more.
Every number here is an order-of-magnitude estimate. The value is in the ratios and the binding constraint, not the digits, if a result changes your architecture, verify it with a measurement before you build on it.
For background on the technique itself, ByteByteGo has a lesson on back-of-the-envelope estimation.
Capacity math for system design. Change one assumption and watch it move through the numbers below.
Everything downstream hangs off peak RPS, so get it first. The only constant you need is 86,400 seconds in a day, round it to 100,000 and your numbers land within 15%, which is well inside the error bars of every other guess you are making.
Size for peak, not average. A system sized for the mean is down every evening.
Storage is a flow problem, not a stock problem: bytes per day, times days. The two multipliers people forget sit on either side of that: compression divides the size, while indexes and replication multiply it back up.
With growth g, cumulative years form a geometric series, not a straight multiply: Y1 × ((1+g)^N − 1) / g. At 30% growth, five years comes out to 9.0 first-years, not 5.
A cache only ever helps the read path, and only for data that gets read again before it changes. It doesn't reduce how much data exists, it reduces how often you have to go get it, which is why the two numbers that matter are the hit ratio (how much traffic it absorbs) and the working set (how much of the data has to stay hot to earn that hit ratio).
Real traffic is skewed, not uniform: a small slice of keys usually accounts for most reads. That's what makes caching cheap, you're not memory-provisioning for the whole dataset, just its hot tail. Get the working-set estimate wrong, low, and you'll see a hit ratio that never reaches what you sized for.
Bandwidth has the same peak-vs-average split as everything else in this tool, but it shows up twice: the peak figure is what you provision, the link speed and NIC capacity that has to survive your busiest second. The monthly transfer figure is what you get billed for, built from average traffic over the whole month, not the peak.
CDN offload and cache hit ratio look similar but solve different problems: caching keeps repeat reads off your database while still serving them from your own servers, so they still count as egress here. A CDN in front of that serves some of those reads from the edge instead, so they never touch your origin's network at all, that's the traffic this tab actually removes.
Two different laws, two different answers, and mixing them up is the classic mistake. Throughput is bounded by CPU work: a core does 1000 ÷ cpu-ms requests per second. Concurrency is Little's Law, L = λ × W, a request waiting 200ms on a database still occupies a thread even though it burns no CPU. That's why an I/O-heavy service needs many more threads than cores.
Utilisation is where queueing theory bites. For an M/M/1 queue, latency scales as 1 / (1 − ρ). At 50% load you wait 2× service time; at 90% you wait 10×; at 99% you wait 100×. Nothing changed about the server, only how full it is.
| Utilisation | Latency × | Servers needed |
|---|
Shard count is the maximum of two independent constraints: how much data fits, and how much traffic a node takes. Sizing on storage alone is how you end up with fat, idle shards that still fall over.
Averages lie about shards. A uniform split assumes uniform keys, and real keys are Zipfian, one celebrity, one popular product, one busy tenant. Estimate your hottest shard at several times the mean and check that number instead.
A log is a buffer, and buffers have three numbers: fill rate, drain rate, and depth. Partition count is set by the greater of raw throughput and consumer parallelism, because one partition can only be read by one consumer in a group, partitions are the ceiling on how wide you can scale reads.
The number people skip is drain time. If consumers run at 1.1× the produce rate, a 30-minute outage takes five hours to clear. Recovery headroom is a design parameter, not an accident.
Latency does not average, it accumulates on the critical path and then gets amplified at the tail.
The single biggest lever is round trips, not milliseconds of compute. Three sequential database queries at 15 ms cost more than every line of your application code. Parallelise what you can, batch the rest, and count the hops before you optimise a loop.
Physics sets a floor you cannot code around: light in fibre travels about 200 km/ms, so a round trip across the Atlantic is ~70 ms no matter what runs at either end.
With 100 backend calls, a per-call p99 of 1% means 63% of user requests hit at least one slow dependency. Your service p99 is built from your dependencies' p99, and fan-out is the multiplier.
| Operation | Time | Scaled to 1 s = 1 ns |
|---|---|---|
| L1 cache reference | 1 ns | 1 s |
| Branch mispredict | 3 ns | 3 s |
| L2 cache reference | 4 ns | 4 s |
| Mutex lock / unlock | 17 ns | 17 s |
| Main memory reference | 100 ns | 2 min |
| Compress 1 KB | 2 µs | 33 min |
| SSD random read (4 KB) | ~16 µs | 4.4 h |
| Read 1 MB from memory | ~50 µs | 14 h |
| Read 1 MB from NVMe SSD | ~200 µs | 2.3 d |
| Round trip in same datacentre | 500 µs | 5.8 d |
| HDD seek | 10 ms | 4 months |
| Round trip within continent | ~40 ms | 1.3 y |
| Round trip CA → Europe | ~150 ms | 4.8 y |
| SLO | Per year | Per month | Per day |
|---|
Availability composes in two directions. Dependencies in series multiply: five services at 99.95% each give you 99.75%, worse than any single part. Redundant copies in parallel multiply the failure probability instead, which is why two 99% replicas give 99.99%.
The catch is independence. Two replicas sharing a power feed, a deploy pipeline or a config store fail together, and the arithmetic quietly stops applying.
Cost estimates are for shape, not procurement, they tell you which component you should be arguing about. In most designs one line dominates by an order of magnitude, and it is usually egress or storage rather than compute.
Two derived numbers matter more than the total: cost per user and cost per million requests. Those are the ones that have to sit below revenue per user, and they are what turn an architecture debate into a business one.
Drag "+ Box", "+ Container", or "+ Text" onto the canvas, or click any of them to add. The components panel on the left (toggle it with the button before "+ Box") holds pre-labeled icons grouped by client and identity, compute, data, messaging and network, or AI -- drag one onto the canvas or click it to drop it in the middle. A container is a large frame: drop boxes (or other containers, nested as deep as you like) inside it and it carries them when it moves. Drag from a box's edge dot to connect it to another box. Double-click a box or a connection to rename it. Click to select, then press Delete to remove, Ctrl/Cmd+D to duplicate, Ctrl/Cmd+Z to undo. Right-click a box for more actions, including its shape (rect, rounded, diamond, or borderless text), an icon, and a color. Right-click a connection to set its line style, arrow direction, or an animated flow. Shift-click to select several boxes, then Group to move them as one, Ungroup to split them apart again. To Front and To Back change which box sits on top when they overlap. Scroll to zoom, drag empty space to pan. Saved to this browser automatically, and Export/Import JSON move a diagram between browsers or back it up.