Ch. 19 · System Design

System Design Capacity Estimates and Assumptions

A worked back-of-envelope estimate for a photo app: peak requests per second, storage per year, egress bandwidth and how wrong it might be.

~6 min readbeginnerupdated Oct 11, 2026

“Let’s estimate the scale first” is the point in a system design interview where many candidates either freeze or produce a number with seven significant figures and no visible reasoning. Neither helps. A capacity estimate isn’t about getting the right answer, since nobody in the room knows it. It’s about turning a few stated assumptions into numbers big enough or small enough to change the design: one database or fifty, a CDN or not, keep everything forever or tier it to cold storage.

This guide works through one estimate end to end, a photo-sharing app, then covers the mistakes that make estimates useless.

Shortcuts worth memorising

A few conversions make the arithmetic fast enough to do out loud:

  • A day has 86,400 seconds. Round it to 100,000 for quick work, and remember that rounding understates per-second rates by about 15%.
  • One million events per day is about 12 per second.
  • A month is about 2.6 million seconds.
  • Bytes to bits: multiply by 8. Network links are sold in bits per second, storage in bytes.
  • KB, MB, GB, TB, PB each step up by 1,000 (close enough to 1,024 for estimates).

You don’t need more than that. Precise numbers like 34,722 requests per second should be said as “about 35,000”.

Working through a photo-sharing app

Start with assumptions, said out loud and written down where the interviewer can see them. Each one is a guess you can defend, and each one can be changed:

Assumption Value Why it’s reasonable
Daily active users 50 million The interviewer’s prompt, or a mid-sized social app
Photos viewed per user per day 60 A few minutes of scrolling
Photos uploaded per user per day 0.2 One in five users posts daily
Peak hour vs daily average 3x Traffic bunches into evenings
Stored original 2 MB Phone photo after compression
Derived sizes (thumbnail, feed, full screen) 0.8 MB total Generated after upload
Size of a typical view 150 KB Most views load the feed size, not the original
Metadata per photo 1 KB IDs, caption, timestamps, counters
Copies of each blob 3 Replication for durability

Then the arithmetic. It’s short enough to do on a whiteboard, but here it is as a Python script so every number below is reproducible:

DAY = 86_400
dau = 50e6
views_per_user = 60
uploads_per_user = 0.2
peak_factor = 3
original_mb, derived_mb = 2.0, 0.8
view_kb, meta_kb, replicas = 150, 1, 3

uploads = dau * uploads_per_user
views = dau * views_per_user
print(f"upload rate         avg {uploads/DAY:,.0f}/s   peak {uploads/DAY*peak_factor:,.0f}/s")
print(f"view rate           avg {views/DAY:,.0f}/s   peak {views/DAY*peak_factor:,.0f}/s")
blob_tb_day = uploads * (original_mb + derived_mb) / 1e6
print(f"new blob storage    {blob_tb_day:.0f} TB/day, x{replicas} copies = {blob_tb_day*365*replicas/1000:.0f} PB/year raw")
egress_gbps = views/DAY * view_kb * 1e3 * 8 / 1e9
print(f"egress              avg {egress_gbps:.0f} Gbit/s   peak {egress_gbps*peak_factor:.0f} Gbit/s")
python

The full script prints a few more lines:

uploads/day         10 M
upload rate         avg 116/s   peak 347/s
view rate           avg 34,722/s   peak 104,167/s
read:write ratio    300:1
new blob storage    28 TB/day, 10.2 PB/year, x3 copies = 31 PB/year raw
metadata            10 GB/day, 3.65 TB/year
egress              avg 42 Gbit/s   peak 125 Gbit/s
API servers at peak 52 (at 2000 req/s each)
Text

What the numbers say about the design

The numbers only matter if they change a decision. Read them one at a time.

Uploads are tiny: about 350 per second at peak. That’s a modest write load. A single well-indexed database could take the metadata inserts. The upload path’s hard part is moving 2 MB files reliably, not request rate, which points towards direct-to-object-storage uploads with pre-signed URLs rather than streaming bytes through API servers.

