The Role of Data in Precise Capacity Estimations for System Design
Reference Numbers to Memorize
Capacity estimation is the skill of back-of-the-envelope math that tells you whether your system design will survive its traffic before you write a line of code.
π― System Scenario & Requirements
System Goal
Capacity estimation is the skill of back-of-the-envelope math that tells you whether your system design will survive its traffic before you write a line of code.
Actors & Components
- Application or API tier
- Core service under design
- Operator or SRE observer
Functional Requirements
- TLDR: Capacity estimation is the skill of back-of-the-envelope math that tells you whether your system design will survive its traffic before you write a line of code.
- Four numbers do most of the work: DAU, QPS, Storage/day, and Bandwidth/day .
- Step 1 β DAU to QPS $$\text{QPS} = \frac{\text{DAU} \times \text{requests per user per day}}{86400 \text{ seconds}}$$ Example β Twitter-scale: - 100M DAU, each user generates 10 requests/day (timelines, searches, posts).
Constraints & Assumptions
β‘ Execution Stages & Decision Trace
Stage 1 β Estimation Decision Flow
Use this decision tree to structure any capacity estimation from scratch.
Start with DAU and follow the branches to determine what infrastructure tier your system actually needs β single server, horizontally scaled, or fully distributed.
Deep Dive
Scenario context
Capacity estimation is the skill of back-of-the-envelope math that tells you whether your system design will survive its traffic before you write a line of code. Four numbers do most of the work: DAU, QPS, Storage/day, and Bandwidth/day.
flowchart LR
DAU["Daily Active Users (e.g., 10M)"] --> QPS["Convert to QPS (requests/sec)"]
QPS --> Storage["Storage/day (data written)"]
QPS --> Baβ¦
This walkthrough focuses on **Reference Numbers to Memorize**.
Before making a design choice, clarify:
-
**Concepts in play:** system-design-hld-architectures, caching, jvm-internals-and-garbage-collection, consistency-replication
## Stage 1 β Estimation Decision Flow
- Use this decision tree to structure any capacity estimation from scratch.
- Start with DAU and follow the branches to determine what infrastructure tier your system actually needs β single server, horizontally scaled, or fully distributed.
- The key decision point is the scale tier .
- A system handling 100 RPS and one handling 100,000 RPS require fundamentally different architectures β not just bigger servers, but different data stores, caching strategies, and deployment models.
*Applies [**system-design-hld-architectures**](/concept/system-design-hld-architectures) to the decision above.*
```mermaid
flowchart TD
Start[Start Estimation] --> DAU[Estimate DAU]
DAU --> RW[Estimate Read/Write Ratio]
RW --> QPS[Calculate QPS DAU actions / 86400]
QPS --> Peak[Multiply by 2-3x for peak]
Peak --> Storage[Calculate Storage/day write QPS record size 86400]
Storage --> BW[Calculate Bandwidth read QPS response size]
BW --> Scale{Scale tier?}
Scale -->|< 1000 RPS| Small[Single server PostgreSQL]
Scale -->|1k-100k RPS| Med[App servers + cache + sharding]
Scale -->|> 100k RPS| Large[Distributed system CDN + microservices]
Observable outcome: A system handling 100 RPS and one handling 100,000 RPS require fundamentally different architectures β not just bigger servers, but different data stores, caching strategies, and deployment models.
Stage 2 β Real-World Application: Capacity Estimation Across Scale Tiers
- The best way to internalize the four-step pipeline is to run it on systems you already use every day.
- Here are three scenarios at very different scales that demonstrate how the numbers drive architecture decisions.
- Twitter/X-Scale Feed - DAU: 400M users.
- Each user reads ~10 timeline requests/day; each user writes ~0.1 tweets/day.
Observable outcome: Each user reads ~10 timeline requests/day; each user writes ~0.1 tweets/day.
Stage 3 β Deep Dive: Worked Example β Design a Pastebin
- Write QPS = (1M Γ 1 paste/day) / 86400 β 12 writes/sec - Read QPS = 12 Γ 10 = 120 reads/sec - Storage: 12 writes/sec Γ 10 KB Γ 86400 = ~10 GB/day β 10 TB over 3 years.
- Read bandwidth: 120 reads/sec Γ 10 KB = 1.2 MB/sec β no CDN needed at this scale.
- What this tells you: - A single PostgreSQL can comfortably handle sub-1000 writes/sec.
- Storage backend should be durable object storage to handle 10 TB over 3 years.
Observable outcome: - Storage backend should be durable object storage to handle 10 TB over 3 years.
Stage 4 β Practical: Interview Estimation Template
- In a system design interview, the interviewer is watching whether you have a structured process β not whether you arrive at the exact right number.
- Here is a reusable five-step template you can follow verbatim: Quick Reference Decision Rules Two threshold rules that short-circuit most infrastructure architecture debates: - If storage 1 TB/day β use object storage (β¦
- A relational database is not designed for multi-terabyte raw ingest at sustained write rates.
- If read bandwidth 1 GB/s β put a CDN in front.
Step 1 β State assumptions:
- DAU: X million
- Read:Write ratio: X:1
- Average object size: X KB/MB
Step 2 β Calculate QPS:
- Write QPS = DAU Γ writes_per_user / 86400
- Read QPS = Write QPS Γ read_write_ratio
- Peak QPS = Average QPS Γ 3
Step 3 β Calculate Storage:
- Storage/day = write QPS Γ record_size Γ 86400
- 5-year storage = storage/day Γ 365 Γ 5
- Account for replication: Γ 3
Step 4 β Calculate Bandwidth:
- Read bandwidth = read QPS Γ response_size
- CDN threshold: > 1 GB/s read bandwidth
Step 5 β Determine scale tier:
- < 1k RPS: single server
- 1k-100k RPS: horizontal scaling + cache
- > 100k RPS: distributed + CDN
Observable outcome: - If read bandwidth 1 GB/s β put a CDN in front.
Stage 5 β Spring Boot + JMeter: From Capacity Estimates to Measured Throughput
- Spring Boot is the standard Java framework for building the HTTP services whose QPS, storage, and latency you estimate.
- Apache JMeter is the open-source load testing tool that validates those estimates against a running service β generating synthetic traffic at the calculated peak load and measuring actual throughput and p95 latency.
- The workflow: estimate peak QPS on paper β implement the endpoint β run a JMeter test plan at estimated load β compare results against your SLA targets.
- Instrument the endpoint to compare live p95/p99 against your paper estimate: JMeter test plan β simulating estimated peak load: Capacity estimate vs.
// Close the estimation feedback loop: @Timed captures actual latency from live traffic
// so you can compare measured p95 against the "p95 < 50ms at 11,600 RPS" estimate.
@GetMapping("/api/users/{id}")
@Timed(value = "user.lookup.latency",
percentiles = {0.50, 0.95, 0.99}, // exported to Prometheus/Grafana/Actuator
description = "Cache-aside: Redis ~0.5ms hit, DB ~5ms miss")
public ResponseEntity<UserDto> getUser(@PathVariable String id) {
// L1 β Redis: serves ~90%+ of requests at sub-millisecond cost
UserDto cached = cache.opsForValue().get("user:" + id);
if (cached != null) return ResponseEntity.ok(cached);
// L2 β PostgreSQL: cache miss, fills cache for next request (5-minute TTL)
return userRepository.findById(id)
.map(u -> { cache.opsForValue().set("user:" + id, u, 5, MINUTES); return ResponseEntity.ok(u); })
.orElse(ResponseEntity.notFound().build());
}
// If Actuator shows p99 > 100ms β add a read replica or extend Redis TTL
<!-- Thread Group: 1000 concurrent users, 5-minute sustained load at peak QPS -->
<!-- This maps to: 11,600 RPS Γ· 1000 users β 11.6 requests/sec per user -->
<ThreadGroup>
<numThreads>1000</numThreads> <!-- virtual users -->
<rampTime>60</rampTime> <!-- ramp up over 60 seconds -->
<duration>300</duration> <!-- hold load for 5 minutes -->
<HTTPSamplerProxy>
<domain>localhost</domain>
<port>8080</port>
<path>/api/users/${userId}</path> <!-- CSV Data Set: randomised user IDs -->
<method>GET</method>
</HTTPSamplerProxy>
<!-- Add Listeners: Aggregate Report, Response Time Graph, Active Threads Graph -->
</ThreadGroup>
Observable outcome: Instrument the endpoint to compare live p95/p99 against your paper estimate: JMeter test plan β simulating estimated peak load: Capacity estimate vs.
Stage 6 β Key Lessons from Back-of-Envelope Estimation
- Five lessons that separate engineers who nail capacity estimation from those who overcomplicate it: 1.
- The goal is the scale tier, not the exact number.
- Whether your estimate lands at 80,000 RPS or 120,000 RPS doesn't change the architecture decision: both require distributed infrastructure with a load balancer and horizontal scaling.
- A 10Γ error is usually tolerable; a 100Γ error is a design problem.
flowchart TD
Start[Start: State Assumptions] --> DAU[DAU actions/user]
DAU --> QPS[Avg Write QPS = DAU writes / 86400]
QPS --> Peak[Peak QPS = Avg 2-3x]
Peak --> RQPS[Read QPS = Write QPS read: write ratio]
RQPS --> Storage[Storage/day = write QPS record size 86400]
RQPS --> BW[Bandwidth/day = read QPS response size]
Storage --> Scale{Scale tier?}
BW --> CDN{BW > 1 GB/s?}
CDN -->|Yes| AddCDN[Add CDN layer]
CDN -->|No| NoCDN[Direct serving OK]
Scale -->|under 1k RPS| Small[Single server]
Scale -->|1k-100k RPS| Med[Horizontal + cache]
Scale -->|over 100k RPS| Large[Distributed + CDN]
Observable outcome: A 10Γ error is usually tolerable; a 100Γ error is a design problem.
End-to-end outcome
The Role of Data in Precise Capacity Estimations for System Design
Alternatives considered
The source focuses on one primary design. A second architecture is not compared, so no alternative is invented here.
Failure and degraded modes
Trade-offs & Failure Modes: Trade-offs, Failure Modes & Decision Guide: Estimation Mistakes
- Detection: Trade-offs & Failure Modes: Trade-offs, Failure Modes & Decision Guide: Estimation Mistakes
- User or operator impact: Trade-offs & Failure Modes: Trade-offs, Failure Modes & Decision Guide: Estimation Mistakes
