Definition
- QPS -- daily active users x actions/day / ~100K seconds/day, then multiply by peak factor.
- Storage -- per-record bytes x daily writes x 365 x retention, plus indexes (~2x) and replication.
- Bandwidth -- peak QPS x response size (bytes to bits to Mbps/Gbps).
- Cache -- hot 20% of items x item size; check it fits a Redis node.
- Latency numbers -- memory ~100 ns, SSD ~100 us, same-DC ~500 us, cross-continent ~150 ms.
- Read/write split -- reads go to caches/replicas; writes hit the primary.
Before opening a new branch, the National Bank of India manager scribbles on an envelope: 10,000 customers, 2 visits per month, 700 visits per day, peak triple that at lunch, so size for 2,000 visits per hour. Estimate paper storage per year, and check if the vault is big enough. Same math, scaled to servers, storage, and cache.
Understanding the Concepts
How do you calculate QPS?
Start with daily active users and actions per user per day. Divide by 86,400 seconds (round to 100,000 for easy math). Multiply by a peak factor (typically 2-5x) to size for spikes.
package com.bank.capacity;
public class QpsEstimate {
public static void main(String[] args) {
long dau = 10_000_000; // daily active users
int actionsPerDay = 5; // payments per user per day
long dailyRequests = dau * actionsPerDay; // 50M
long avgQps = dailyRequests / 100_000; // ~500 (round 86400 to 100K)
long peakQps = avgQps * 4; // ~2000
System.out.printf("DAU: %,d%n", dau);
System.out.printf("Daily requests: %,d%n", dailyRequests);
System.out.printf("Avg QPS: %d, Peak QPS: %d%n", avgQps, peakQps);
}
}
Expected output:
DAU: 10,000,000
Daily requests: 50,000,000
Avg QPS: 500, Peak QPS: 2000
How do you estimate storage?
Multiply per-record size by daily writes, extend to yearly, apply retention, and add overhead for indexes and replication.
package com.bank.capacity;
public class StorageEstimate {
public static void main(String[] args) {
long recordBytes = 1024; // 1 KB per payment record
long dailyWrites = 50_000_000; // 50M payments/day
long bytesPerDay = recordBytes * dailyWrites; // 50 GB/day
long bytesPerYear = bytesPerDay * 365; // ~18 TB/year
int retentionYears = 5;
long rawStorage = bytesPerYear * retentionYears; // ~90 TB
long withIndexes = rawStorage * 2; // ~180 TB
System.out.printf("Per day: %d GB%n", bytesPerDay / (1024L * 1024 * 1024));
System.out.printf("Per year: %d TB%n", bytesPerYear / (1024L * 1024 * 1024 * 1024));
System.out.printf("5-year with indexes: %d TB --> NEEDS SHARDING%n",
withIndexes / (1024L * 1024 * 1024 * 1024));
}
}
Expected output:
Per day: 46 GB
Per year: 16 TB
5-year with indexes: 163 TB --> NEEDS SHARDING
How do you estimate bandwidth?
Multiply peak QPS by response size. Convert bytes to bits for network capacity.
package com.bank.capacity;
public class BandwidthEstimate {
public static void main(String[] args) {
long peakQps = 2000;
long responseSizeBytes = 2048; // 2 KB
long bytesPerSec = peakQps * responseSizeBytes; // 4 MB/s
long bitsPerSec = bytesPerSec * 8; // 32 Mbps
System.out.printf("Peak bandwidth: %d MB/s = %d Mbps%n",
bytesPerSec / (1024 * 1024), bitsPerSec / (1024 * 1024));
System.out.println("Comfortably within one server NIC (1 Gbps)");
System.out.println("For media-heavy responses --> need CDN");
}
}
Expected output:
Peak bandwidth: 3 MB/s = 30 Mbps
Comfortably within one server NIC (1 Gbps)
For media-heavy responses --> need CDN
How do you size a cache?
Apply the Pareto principle: 20% of items receive 80% of traffic. Multiply the hot item count by item size to get cache memory.
package com.bank.capacity;
public class CacheEstimate {
public static void main(String[] args) {
long totalAccounts = 10_000_000;
double hotFraction = 0.20; // Pareto: 20% get 80% of traffic
long hotItems = (long) (totalAccounts * hotFraction); // 2M
long itemSizeBytes = 1024; // 1 KB per cached balance
long cacheBytes = hotItems * itemSizeBytes; // 2 GB
System.out.printf("Hot items: %,d%n", hotItems);
System.out.printf("Cache size: %d GB%n", cacheBytes / (1024 * 1024 * 1024));
System.out.println("Fits one Redis node (typically 25-50 GB max)");
}
}
Expected output:
Hot items: 2,000,000
Cache size: 1 GB
Fits one Redis node (typically 25-50 GB max)
What are the latency numbers every engineer should know?
These numbers anchor all design decisions about where to place data and how many network hops are acceptable on the critical path.
| Operation | Approximate Latency |
|---|---|
| L1 cache reference | ~1 ns |
| Main memory reference | ~100 ns |
| SSD random read | ~100 us |
| Same-DC round trip | ~500 us |
| Read 1 MB from SSD | ~1 ms |
| Disk seek (HDD) | ~10 ms |
| Cross-continent round trip | ~150 ms |
package com.bank.capacity;
public class LatencyNumbers {
public static void main(String[] args) {
System.out.println("Memory: ~100 ns");
System.out.println("SSD read: ~100 us (1000x slower than memory)");
System.out.println("Same-DC hop: ~500 us");
System.out.println("HDD seek: ~10 ms (100x slower than SSD)");
System.out.println("Cross-region: ~150 ms (300x slower than same-DC)");
System.out.println("Key: avoid sequential cross-region calls on hot path");
}
}
Expected output:
Memory: ~100 ns
SSD read: ~100 us (1000x slower than memory)
Same-DC hop: ~500 us
HDD seek: ~10 ms (100x slower than SSD)
Cross-region: ~150 ms (300x slower than same-DC)
Key: avoid sequential cross-region calls on hot path
How does the estimate drive architecture decisions?
Each estimate maps to a concrete architecture choice.
| Estimate | Architecture Decision |
|---|---|
| QPS | Fleet size (via Little's Law or threads-per-pod) |
| Storage/year | Single node vs sharding |
| Read/write split | Where caches and replicas go |
| Bandwidth | Whether a CDN is mandatory |
| Latency numbers | Avoid sequential cross-region calls on hot path |
package com.bank.capacity;
public class ArchitectureFromEstimate {
public static void main(String[] args) {
long peakQps = 2000;
long storagePerYearTb = 16;
long cacheSizeGb = 1;
System.out.println("Peak QPS " + peakQps + " --> 4 pods at 500 QPS each");
System.out.println("Storage " + storagePerYearTb + " TB/year --> sharding needed");
System.out.println("Cache " + cacheSizeGb + " GB --> one Redis node");
System.out.println("Bandwidth 30 Mbps --> single NIC OK, no CDN needed");
}
}
Expected output:
Peak QPS 2000 --> 4 pods at 500 QPS each
Storage 16 TB/year --> sharding needed
Cache 1 GB --> one Redis node
Bandwidth 30 Mbps --> single NIC OK, no CDN needed
What are common mistakes?
The most common mistake is over-precision. The goal is the right power of ten, not three decimal places.
package com.bank.capacity;
public class CommonMistakes {
public static void main(String[] args) {
// WRONG: calculating to 3 decimal places
System.out.println("WRONG: 'QPS is exactly 578.703'");
System.out.println("CORRECT: 'QPS is roughly 500-600, peak ~2000'");
// WRONG: sizing for average, not peak
System.out.println("WRONG: '500 QPS average, provision for 500'");
System.out.println("CORRECT: 'peak is 4x average = 2000, provision for 2000'");
// WRONG: forgetting indexes and replication in storage
System.out.println("WRONG: '18 TB/year raw'");
System.out.println("CORRECT: '18 TB x 2 (indexes) x 3 (replicas) = 108 TB'");
}
}
Expected output:
WRONG: 'QPS is exactly 578.703'
CORRECT: 'QPS is roughly 500-600, peak ~2000'
WRONG: '500 QPS average, provision for 500'
CORRECT: 'peak is 4x average = 2000, provision for 2000'
WRONG: '18 TB/year raw'
CORRECT: '18 TB x 2 (indexes) x 3 (replicas) = 108 TB'
What should a senior engineer know?
- State assumptions out loud; the interviewer wants to see reasoning, not just numbers.
- Always size for peak, not average.
- Split reads from writes early because they have different scaling strategies.
- These numbers are the starting point; validate with real load tests before production.
- The right power of ten tells you whether you need one server or a thousand.
package com.bank.capacity;
public class SeniorInsights {
public static void main(String[] args) {
System.out.println("1. State assumptions: '10M DAU, 5 actions/day'");
System.out.println("2. Size for peak (4x average), not average");
System.out.println("3. Split reads (cache + replicas) from writes (primary)");
System.out.println("4. Validate with load tests before production");
System.out.println("5. Right power of ten > precise decimal");
}
}
Expected output:
1. State assumptions: '10M DAU, 5 actions/day'
2. Size for peak (4x average), not average
3. Split reads (cache + replicas) from writes (primary)
4. Validate with load tests before production
5. Right power of ten > precise decimal
Visual Overview
Back-of-Envelope Flow Input: 10M DAU, 5 actions/day ┌────────────────────────────────────────┐ │ QPS: 10M x 5 / 100K = 500 avg │ │ x 4 peak factor = 2000 peak │ ├────────────────────────────────────────┤ │ Storage: 1KB x 50M/day = 50 GB/day │ │ x 365 = 18 TB/year │ │ x 5 years x 2 (indexes) │ │ = 180 TB --> NEEDS SHARDING │ ├────────────────────────────────────────┤ │ Bandwidth: 2000 x 2KB = 4 MB/s (32Mb) │ │ --> single NIC OK │ ├────────────────────────────────────────┤ │ Cache: 20% of 10M x 1KB = 2 GB │ │ --> one Redis node │ └────────────────────────────────────────┘ Estimate --> Architecture QPS --> fleet size Storage --> sharding Bandwidth --> CDN? Cache --> Redis topology
Key Takeaway
A back-of-envelope capacity estimate grounds an architecture in physical reality. You derive QPS from daily active users and actions, scale daily storage to yearly, compute bandwidth from rate times payload, and size the cache from the hot fraction. The latency numbers anchor everything: memory is roughly 1,000 times faster than SSD, SSD is 1,000 times faster than HDD seek, and a cross-continent round trip is 150 ms no matter how fast the server.
The estimate drives architecture decisions: QPS sizes the fleet, storage per year decides sharding, the read/write split places caches and replicas, bandwidth decides whether a CDN is mandatory, and the latency numbers keep slow cross-region calls off the critical path.
At National Bank of India, Pankaj estimated a transaction-history service: 10M DAU at 5 actions gave 500 average and 2,000 peak QPS, the write volume projected to over 160 TB with indexes so it needed sharding, and the hot 20% of accounts fit in about 2 GB of Redis on one node.
Remember one sentence: turn users and actions into QPS, bytes per day into bytes per year, rate times payload into bandwidth, and the hot fraction into cache size, all anchored by the latency numbers, because the right power of ten tells you whether you need one server or a thousand.
---