Views are about 100,000 per second at peak, a 300:1 read-to-write ratio. That’s the number that shapes the architecture. Reads need caching, read replicas or a denormalised feed store. Optimising the write path first would be effort in the wrong place.

125 Gbit/s of egress at peak. No sensible origin fleet serves that directly. Images must come from a CDN, and the cache hit ratio at the CDN becomes a cost and capacity metric. At a 95% hit ratio the origin sees about 6 Gbit/s; at 99%, about 1.3 Gbit/s.

31 PB of raw blob storage per year. Metadata is only 3.65 TB per year, small enough to fit on one large database server with room to spare, while the blobs need object storage. At this volume, storage classes matter: photos older than a few months are rarely viewed, so moving them to an infrequent-access tier, or using erasure coding (around 1.5x overhead instead of 3x) instead of three full copies, is worth real money.

About 52 API servers at peak, if each handles 2,000 metadata requests per second at around half its CPU. The per-server number is itself an assumption; measure it with a load test rather than trusting a figure from the internet. Add headroom for a zone failure: with three availability zones, losing one leaves two-thirds of the fleet, so you’d run roughly 78.

How wrong could it be?

A single estimate hides its uncertainty. Better practice is to show a range by changing the shakiest assumptions together:

sensitivity of yearly raw blob storage (PB):
  low     uploads/user 0.1  MB/upload 1.5  -> 8 PB
  likely  uploads/user 0.2  MB/upload 2.8  -> 31 PB
  high    uploads/user 0.4  MB/upload 5.0  -> 110 PB
Text

The range is more than tenfold. That’s normal, and saying so is a strength. It tells you which assumptions to validate first (how often people post, and how big the stored files really are) and that the storage design must scale smoothly across that range rather than be tuned to one number.

Mistakes interviewers listen for

Using the daily average as the design load. Systems fail at the peak. 35,000 views per second on average and 104,000 at the busiest hour are different systems. If the product has spiky events (a sports final, a ticket release), the peak factor can be 10x or more.

Mixing bits and bytes. 5.2 GB/s of image data is about 42 Gbit/s on the wire. Getting this wrong understates network needs by 8x.

Forgetting copies. Replicas, derived image sizes, backups and indexes each multiply raw size. “10 PB a year” is the logical number; the bill is for 31.

False precision. Quoting “104,167 requests per second” implies a certainty you don’t have. Say “about 100,000” and move on.

Estimating without a purpose. If the numbers don’t change any decision, you’ve spent five minutes of a 45-minute interview on arithmetic. Estimate what’s in question (storage tiering, sharding, CDN) and skip the rest.

Confusing DAU and MAU. Monthly actives can be three to five times daily actives. Use the one that matches the rate you’re computing.

Interview practice: storage for a chat service

“Estimate message throughput and five-year storage for a chat app with 20 million daily active users.”

Answer

I’d start by stating assumptions: each active user sends 40 messages a day; each stored message is about 300 bytes once you count the text, sender and conversation ids, a timestamp and index overhead; the busiest hour runs at 4x the average because chat is concentrated in evenings; and we keep three copies.

That’s 800 million messages a day, about 9,300 per second on average and around 37,000 per second at peak. Storage is 240 GB a day, roughly 438 TB over five years before replication and about 1.3 PB with three copies.

What that tells me: 37,000 writes per second at peak is too much for a single relational primary to absorb comfortably, so the message store should be partitioned, most naturally by conversation id, which keeps a conversation’s messages together for the “load recent messages” query. 1.3 PB argues for tiering old messages to cheaper storage, since people rarely scroll back years. I’d also flag the assumption I trust least, the 40 messages per user per day, because group chats with heavy users could easily double it, and say the partitioning scheme should absorb that without redesign.

Further reading

More in System Design

read ✓System Design · mid

System Design Partitioning and Hot Keys

Choosing a partition key, why hash mod N moves 91% of keys when you add a node, how consistent hashing fixes it, and how to split a hot key.

~6 min readread →
esc