Learn quick estimation for system design: powers of two, latency orders of magnitude, and turning daily users into requests per second, storage, bandwidth and cache size.
What you'll learn: By the end of this lesson you will be able to turn "two million daily users" into requests per second, storage per year, bandwidth and cache memory. You will know the rough speeds of memory, disks and networks, and you will be able to sanity-check an estimate before it leads you astray.
Before a builder orders materials, she does a rough calculation: how many bricks does a wall this size need, give or take? She does not need the exact count. She needs to know whether it is a hundred bricks or a hundred thousand, because that decides whether she needs a wheelbarrow or a lorry.
Estimation in system design is the same habit. You are not predicting the future to three decimal places. You are finding the order of magnitude: do we need one server or fifty? Gigabytes or petabytes? A single database or many? Those answers decide the whole architecture, and you can get them in ten minutes with arithmetic you can do in your head.
The core idea
Estimation is a short chain: users, then actions, then requests per second, then resources. You start with an assumption about people, convert it into load, and then convert load into storage, bandwidth, memory and machines.
Two principles keep it honest.
Round aggressively. There are 86,400 seconds in a day, but 100,000 (10^5) is close enough for a first pass and far easier to divide by.
Label every assumption. Every input is a guess. Write it down so anyone can change it and see how the answer moves. A result is only as trustworthy as its weakest input, and rough inputs are fine as long as the output only feeds decisions that tolerate roughness.
Estimates earn their keep three ways. They choose the architecture (20 requests per second runs on a laptop; 200,000 needs a fleet). They find the bottleneck (the database may be trivial and the network the problem). And they catch absurd designs early (a petabyte a year does not belong in a relational table).
Numbers worth remembering
Powers of two and units
Computers count in powers of two, we talk in powers of ten, and for estimation the two are close enough to treat as the same.
Power
Exact
Approx.
Name
2^10
1,024
thousand
kilobyte (KB)
2^20
1,048,576
million
megabyte (MB)
2^30
about 1.07 billion
billion
gigabyte (GB)
2^40
about 1.1 trillion
trillion
terabyte (TB)
2^50
about 1.1 quadrillion
quadrillion
petabyte (PB)
Useful conversions:
1 day is 86,400 seconds, about 10^5 for quick maths. 1 month is about 2.5 million seconds. 1 year is about 31.5 million seconds (roughly 3 x 10^7).
Network speeds are quoted in bits per second, storage in bytes. One byte is 8 bits, so 1 Gbps is about 125 MB per second.
A million requests per day is about 12 requests per second on average (1,000,000 / 86,400). Memorise that: 10 million per day is about 120 per second.
Latency orders of magnitude
Speeds differ by factors of a thousand and more. These are rough, widely quoted approximations, and real hardware varies. What matters is the ratio between them.
Operation
Approximate time
Read from CPU cache
about a nanosecond
Read from main memory (RAM)
about 100 nanoseconds
Random read from an SSD
about 100 microseconds (0.1 ms)
Network round trip inside one data centre
about 0.5 milliseconds
Random read from a spinning disk (seek)
about 10 milliseconds
Network round trip across a continent or ocean
about 50 to 150 milliseconds
Memory is roughly a thousand times faster than an SSD, which is far faster than a network call, which is far faster than crossing an ocean. That is why caches work, why chatty services over the network are slow, and why a CDN near the user matters. No engineering removes the time light takes to cross an ocean.
From users to requests per second
Each arrow in this diagram is one multiplication or division.
flowchart LR
A["Daily active users"] --> B["Actions per user per day"]
B --> C["Requests per day"]
C --> D["Divide by 86400"]
D --> E["Average requests per second"]
E --> F["Multiply by peak factor"]
F --> G["Peak requests per second"]
G --> H["Servers, database load, cache"]
C --> I["Size per item"]
I --> J["Storage and bandwidth"]
How to read it: the top row finds load, the last arrow turns load into machines, and the branch at the bottom turns the same request count into bytes.
DAU is daily active users, usually much smaller than total registered users.
Peak factor is how much busier the busiest moment is than the average. A factor of 2 to 5 is a common starting assumption for consumer apps; without data pick 3 and say so.
Read/write ratio is how many reads happen per write. It often decides whether you invest in caches and replicas (many reads) or write scaling (many writes).
A worked example: the photo-sharing app
Everything below is an illustrative assumption. A real product would replace these with measurements.
Assumptions: 10 million registered users and 2 million DAU; each DAU views 30 photos and uploads 0.1 photos a day; an original is about 2 MB and the resized versions add about 0.5 MB (2.5 MB stored per photo); a view delivers a medium image of about 200 KB; peak factor 3; metadata is about 1 KB per photo.
Requests. Reads: 2,000,000 x 30 = 60,000,000 views a day, about 700 per second on average and about 2,100 reads per second at peak. Writes: 2,000,000 x 0.1 = 200,000 uploads a day, about 2.3 per second and about 7 per second at peak. The ratio is 60,000,000 / 200,000 = 300 to 1: upload is not the scaling problem, viewing is.
Storage. 200,000 photos x 2.5 MB = 500,000 MB, so about 500 GB per day and roughly 180 TB per year (500 GB x 365 is about 182,000 GB). That belongs in object storage, not a relational database. Metadata is 200,000 x 1 KB = 200 MB a day, about 73 GB a year. Two datasets more than a thousand times different in size belong in different kinds of storage, and estimation found that in two lines.
Bandwidth. Egress is 60,000,000 x 200 KB = 12 TB a day, about 140 MB per second, or about 1.1 Gbps on average (140 x 8 = 1,120 Mbps) and about 3.3 Gbps at peak. Serving that from your own servers is expensive, so a CDN absorbs it. If we assume it serves 90% from edge caches, about 10% reaches your origin: roughly 0.33 Gbps at peak. Ingress is 200,000 x 2 MB = 400 GB a day, modest at about 37 Mbps on average.
Cache memory. Cache each active user's feed as 100 photo IDs of 8 bytes: 800 bytes x 2,000,000 = 1.6 GB. Add a hot set of 1 million metadata records at 1 KB (1 GB) for about 2.6 GB. Double it for key and structure overhead and you get about 5 GB, which fits one modest node, though we would run two for availability.
Servers and database. If one application instance handles about 500 simple requests per second (assumed), 2,100 peak needs 4 to 5 instances, so run perhaps 8 for headroom. If the cache absorbs 90% of metadata reads, the database sees about 210 reads per second and 7 writes. One well-indexed relational database copes, so the estimate says skip sharding for now.
Quantity
Estimate (assumed inputs)
What it suggests
Peak reads
about 2,100 per second
A handful of app servers plus a cache
Peak writes
about 7 per second
Upload rate is not the bottleneck
Storage growth
about 500 GB per day, 180 TB per year
Object storage with tiering
Metadata growth
about 73 GB per year
One relational database is plenty
Peak egress
about 3.3 Gbps
A CDN is essential
Cache size
about 5 GB
One or two modest nodes
DB read load
about 210 per second behind cache
No sharding needed yet
NOTE
The point is the shape of the answer, not the digits. If real DAU is 5 million instead of 2 million, every number scales about 2.5 times and the conclusions stay the same. At 200 million they would change, and you would see it immediately.
Spiky traffic: which number do you size for?
The photo app has a gentle daily curve, so a peak factor of 3 is reasonable. Event-driven products do not. A concert on-sale can send more than a hundred times the normal traffic in the first minutes. This flowchart picks the number to design for.
flowchart TD
S["Start with expected traffic"] --> Q1{"Is traffic steady with gentle peaks?"}
Q1 -->|yes| A1["Size for average times a peak factor of 2 to 5"]
Q1 -->|no| Q2{"Can you smooth the burst with a queue or waiting room?"}
Q2 -->|yes| A2["Size for the controlled admission rate"]
Q2 -->|no| A3["Size for the raw burst or accept failures"]
A2 --> C["Also size the cheap parts that absorb the burst"]
A3 --> C
How to read it: if you can control how fast work enters the expensive part of the system, size that part for the rate you choose, and separately size the cheap front door that absorbs the rush.
Sanity checks and rounding
Compare against a known reference. If you estimate that one server does 1,000,000 database-backed requests per second, your intuition should object. A single machine often handles hundreds to a few thousand, depending heavily on the work.
Check units. Bits versus bytes, per second versus per day, KB versus MB. Most big errors are unit mistakes.
Estimate it two ways. Compute bandwidth from requests and sizes, then from storage turnover. If they differ by 100 times, an assumption is wrong.
Round to one significant digit and state the range. "Between 1,500 and 3,000 per second" is more honest than "2,083.3".
TIP
Say each step aloud: "60 million per day, divide by about 100 thousand, so about 600 per second." This makes you quick and makes errors easy to hear.
In practice: a ticketing platform prepares for a concert on-sale
NOTE
A realistic scenario, not a case history. This is a composite example built from common production patterns. It does not describe any specific company, and every number in it is an illustrative assumption.
The situation
A ticketing platform with an eight-person engineering team will put 40,000 tickets for a major concert on sale at 10:00 on a Saturday. The stack runs on Kubernetes: six API pods, PostgreSQL with one read replica, Redis, and a third-party payment gateway. Normal traffic is about 300 requests per second (assumed). The last big on-sale ended with a 40-minute outage and an apology.
Marketing expects 250,000 fans online at 10:00. Assume they arrive within five minutes, so about 833 arrivals per second. Every browser sits on the page and polls "any tickets yet?" every 5 seconds: 250,000 / 5 = 50,000 requests per second, roughly 167 times the normal 300. Meanwhile at most 40,000 tickets, about 2 per order, means around 20,000 orders at best, over perhaps ten minutes, a few dozen orders per second. The polling traffic outnumbers purchases by more than a thousand to one.
Starting architecture
How to read it: every request, including the useless polls, reaches the same API pods and the same database. If one pod handles about 200 simple requests per second (assumed), six pods give 1,200. Meeting 50,000 by brute force means roughly 250 pods.
Options considered
Option
What it fixes
What it costs
Risk
Do nothing, or add pods only
Some API capacity
Cheap to try
250 pods is the wrong fix: the database and seat-hold rows still melt, and the 5-second poll storm hits it all
Virtual waiting room: static page on a CDN, a small Redis-backed status service, fans admitted at a fixed rate
Turns a 50,000 per second spike into a controlled admission rate
A few weeks of work; a new component with its own failure modes
Unfair-feeling queues; a bug in admission
Pre-sale lottery or randomised draw
Removes the rush entirely
A product change and business buy-in
Customers dislike it if it feels arbitrary
The decision
Field
Record
Context
250,000 fans for 40,000 tickets. The expensive resource is the seat-hold and order path in PostgreSQL.
Decision
Build the waiting room. Status polls are answered from Redis with a 15 second poll hint and random jitter. Admit 60 fans per second to checkout.
Consequences
Good: the cheap tier absorbs the burst, checkout load is predictable. Bad: more components, and fans see a queue instead of a fast failure.
Revisit when
Expected concurrent fans exceed about 500,000, or checkout tests show the database cannot sustain 60 admissions per second.
The numbers behind the decision: with a 15-second poll, 250,000 / 15 is about 16,700 status requests per second. Assume the status service does about 5,000 per second per instance (it is one Redis read): four instances, eight for headroom. Admitting 60 fans per second moves 40,000 fans through in about 667 seconds, roughly 11 minutes, and if half of them buy that covers the 20,000 orders. If each admitted fan spends about 5 minutes in checkout, concurrent shoppers are 60 x 300 = 18,000 (arrival rate times time in system). At about 25 requests per checkout, that is 60 x 25 = 1,500 requests per second on the real API. At 200 per pod that is about eight pods, so scale the API tier up for the day. Most of those requests are cheap reads, so the database sees far fewer queries than the API sees requests, though the seat-hold writes still need a load test.
Rollout and verification
Ticketing platform, virtual waiting roomFans to CDN; CDN to Status service; Status service to Redis; Status service to Fans (admission tokens, 60/s); Fans to Checkout API (checkout with token); Checkout API to PostgreSQL; Checkout API to Payment gatewayFansFansCDN: waiting pageCDNwaiting pageStatus service: x8Status servicex8Redis: queue stateRedisqueue stateCheckout APICheckout APIPostgreSQLPostgreSQLPayment gatewayPaymentgatewayadmission tokens, 60/scheckout with token
Ship it for a smaller, lower-demand event first. Before the big day, load-test staging with production-sized data (a tool such as k6 can ramp virtual users) to at least 60,000 status requests per second and 100 admissions per second, which is above the plan. Dashboards in Grafana: arrivals per second, queue depth, admissions per second, status-service p99 latency and 5xx rate, Redis CPU, database transactions per second, payment error rate and CDN offload. The target, not a promise: status p99 under 300 ms, checkout p95 under 800 ms at 60 admissions per second.
The 2 a.m. failure
A show in another time zone goes on sale at 02:00 for the team. At 02:03 the page reads "status service 5xx above 20%". The Redis primary failed over, a few seconds of errors followed, and the clients retried instantly, with no backoff, doubling the incoming load.
sequenceDiagram
participant B as Browsers
participant S as Status service
participant R as Redis
B->>S: Poll every 15 s
S->>R: Read queue state
R--xS: Failover and timeouts
S-->>B: 503 errors
B->>S: Immediate retries from all clients
Note over B,S: Load doubles, pods saturate
First three checks: the status 5xx and latency panel, redis-cli INFO replication (the role and master_link_status), and kubectl get hpa and kubectl top pods for the status service. Mitigation: raise the server-sent poll interval through a flag, return 429 with Retry-After at the CDN edge for clients that poll too often, and pause admission until Redis is stable. Follow-up: ship client-side exponential backoff with jitter, serve the last known queue state from a short in-process cache for a few seconds, and rehearse a Redis failover in a game day.
Architect's notes
Estimate the burst, not the day. The daily average hides the minutes that cause outages. Ask for arrivals per second at the opening minute.
Find the expensive resource and protect it. Here it is the order path in PostgreSQL. Everything else exists to shield it.
Count polling. Clients that poll multiply load by users divided by interval. Longer, server-controlled, jittered intervals are the cheapest scaling lever.
Ask in the review: what is the plan when the estimate is 3 times too low? Pre-agree the lever (slow admissions, shed load) before the day.
Defer permanent capacity. Rent extra pods for the on-sale and scale down afterwards. Do not buy for a peak that happens a few times a year.
Common mistakes
Chasing precision. An hour on exact numbers when the decision needs the order of magnitude. Fix: round to one significant digit.
Hidden assumptions. Inputs nobody wrote down cannot be challenged. Fix: list every assumption next to the result.
Averaging instead of peaking. Systems fail at the peak, so always apply a peak factor, and for events size for the burst.
Confusing registered with daily active users. That can inflate load ten times. Fix: ask which one the number is.
Skipping the sanity check. A unit slip, such as bits versus bytes (a factor of eight), makes a result wrong by a lot. Fix: recheck units against a known reference.
Estimating only requests. Storage, bandwidth and memory often shape the design more. Fix: estimate all four, and remember replication multiplies storage cost.
Treating all requests as equal. A search or an image resize costs far more than a cached read. Fix: weight by cost and plan growth headroom.
Key takeaways
Order of magnitude is the goal. Tens, thousands or millions, not the exact figure.
Chain from users to resources. Users, actions, requests per second, then storage, bandwidth, memory and machines.
Learn a few constants. A day is about 10^5 seconds, and a million requests per day is about 12 per second.
Know the latency ladder. Memory, SSD, same-data-centre network and cross-ocean network differ by large factors.
Always apply a peak factor. For event-driven traffic, size for the burst and consider smoothing it.
Read/write ratio steers design. A 300-to-1 read-heavy workload points to caches, replicas and a CDN.
Estimates can say no. Good numbers tell you what you do not need yet, such as sharding.
Label and recheck. Mark every input as an assumption, check units, and refine when real data arrives.
Practice exercises
Exercise 1 — Starter (30 min): A messaging app has 5 million daily active users who each send 40 messages per day. Calculate average and peak messages per second (peak factor 3). Then, assuming each message is 200 bytes, calculate how much message data is created per day and per year.
Exercise 2 — Intermediate (2 hours): Redo the photo-app estimate with 20 million DAU, each viewing 50 photos and uploading 0.2 photos a day, original size 3 MB. Produce a new summary table, then write two sentences on which conclusions changed (for example, does one relational database still suffice for metadata reads behind a cache?) and which stayed the same.
Exercise 3 — Advanced (half day): Estimate a URL shortener with 100 million new links per month and a 100-to-1 read-to-write ratio. Calculate writes and reads per second at average and peak, storage over five years assuming 500 bytes per record, and the cache size needed if 20% of links get 80% of the traffic. Write down every assumption, then identify the single input that most changes your answer and explain how you would measure it for real.
Capacity and Cost - the advanced version: turning estimates into a capacity plan.
Enroll to keep reading
Lesson 3 of 12 · System Design Foundations: From Zero to Your First Design
Learn quick estimation for system design: powers of two, latency orders of magnitude, and turning daily users into requests per second, storage, bandwidth and cache size.
In the rest of this lesson
A worked example: the photo-sharing app
Spiky traffic: which number do you size for?
Sanity checks and rounding
In practice: a ticketing platform prepares for a concert on-sale
Architect's notes
Common mistakes
Enrolling is free. It unlocks every lesson in System Design Foundations: From Zero to Your First Design, tracks your progress across devices and keeps your quiz scores. The newsletter is optional.
Real architecture decisions, LLMOps patterns that survive production, and engineering leadership advice — from 12+ years of building at enterprise scale. Free. No spam. Unsubscribe anytime.
Get new articles by email
Free. We email a confirmation link first, and you can unsubscribe from any email.