Part I — Beginner: Foundations
The building blocks of almost every design, assuming no prior distributed- systems knowledge. By the end of Part I you can estimate load, draw a standard web architecture, design an API, choose a database, add caching, and scale reads.
3. Back-of-the-Envelope Estimation
3.1 Numbers to memorize
Time
1 day = 86,400 s ≈ 10^5 s (use 100k for mental math)
1 month ≈ 2.6M s ≈ 2.5 * 10^6
1 year ≈ 31.5M s ≈ 3 * 10^7
1M requests/day ≈ 12 req/s
100M requests/day ≈ 1,200 req/s
1B requests/day ≈ 12,000 req/s
Sizes (powers of two)
2^10 ≈ 1 thousand → KB 2^30 ≈ 1 billion → GB
2^20 ≈ 1 million → MB 2^40 ≈ 1 trillion → TB 2^50 → PB
Latency (order of magnitude; exact values vary with hardware)
L1 cache reference ~1 ns
Branch mispredict ~5 ns
Main memory reference ~100 ns
Compress 1 KB (fast codec) ~2-10 µs
Read 1 MB sequentially from memory ~10-250 µs
SSD random read (4 KB) ~16-100 µs
Round trip within a datacenter ~0.5 ms
Read 1 MB sequentially from SSD ~1 ms
HDD seek ~2-10 ms
Read 1 MB sequentially from HDD ~5-20 ms
Round trip US East <-> US West ~60-70 ms
Round trip US <-> Europe ~80-150 ms
Round trip US <-> Australia ~150-200 ms
The ratios matter more than the exact values: memory is ~100× faster than SSD, SSD is ~10-100× faster than HDD for random access, and a cross-continent round trip costs as much as thousands of in-DC RPCs. That last point is why synchronous cross-region calls on the hot path are almost always wrong.
Throughput of a single node (rough, modern hardware)
| Component | Ballpark per node |
|---|---|
| Stateless app server (simple JSON API) | 1k–10k req/s per core-heavy instance |
| Redis / Memcached | 100k–1M ops/s |
| Postgres / MySQL (simple indexed reads) | 10k–50k QPS; writes 1k–10k TPS (fsync-bound) |
| Cassandra / ScyllaDB node | 10k–100k+ writes/s |
| Kafka broker | hundreds of MB/s; ~1M small msgs/s per cluster is routine |
| Network NIC | 10–100 Gbps |
3.2 The estimation recipe
1. Users: DAU, and actions per user per day
2. QPS: avg = DAU * actions / 86,400; peak = avg * (2-5x)
3. Storage: writes/day * object size * retention (* replication factor 3)
4. Bandwidth: QPS * payload size (ingress and egress separately)
5. Memory: cache the hot set (often 20% of the daily read volume, Pareto)
6. Machines: peak QPS / per-node capacity, then add headroom (N+2, ~50-60% target util)
3.3 Worked example: a Twitter-like timeline
Assumptions
DAU = 200M
Tweets per user per day = 0.5 → 100M tweets/day
Timeline reads per user = 20/day → 4B reads/day
Tweet size = 300 bytes text + 200 bytes metadata ≈ 500 B
Media: 10% of tweets have media, avg 500 KB
Write QPS
100M / 86,400 ≈ 1,160 tweets/s avg; peak 3x ≈ 3,500/s
Read QPS
4B / 86,400 ≈ 46,000 timeline reads/s avg; peak ≈ 140,000/s
→ read:write ≈ 40:1 → read-optimized design (precompute timelines)
Storage (text)
100M * 500 B = 50 GB/day → ~18 TB/year → ×3 replication ≈ 55 TB/year
Storage (media)
10M * 500 KB = 5 TB/day → ~1.8 PB/year → object storage + CDN, not the DB
Egress
140k reads/s * 20 tweets * 500 B ≈ 1.4 GB/s of text at peak
(media egress dominates by orders of magnitude → CDN is mandatory)
Timeline cache (fan-out-on-write)
Store last 800 tweet IDs per active user: 800 * 8 B = 6.4 KB/user
200M users * 6.4 KB ≈ 1.3 TB → ~a few dozen 64 GB cache nodes (with replicas)
Decisions this forced: a precomputed timeline cache (read-heavy), media in object storage behind a CDN (petabytes), and tweet text in a horizontally scalable store (tens of TB per year). That is the purpose of estimation.
3.4 Estimation mistakes interviewers notice
- Forgetting peak vs average. Design for peak, and pay for the average using autoscaling.
- Forgetting the replication factor (×3) and index overhead (often +30–100%).
- Mixing bits and bytes in bandwidth (a 10 Gbps NIC moves ~1.25 GB/s).
- Presenting false precision. "About 50k QPS" is better than "46,296 QPS".
- Not stating which decision the number drives.
3.5 🏗️ Flow: estimation as a decision tree
Assumptions (DAU, actions/user, object sizes, retention, peak factor)
│
├──▶ Write QPS ──▶ < 5k/s ? ──yes──▶ single relational primary OK
│ └──no──▶ sharded SQL / wide-column / log-structured store
│
├──▶ Read QPS ──▶ read:write > 10:1 ? ──yes──▶ cache tier + replicas / precompute
│
├──▶ Storage/yr ──▶ > 10 TB hot ? ──yes──▶ partitioning + tiering (hot/warm/cold)
│ └── blobs? ──▶ object store + CDN, metadata only in DB
│
├──▶ Egress ──▶ > 1 Gbps sustained ? ──yes──▶ CDN mandatory; cost is now egress-dominated
│
└──▶ Concurrent connections ──▶ > 1M ? ──yes──▶ dedicated connection gateway tier
(memory per conn × conns per node)
3.6 🌍 In production: real numbers to calibrate against
| System | Published number | What it teaches |
|---|---|---|
| Twitter timelines (QCon 2013 talk, "Timelines at Scale") | ~300k QPS of timeline reads vs a few thousand tweets/s of writes; home timelines kept in Redis, capped at ~800 entries | Read:write of about 100:1 justifies fan-out on write |
| WhatsApp (2012 blog) | ~2 million concurrent TCP connections on a single FreeBSD/Erlang server | Connection count, not QPS, can size a chat fleet |
| Slack + Vitess (2020 blog) | ~2.3M QPS at peak (2M reads, 300k writes), median query 2 ms, p99 11 ms | What a well-run sharded MySQL fleet delivers |
| Discord on ScyllaDB (2023 blog) | 177 Cassandra nodes → 72 ScyllaDB nodes; p99 reads from 40–125 ms down to 15 ms | Storage engine and GC behavior change fleet size by 2–3× |
| Ticketmaster, Eras Tour presale (2022 statement) | ~3.5B system requests, 4× their previous peak, driven heavily by bots | Peak estimates must include abusive traffic |
| Alibaba Singles' Day (2019) | ~544k orders per second at peak | Flash-sale peaks are 100× normal levels |
3.7 ⚠️ Corner cases & deep details
Per-row and per-object overhead is not zero.
Postgres heap tuple header ≈ 23 B (+ alignment padding to 8 B) + 4 B line pointer
A "100-byte row" occupies ≈ 130-140 B on disk, before indexes.
Each B-tree index entry ≈ 16-20 B + key size. 3 secondary indexes can double the footprint.
Free space (fillfactor, MVCC dead tuples before VACUUM) → budget +20-50%.
LSM stores: size-tiered compaction can temporarily need ~2× the data size in free disk.
→ "50% disk full" may already be your real ceiling.
Object storage: small objects are priced per request.
10B objects × $0.005 per 1k PUT = $50k just to write them once → batch small items into larger files.
Things candidates forget to multiply by:
- Retries and duplicates. At 1% error rate with 3 retries, offered load can rise sharply during incidents, which is exactly when you have the least capacity.
- Fan-out. One user request → 20 backend calls → 20× internal QPS.
- Indexes, replicas, backups, snapshots, and logs. The "real" storage is often 5–10× the raw data.
- Connection memory. WebSocket gateways use tens of KB per connection (buffers, TLS state, app state). 1M connections × 50 KB = 50 GB of RAM.
- Ephemeral ports. A proxy making outbound connections to one backend IP:port has ~28k–64k source ports. High connection churn (no keep-alive) exhausts them, so connections fail while CPU is idle.
- Skew. The average shard load is meaningless when the hottest shard has 10× the average under a Zipf distribution. Estimate the hottest key and the hottest shard separately.
- Diurnal plus event peaks. Daily peak ≈ 2–3× average. Event peaks (sports finals, New Year, launches) can be 10–100× and arrive in seconds.