Forward-deployed / Learning zone
System Designa standalone module
Lesson 01

Foundations & framework

TL;DR

Every system starts as one server. It grows by separating concerns: compute from storage, reads from writes, hot data from cold. Scaling is the art of adding capacity without redesigning. It uses load balancers, replication, caching, CDNs, message queues, and sharding. Before you design anything, back-of-envelope estimation forces you to confront the numbers: QPS, storage, bandwidth, cost. The four-step framework — scope, high-level design, deep dive, wrap-up — keeps you from getting lost in detail before you've agreed on what you're building.

🎯 For the technical PM

Why it matters — If you can't estimate QPS or spot where a system will bottleneck, you're making capacity and timeline decisions blind. The framework isn't academic. It's how your team should scope every technical bet.

What it changes in your decisions — You stop treating scaling as "we'll figure it out later." You insist on estimation before committing to architecture. You know when vertical scaling hits a wall and horizontal scaling demands statelessness.

Ask your eng team — "What's our peak QPS, and which tier hits its limit first — compute, storage, or network?"

Risk if ignored — You ship a system that works at demo scale and breaks at launch scale, or you over-provision by 10× because nobody ran the numbers.


Scaling: from one server to millions of users

The evolution follows a predictable path. Each step solves one bottleneck and introduces the next:

Scaling path
From one server to millions of users
Start
Single serverweb + DB + cache on one box
▼ separate concerns
Tier
Load balancerdistribute requests Horizontal web serversstateless, auto-scale DB replicationmaster → slaves, read fan-out
▼ reduce latency
Speed
Cache tierRedis / Memcached — read-through CDNstatic assets at the edge Stateless sessionsshared store → any server
▼ global reach
Scale
Multi-datacenterGeoDNS routing Message queuesasync processing, decoupling Database shardingpartition by shard key
Tradeoff: sharding trades join capability for write throughput — resharding needs consistent hashing (lesson 2)

Single server to separated tiers

Everything starts on one machine: web server, database, cache, static files. The first split separates compute (web/application servers) from storage (database). They scale differently: compute is stateless and horizontally scalable, while storage needs replication and consistency guarantees.

Load balancing and replication

A load balancer sits in front of web servers and distributes requests. Users hit the LB's public IP. Individual servers keep private IPs, hidden from clients. This enables horizontal scaling (add servers behind the LB) and eliminates single points of failure.

Database replication follows, typically master-slave: writes go to the master, reads fan out across replicas. If the master fails, a slave is promoted. If a slave fails, reads redirect to others. The tradeoff: replication lag means reads can be stale. That's acceptable for most features, but dangerous for financial transactions.

Caching, CDN, and statelessness

A cache tier (Redis, Memcached) sits between the web tier and the database. Read-through caching: check cache first, fall back to DB, populate cache for next read. Critical decisions: expiration policy, eviction strategy (LRU is the default), and how to handle cache-DB inconsistency during writes.

A CDN pushes static assets to edge servers near users. CDN cost is proportional to traffic, so cache expiration tuning matters — too short wastes origin bandwidth, too long serves stale content.

Statelessness means moving session data out of individual web servers into a shared store (Redis, or a dedicated session DB). This lets any request go to any server. That's what makes auto-scaling and multi-datacenter deployments possible.

Sharding: the final frontier

When the database itself becomes the bottleneck, you shard — partition data across multiple database instances. The key decision is the shard key: it must distribute data evenly and support your most common queries without cross-shard joins.

Sharding introduces three hard problems:


Back-of-envelope estimation

Jeff Dean's philosophy: "back-of-the-envelope calculations are estimates you create using a combination of thought experiments and common performance numbers." The goal isn't precision — it's sanity-checking whether a design is feasible.

The numbers you need to know

Operation Latency
L1 cache reference 0.5 ns
L2 cache reference 7 ns
Main memory reference 100 ns
SSD random read 150 μs
HDD seek 10 ms
Send 1 MB over 1 Gbps network 10 ms
Read 1 MB sequentially from SSD 1 ms
Read 1 MB sequentially from HDD 20 ms
Cross-datacenter round trip 150 ms

Key insight: memory is fast, disk is slow, network is expensive. Compress data before sending it over the network. Avoid disk seeks whenever possible.

Availability in nines

Availability Downtime/year Downtime/day
99% (two nines) 3.65 days 14.4 minutes
99.9% 8.77 hours 1.44 minutes
99.99% 52.6 minutes 8.6 seconds
99.999% (five nines) 5.26 minutes 0.86 seconds

Worked example: Twitter-scale estimation

The point isn't the exact number — it's knowing whether you need terabytes or petabytes, hundreds of QPS or hundreds of thousands.


The four-step system design framework

A structured approach that prevents you from diving into implementation before agreeing on scope:

The four-step system design framework
1
Understand & scope
3–10 min
Clarify requirements, document assumptions, establish scale
→
2
High-level design
10–15 min
Box diagrams, walk use cases, back-of-envelope check
→
3
Design deep dive
10–25 min
Algorithms, data models, consistency, failure handling
→
4
Wrap-up
3–5 min
Bottlenecks, tradeoffs, what you'd improve next

Step 1: Understand the problem and establish scope

Ask clarifying questions. Document assumptions. Determine the scale (users, QPS, storage, latency requirements). Identify the most important features. You can't design everything in 45 minutes, so scope decides what to detail and what to sketch.

Step 2: Propose high-level design and get buy-in

Draw the box diagram: clients, load balancers, web servers, caches, databases, message queues, CDNs. Walk through the main use cases against the diagram. Do a quick back-of-envelope check. Get agreement before going deeper.

Step 3: Design deep dive

Prioritize the 2–3 most critical or interesting components. This is where you discuss algorithms, data models, API contracts, consistency models, and failure handling. Balance depth with breadth — a thorough treatment of one component is better than a shallow pass across everything.

Step 4: Wrap-up

Summarize the design. Name the bottlenecks you didn't address. Propose what you'd improve next: error handling, scaling the next bottleneck, metrics and monitoring. This is where you show you understand the system's limits, not just its capabilities.


Failure modes

Practitioner checklist