System Design
Intro
System design starts with requirements, not boxes. Estimate capacity
on the back of an envelope, draw a small high-level architecture,
then deep-dive only the components that are unique or risky. Make
trade-offs explicit — every design decision has a cost.
Overview
Gather requirements
Always start with requirements. Split them into functional and
non-functional.
Functional: what does the system do? Core use cases (2-4 most
important), user-facing vs internal features, data formats, and key
end-to-end workflows.
Non-functional: how well must it do it?
- Scale — DAU, peak QPS, data volume
- Latency — p50 and p99 targets
- Availability — 99.9%, 99.99%, or higher (and the downtime
budget that implies)
- Consistency — strong or eventual, and which parts need which
- Durability — RPO/RTO targets, can we lose data
- Security — auth, encryption, compliance constraints
- Cost — budget, managed vs self-hosted preference
Do not over-specify. Focus on what differentiates this system from a
generic CRUD app.
Capacity estimation
Numbers and formulas live in references/estimation-cheatsheet.md.
Back-of-envelope framework:
- Users — DAU -> peak concurrent -> requests per second
- Storage — data per record * records per day * retention
- Bandwidth — request size * QPS in + response size * QPS out
- Compute — QPS / requests-per-instance = instance count
State assumptions explicitly: read:write ratio (most systems are
10:1 to 100:1), peak-to-average ratio (typically 3x-10x), data
growth rate (linear or exponential), and cache hit rate (often
80-95% for read-heavy workloads).
High-level design
Draw 5-10 major components. Start with the client and work inward.
For each core use case, trace the data flow with numbered arrows.
Place standard infrastructure where it belongs: load balancer, API
gateway, CDN, cache, database, queue. For each kind of data, choose
a store (relational, document, blob, cache, search index). Keep it
simple first; add complexity only when requirements demand it.
Component deep dives
For each critical component, detail:
- API design — endpoints, request/response formats, pagination
- Data model — schema, indexing, partitioning/sharding key
- Scaling strategy — horizontal vs vertical, stateless vs
stateful
- Failure handling — what happens when this component dies
- Caching — what is cached, where (client, CDN, app, DB), TTL,
invalidation
Focus deep dives on components that are unique to this system or
most likely to be bottlenecks. Don't deep-dive a stock load
balancer.
Trade-off analysis
Every decision has trade-offs. State the side you chose and why,
given the requirements:
- Consistency vs availability — CAP theorem; in a partition you
pick C or A
- Latency vs throughput — batching raises throughput at the cost
of latency
- Simplicity vs scalability — simpler designs are easier to
operate but may not scale
- Cost vs performance — caching reduces latency but raises
infrastructure cost
- Flexibility vs optimization — denormalization speeds reads but
complicates writes
Scalability patterns
- Horizontal scaling — stateless services behind a load balancer
- Database sharding — partition by user ID, tenant ID, or region
- Read replicas — offload reads, accept replication lag
- Caching layers — local cache -> distributed cache (Redis) ->
CDN
- Async processing — push non-critical work onto background
queues
- Event-driven decoupling — pub/sub between producers and
consumers
- Rate limiting — token bucket, sliding window
- Circuit breakers — prevent cascade failures when downstream is
unhealthy
Example workflows
- URL shortener: create / redirect / analytics. 100M URLs/month,
<100ms redirect, 99.99% availability. ~40 writes/s, ~4000 reads/s,
~10 TB over 5 years. Stateless API service -> Redis cache (hot
URLs) -> sharded DB. Base62 encode a unique ID for short codes,
CDN for redirect caching, async event stream for analytics.
- Real-time chat: 1:1 and group messaging, presence, history.
50M DAU, <200ms delivery, eventual consistency for history. ~500K
concurrent connections at peak, ~100K messages/s. WebSocket
gateway sharded by user, message routing service, Cassandra
(partitioned by chat_id) for storage, Redis for presence, push
notifications for offline users.
- Social news feed: post creation, feed generation, follow
relationships. 500M DAU, feed loads <500ms, eventual consistency
acceptable. ~10K posts/s writes, ~500K feed reads/s. Fan-out on
write for users with <10K followers (precompute feeds in Redis),
fan-out on read for celebrities (merge at read time). Sharded
post storage, feed cache in Redis sorted sets, media in object
storage behind a CDN.
Gotchas
Agent-specific failure modes — provider-neutral pause-and-self-check items:
- Drawing components before gathering requirements. Starting with boxes and arrows before establishing functional requirements, scale, and consistency needs produces a design that answers "what architecture looks interesting" rather than "what does this system actually need to do." Always gather functional requirements and non-functional constraints first; let them determine the components.
- Skipping capacity estimation. A design without back-of-envelope estimates has no basis for claiming it will work at the stated scale. "We'll add caching if we need it" is not an architecture — it is a hope. Estimate QPS, storage, and bandwidth before deciding on sharding, caching, or whether a single database will suffice.
- Defaulting to strong consistency everywhere. Most operations in most systems can tolerate eventual consistency, and the cost of strong consistency — coordination overhead, reduced availability, latency — is significant. Defaulting to strong consistency "to be safe" means paying that cost unnecessarily. Identify which operations actually require it (financial transactions, inventory decrement) and use eventual consistency for everything else.
- Hidden single points of failure. A design with load-balanced web servers, replicated databases, and a CDN may still have a single point of failure: the database primary, a single cache cluster, or an orchestrator with no failover. Explicitly check every component in the design for what happens if it fails; if the answer is "everything stops," that's a SPOF that needs redundancy.
- Premature sharding. Database sharding adds significant operational complexity: cross-shard queries, resharding when boundaries are wrong, and consistency across shards. Most systems do not need sharding until they are well past what a single well-tuned primary with read replicas can handle. Design for sharding (choose a good shard key) but implement it only when measured data shows the single-node approach is insufficient.
- Caching without an invalidation strategy. Adding a cache without specifying TTL, invalidation triggers, and cache-aside vs write-through strategy means the cache will eventually serve stale data indefinitely. "We'll figure out invalidation later" produces a cache that is harmful rather than helpful. Define the invalidation strategy before implementing the cache.
- Ignoring the cost of synchronous chains of services. A request that calls Service A, which calls Service B, which calls Service C in series has a latency that is the sum of all three, and fails if any one of them fails. Beyond two or three hops, this becomes brittle and slow. Identify serial chains in the design and either parallelize calls, pre-compute results asynchronously, or collapse services.
Full reference
Powers of 2
| Power |
Value |
Approx |
Common usage |
| 2^10 |
1,024 |
1 Thousand |
1 KB |
| 2^20 |
1,048,576 |
1 Million |
1 MB |
| 2^30 |
1,073,741,824 |
1 Billion |
1 GB |
| 2^40 |
~1.1 Trillion |
1 Trillion |
1 TB |
Useful shortcuts: 1 million seconds ~ 12 days. 1 billion seconds ~
32 years.
Latency numbers every engineer should know
| Operation |
Time |
| L1 cache reference |
1 ns |
| L2 cache reference |
4 ns |
| Main memory reference |
100 ns |
| Mutex lock/unlock |
17 ns |
| SSD random read |
16 us |
| Read 1 MB from memory |
3 us |
| Read 1 MB sequentially from SSD |
50 us |
| Read 1 MB sequentially from HDD |
825 us |
| HDD random read |
2 ms |
| Round trip within same datacenter |
500 us |
| Round trip CA to Netherlands |
150 ms |
Key takeaways: memory is ~100x faster than SSD for random access,
SSD ~100x faster than HDD, intra-DC network ~0.5 ms,
cross-continent ~150 ms.
DAU to QPS conversion
Formula: QPS = DAU * actions_per_user_per_day / 86400
| DAU |
Actions/User/Day |
QPS (avg) |
QPS (peak, 3x) |
| 1M |
10 |
~115 |
~350 |
| 10M |
10 |
~1,150 |
~3,500 |
| 100M |
10 |
~11,500 |
~35,000 |
| 1B |
10 |
~115,000 |
~350,000 |
Rule of thumb: 1M DAU * 10 actions/day ~ 100 QPS average, 300-500
QPS peak.
Storage estimation
| Data type |
Typical size |
| User profile (text) |
1-5 KB |
| Tweet / short post |
250 B - 1 KB |
| Chat message |
100-500 B |
| Photo (compressed) |
200 KB - 2 MB |
| Video (1 min, compressed) |
5-50 MB |
| Log entry |
200-500 B |
| DB row (typical) |
500 B - 2 KB |
Formula: total = records_per_day * avg_size * retention_days.
Example: 10M messages/day * 500 bytes * 365 days * 5 years ~ 9 TB.
Bandwidth
Formula: bandwidth = QPS * avg_size. Example: 10K QPS * 5 KB
average response = 50 MB/s = 400 Mbps outbound.
Common capacity patterns
- 1M DAU social app: ~100 QPS avg, ~500 QPS peak. ~10 GB/day
new data. ~5 TB over 2 years. 2-5 app servers, 1-2 DB servers,
Redis cache.
- 100M DAU messaging app: ~50K QPS avg, ~200K QPS peak. ~500
GB/day messages. ~500 TB over 3 years. Sharded DB cluster,
dedicated cache layer, WebSocket gateway fleet.
- 1B DAU read-heavy platform: ~500K QPS avg, ~2M QPS peak.
Multi-region CDN mandatory. Read replicas plus 95%+ cache hit
rate. Terabytes of bandwidth per day.
Server capacity rules of thumb
| Resource |
Typical limit |
| Single web server (simple API) |
1K-10K QPS |
| Single Redis instance |
100K+ ops/s |
| Single PostgreSQL (tuned) |
5K-20K QPS |
| Single Kafka broker |
100K+ messages/s |
| Single machine network |
1-10 Gbps |
| Single machine connections |
10K-100K concurrent |
Quick estimation framework
- Start with DAU and read:write ratio.
- Compute average and peak QPS.
- Estimate storage per year.
- Estimate bandwidth (QPS * size).
- Divide QPS by per-server capacity to get instance count.
- Add 2-3x headroom for growth and spikes.
- Consider geographic distribution needs.
Anti-patterns
- Drawing boxes before requirements — leads to designs that
solve the wrong problem.
- Skipping estimation — "we'll scale when we get there" usually
means the design cannot.
- Picking strong consistency by default — most systems can
tolerate eventual consistency for most operations, and the cost
of strong consistency is high.
- Single point of failure hidden in plain sight — usually the
database, the load balancer, or a "smart" orchestrator.
- Caching without invalidation strategy — stale reads outlive
the bug that introduced them.
- Premature sharding — shard when you have to, not before. The
operational cost is real.
- Synchronous chains of more than 2-3 services — latency
multiplies and any one failure takes the chain down.
1---2name: system-design3description: System design methodology — requirements, capacity estimation, component design, trade-offs. Use when designing a new distributed system, evaluating an existing architecture for scalability or cost, performing back-of-envelope capacity estimation, or preparing for a system design review.4---56# System Design78## Intro910System design starts with requirements, not boxes. Estimate capacity11on the back of an envelope, draw a small high-level architecture,12then deep-dive only the components that are unique or risky. Make13trade-offs explicit — every design decision has a cost.1415## Overview1617### Gather requirements1819Always start with requirements. Split them into functional and20non-functional.2122**Functional:** what does the system do? Core use cases (2-4 most23important), user-facing vs internal features, data formats, and key24end-to-end workflows.2526**Non-functional:** how well must it do it?2728- **Scale** — DAU, peak QPS, data volume29- **Latency** — p50 and p99 targets30- **Availability** — 99.9%, 99.99%, or higher (and the downtime31 budget that implies)32- **Consistency** — strong or eventual, and which parts need which33- **Durability** — RPO/RTO targets, can we lose data34- **Security** — auth, encryption, compliance constraints35- **Cost** — budget, managed vs self-hosted preference3637Do not over-specify. Focus on what differentiates this system from a38generic CRUD app.3940### Capacity estimation4142Numbers and formulas live in `references/estimation-cheatsheet.md`.4344Back-of-envelope framework:45461. **Users** — DAU -> peak concurrent -> requests per second472. **Storage** — data per record * records per day * retention483. **Bandwidth** — request size * QPS in + response size * QPS out494. **Compute** — QPS / requests-per-instance = instance count5051State assumptions explicitly: read:write ratio (most systems are5210:1 to 100:1), peak-to-average ratio (typically 3x-10x), data53growth rate (linear or exponential), and cache hit rate (often5480-95% for read-heavy workloads).5556### High-level design5758Draw 5-10 major components. Start with the client and work inward.59For each core use case, trace the data flow with numbered arrows.60Place standard infrastructure where it belongs: load balancer, API61gateway, CDN, cache, database, queue. For each kind of data, choose62a store (relational, document, blob, cache, search index). Keep it63simple first; add complexity only when requirements demand it.6465### Component deep dives6667For each critical component, detail:6869- **API design** — endpoints, request/response formats, pagination70- **Data model** — schema, indexing, partitioning/sharding key71- **Scaling strategy** — horizontal vs vertical, stateless vs72 stateful73- **Failure handling** — what happens when this component dies74- **Caching** — what is cached, where (client, CDN, app, DB), TTL,75 invalidation7677Focus deep dives on components that are unique to this system or78most likely to be bottlenecks. Don't deep-dive a stock load79balancer.8081### Trade-off analysis8283Every decision has trade-offs. State the side you chose and why,84given the requirements:8586- **Consistency vs availability** — CAP theorem; in a partition you87 pick C or A88- **Latency vs throughput** — batching raises throughput at the cost89 of latency90- **Simplicity vs scalability** — simpler designs are easier to91 operate but may not scale92- **Cost vs performance** — caching reduces latency but raises93 infrastructure cost94- **Flexibility vs optimization** — denormalization speeds reads but95 complicates writes9697### Scalability patterns9899- **Horizontal scaling** — stateless services behind a load balancer100- **Database sharding** — partition by user ID, tenant ID, or region101- **Read replicas** — offload reads, accept replication lag102- **Caching layers** — local cache -> distributed cache (Redis) ->103 CDN104- **Async processing** — push non-critical work onto background105 queues106- **Event-driven decoupling** — pub/sub between producers and107 consumers108- **Rate limiting** — token bucket, sliding window109- **Circuit breakers** — prevent cascade failures when downstream is110 unhealthy111112### Example workflows113114- **URL shortener:** create / redirect / analytics. 100M URLs/month,115 <100ms redirect, 99.99% availability. ~40 writes/s, ~4000 reads/s,116 ~10 TB over 5 years. Stateless API service -> Redis cache (hot117 URLs) -> sharded DB. Base62 encode a unique ID for short codes,118 CDN for redirect caching, async event stream for analytics.119- **Real-time chat:** 1:1 and group messaging, presence, history.120 50M DAU, <200ms delivery, eventual consistency for history. ~500K121 concurrent connections at peak, ~100K messages/s. WebSocket122 gateway sharded by user, message routing service, Cassandra123 (partitioned by chat_id) for storage, Redis for presence, push124 notifications for offline users.125- **Social news feed:** post creation, feed generation, follow126 relationships. 500M DAU, feed loads <500ms, eventual consistency127 acceptable. ~10K posts/s writes, ~500K feed reads/s. Fan-out on128 write for users with <10K followers (precompute feeds in Redis),129 fan-out on read for celebrities (merge at read time). Sharded130 post storage, feed cache in Redis sorted sets, media in object131 storage behind a CDN.132133## Gotchas134135Agent-specific failure modes — provider-neutral pause-and-self-check items:136137- **Drawing components before gathering requirements.** Starting with boxes and arrows before establishing functional requirements, scale, and consistency needs produces a design that answers "what architecture looks interesting" rather than "what does this system actually need to do." Always gather functional requirements and non-functional constraints first; let them determine the components.138- **Skipping capacity estimation.** A design without back-of-envelope estimates has no basis for claiming it will work at the stated scale. "We'll add caching if we need it" is not an architecture — it is a hope. Estimate QPS, storage, and bandwidth before deciding on sharding, caching, or whether a single database will suffice.139- **Defaulting to strong consistency everywhere.** Most operations in most systems can tolerate eventual consistency, and the cost of strong consistency — coordination overhead, reduced availability, latency — is significant. Defaulting to strong consistency "to be safe" means paying that cost unnecessarily. Identify which operations actually require it (financial transactions, inventory decrement) and use eventual consistency for everything else.140- **Hidden single points of failure.** A design with load-balanced web servers, replicated databases, and a CDN may still have a single point of failure: the database primary, a single cache cluster, or an orchestrator with no failover. Explicitly check every component in the design for what happens if it fails; if the answer is "everything stops," that's a SPOF that needs redundancy.141- **Premature sharding.** Database sharding adds significant operational complexity: cross-shard queries, resharding when boundaries are wrong, and consistency across shards. Most systems do not need sharding until they are well past what a single well-tuned primary with read replicas can handle. Design for sharding (choose a good shard key) but implement it only when measured data shows the single-node approach is insufficient.142- **Caching without an invalidation strategy.** Adding a cache without specifying TTL, invalidation triggers, and cache-aside vs write-through strategy means the cache will eventually serve stale data indefinitely. "We'll figure out invalidation later" produces a cache that is harmful rather than helpful. Define the invalidation strategy before implementing the cache.143- **Ignoring the cost of synchronous chains of services.** A request that calls Service A, which calls Service B, which calls Service C in series has a latency that is the sum of all three, and fails if any one of them fails. Beyond two or three hops, this becomes brittle and slow. Identify serial chains in the design and either parallelize calls, pre-compute results asynchronously, or collapse services.144145## Full reference146147### Powers of 2148149| Power | Value | Approx | Common usage |150|-------|-------|--------|--------------|151| 2^10 | 1,024 | 1 Thousand | 1 KB |152| 2^20 | 1,048,576 | 1 Million | 1 MB |153| 2^30 | 1,073,741,824 | 1 Billion | 1 GB |154| 2^40 | ~1.1 Trillion | 1 Trillion | 1 TB |155156Useful shortcuts: 1 million seconds ~ 12 days. 1 billion seconds ~15732 years.158159### Latency numbers every engineer should know160161| Operation | Time |162|-----------|------|163| L1 cache reference | 1 ns |164| L2 cache reference | 4 ns |165| Main memory reference | 100 ns |166| Mutex lock/unlock | 17 ns |167| SSD random read | 16 us |168| Read 1 MB from memory | 3 us |169| Read 1 MB sequentially from SSD | 50 us |170| Read 1 MB sequentially from HDD | 825 us |171| HDD random read | 2 ms |172| Round trip within same datacenter | 500 us |173| Round trip CA to Netherlands | 150 ms |174175Key takeaways: memory is ~100x faster than SSD for random access,176SSD ~100x faster than HDD, intra-DC network ~0.5 ms,177cross-continent ~150 ms.178179### DAU to QPS conversion180181Formula: `QPS = DAU * actions_per_user_per_day / 86400`182183| DAU | Actions/User/Day | QPS (avg) | QPS (peak, 3x) |184|------|------------------|-----------|----------------|185| 1M | 10 | ~115 | ~350 |186| 10M | 10 | ~1,150 | ~3,500 |187| 100M | 10 | ~11,500 | ~35,000 |188| 1B | 10 | ~115,000 | ~350,000 |189190Rule of thumb: 1M DAU * 10 actions/day ~ 100 QPS average, 300-500191QPS peak.192193### Storage estimation194195| Data type | Typical size |196|-----------|--------------|197| User profile (text) | 1-5 KB |198| Tweet / short post | 250 B - 1 KB |199| Chat message | 100-500 B |200| Photo (compressed) | 200 KB - 2 MB |201| Video (1 min, compressed) | 5-50 MB |202| Log entry | 200-500 B |203| DB row (typical) | 500 B - 2 KB |204205Formula: `total = records_per_day * avg_size * retention_days`.206Example: 10M messages/day * 500 bytes * 365 days * 5 years ~ 9 TB.207208### Bandwidth209210Formula: `bandwidth = QPS * avg_size`. Example: 10K QPS * 5 KB211average response = 50 MB/s = 400 Mbps outbound.212213### Common capacity patterns214215- **1M DAU social app:** ~100 QPS avg, ~500 QPS peak. ~10 GB/day216 new data. ~5 TB over 2 years. 2-5 app servers, 1-2 DB servers,217 Redis cache.218- **100M DAU messaging app:** ~50K QPS avg, ~200K QPS peak. ~500219 GB/day messages. ~500 TB over 3 years. Sharded DB cluster,220 dedicated cache layer, WebSocket gateway fleet.221- **1B DAU read-heavy platform:** ~500K QPS avg, ~2M QPS peak.222 Multi-region CDN mandatory. Read replicas plus 95%+ cache hit223 rate. Terabytes of bandwidth per day.224225### Server capacity rules of thumb226227| Resource | Typical limit |228|----------|---------------|229| Single web server (simple API) | 1K-10K QPS |230| Single Redis instance | 100K+ ops/s |231| Single PostgreSQL (tuned) | 5K-20K QPS |232| Single Kafka broker | 100K+ messages/s |233| Single machine network | 1-10 Gbps |234| Single machine connections | 10K-100K concurrent |235236### Quick estimation framework2372381. Start with DAU and read:write ratio.2392. Compute average and peak QPS.2403. Estimate storage per year.2414. Estimate bandwidth (QPS * size).2425. Divide QPS by per-server capacity to get instance count.2436. Add 2-3x headroom for growth and spikes.2447. Consider geographic distribution needs.245246### Anti-patterns247248- **Drawing boxes before requirements** — leads to designs that249 solve the wrong problem.250- **Skipping estimation** — "we'll scale when we get there" usually251 means the design cannot.252- **Picking strong consistency by default** — most systems can253 tolerate eventual consistency for most operations, and the cost254 of strong consistency is high.255- **Single point of failure hidden in plain sight** — usually the256 database, the load balancer, or a "smart" orchestrator.257- **Caching without invalidation strategy** — stale reads outlive258 the bug that introduced them.259- **Premature sharding** — shard when you have to, not before. The260 operational cost is real.261- **Synchronous chains of more than 2-3 services** — latency262 multiplies and any one failure takes the chain down.