Patterns

Patterns

System design in small pieces: build a Design one rung at a time, then look up the pattern behind each rung.

A cheatsheet for designing systems, in two halves that point at each other. The Stack ladder adds one piece at a time and says when to climb. The patterns explain the idea behind each rung, what it costs, and how to set it in the game.

New here? Start at One box and climb, or jump to a pattern when a gauge on your Board turns red. Most pages draw a real Design on the game’s own board and explain every setting it uses.

Foundations#

The vocabulary the rest leans on: latency, availability, CAP and the tail.

Stack ladder#

Build a system one rung at a time, from a single box to several regions.

1
One box

Run the code and the data on one machine. It is the cheapest way to launch and the right place to start.

2
Split the server and the storage

Move the data off the App Server into its own Database Node, so code and data stop fighting over one machine and each can grow alone.

3
Stateless app servers

Keep nothing on the App Server that a second copy would need, so any copy can answer any request and a dead one costs nothing.

4
Scale up first

Give the App Server a bigger instance before you add a second one. It is the cheapest change that adds capacity.

5
Several app servers behind a load balancer

Put a Load Balancer in front of identical App Servers so traffic splits across them and capacity grows by adding copies.

6
Drop dead nodes with health checks

Have the Load Balancer probe each server and stop sending traffic to one that has failed, so a dead host costs seconds instead of a share of requests.

7
Pooler between servers and database

Put a Connection Pooler between the App Servers and the Database so many app connections share a few database connections.

8
DNS in front

Put DNS ahead of several Load Balancers so the entry point is not a single machine, and set its TTL to match how fast you need failover.

9
Static files on object storage and a CDN

Serve pages and media from the edge and keep files in Object Storage, so the origin only handles what must be fresh.

10
Add a cache

Put a Cache between the App Server and the Database so repeated reads are answered from memory and the Database keeps its capacity for writes.

11
Add a read replica

Copy the Database to a replica that answers reads, so the primary's capacity goes to writes and read traffic scales out.

12
Route reads and writes apart

Send writes to the primary and reads to a replica, and send reads that follow a write back to the primary so users never see their own change missing.

13
Search beside the database

Put full-text search on its own Search Engine, so a busy query stops taking CPU and cache from checkouts and writes.

14
Document and wide-column stores

Use a document store for data read and written by key with a changing shape, and a wide-column store when writes outgrow one machine, and accept what each gives up.

15
Split the database by feature

Give each feature its own App Server and Database, so one feature's load and failures stay in its own lane.

16
Slow work to a queue and workers

Hand work the user does not wait for to a Queue, and let Workers drain it at their own pace.

17
Message platform for fan-out

Send each notification through a Queue and Workers to a managed provider, so a slow or failing send never holds a request.

18
Functions for spiky jobs

Run bursty or rare work on a Serverless Function that bills per use and scales from zero, and keep steady load on boxes.

19
Autoscale the compute tier

Link App Servers to an Autoscaler so the number of copies follows the load, inside a floor and a cap you choose.

20
Stream changes with CDC

Read the Database's write log and publish each change to a stream, so search and other copies follow the data without a second write.

21
Reports off the primary, in a warehouse

Send report scans to a columnar Data Warehouse, so analytics stops evicting the buffer cache that checkouts depend on.

22
WAF and gateway at the front

Put a WAF and an API Gateway in front of the App Servers, so bots and bursts are stopped before they cost you capacity.

23
Auth and TLS at the gateway

Check each token once at the API Gateway, hand password hashing to an Auth Provider, and let a certificate manager renew TLS, so the front door stops costing the App Server and stops expiring.

24
Feature flags and CI for safe deploys

Ship through a pipeline that sends a canary first, and put new Features behind flags, so a bad deploy breaks a slice of users instead of all of them.

25
A WebSockets tier for realtime

Hold realtime connections open on a Realtime Hub that runs a runtime built for them, and route only realtime traffic to it, so chat and live updates stop eating App Server workers.

26
Shard the database

Split the Database into shards by a key, so writes divide across several primaries once one box at its largest size is full.

27
Distributed SQL and leaderless stores

Replace hand-made shards with a store that replicates by itself, either leaderless with tunable quorums or SQL on Raft, and pick by whether you need every read correct.

28
Logs, metrics and traces

Tap the Nodes into Analytics and research the visibility track, so you can see error causes, history and the slow hop of each request instead of guessing.

29
Split into services with a mesh

Break one codebase into Services on a Kubernetes Cluster and put a Service Mesh beside them, so each deploys alone and every call gets retries, a breaker and metrics.

30
Multi-region with GeoDNS

Open a second region, route each region's Users to it with GeoDNS, and keep reads local, so far Users stop paying the ocean twice on every request.

31
Compute at the edge

Run small handlers on Edge Functions next to the user and keep heavy work on the App Server, so cheap requests skip the trip to the origin.

32
Disaster recovery and a failover region

Back up the primary, keep a standby for zone loss and a store spread across regions for region loss, and prove it with a drill.

33
An LLM behind a gateway on GPUs

Serve AI prompts from your own GPU Nodes beside a hosted LLM API, and tune model, precision and batch size, because a GPU is cheaper per prompt only while it stays busy.

34
Vector search and embeddings

Keep embeddings of your documents in a Vector Index and look up the nearest ones before each prompt, so answers use your own data instead of only the model's.

Scaling reads and compute#

Caches, replicas, load balancers and autoscaling.

Cache-aside

Keep hot reads in memory and let the app fill the cache on a miss, so the database serves each popular row once instead of once per request.

Write-through and write-behind

Let the cache take writes too, either passing each one straight on to the database or holding it briefly, so reads never see an old value.

Cache stampede protection

Stop many readers from rebuilding the same expired cache key at once, so one popular key cannot knock the database over.

Cache warming and tiered caching

Fill a cache before it takes traffic, and put a second cache between the first and the origin, so a cold or distant cache does not send every read to the source.

Read replicas

Copy the database to followers that serve reads, so read traffic scales out while one primary takes the writes.

CDN push and pull

Put copies of your content on servers near users, either filling them on demand from your origin (pull) or uploading them yourself (push).

Vertical and horizontal scaling

Make one machine bigger, or run more machines, and know which one to reach for first.

Autoscaling, reactive and scheduled

Add copies of a service when load rises and remove them when it falls, either after the load shows up or ahead of a peak you can predict.

Connection pooling

Put a pooler between the app and the database so many app connections share a few database connections.

Load balancing algorithms

Pick how a load balancer chooses among servers, because the right rule depends on whether servers differ and whether requests do.

Layer 4 and layer 7 load balancing

Choose between a balancer that forwards TCP connections quickly and one that reads each HTTP request so it can route by content.

Fan-out on write and on read

Build a user's feed either when someone posts, by copying the post to every follower, or when the user reads, by gathering posts from everyone they follow.

Materialized views and denormalization

Store the answer to an expensive query, or keep a joined copy of the data, so reads stop doing the same work again and again.

Precomputation

Do expensive work before anyone asks for it, so a request reads a ready answer instead of building one.

Scaling data#

Sharding, replication, quorums and the stores that fit each shape.

Sharding

Split one dataset across several databases by a key, so writes and storage scale past what one machine holds.

Consistent hashing

Place keys on a ring of servers, so adding or removing one server moves only its share of the keys instead of nearly all of them.

Hot keys and hot shards

When one key or one shard takes a large share of the traffic, adding servers does not help, so spread that key or change the shard key.

Federation

Split the data by feature into separate databases, so each service owns its tables and one busy feature cannot slow the rest.

Replication strategies

Choose how copies of the data stay in step, trading write latency and lost writes against stale reads and complexity.

Quorum reads and writes

Keep several copies of each key and let a read and a write each ask enough of them to overlap, so reads see the latest write without a leader.

Consensus and leader election

Make a group of servers agree on one order of writes through a majority vote, so the data stays correct while a minority fails.

Read your writes

After a user writes, send that user's reads to the primary for a while, so they never see data older than their own change.

Full-text search and the inverted index

Index every word to the documents that hold it in a separate search engine, so a query reads a few short lists instead of scanning every row.

OLTP and OLAP split

Run reports on a separate columnar warehouse, so large scans stop evicting the cache and slowing the database that serves users.

Object storage and lifecycle rules

Keep files in object storage and let lifecycle rules move old objects to cheaper classes, so storage cost stops growing in step with every upload.

Messaging and async#

Queues, streams and the guarantees between them.

Resilience#

Timeouts, retries, breakers and the other ways to fail small.

Deploys and change#

Ship and migrate without taking the system down.

Operate and observe#

See the system, set targets, and plan for the day it breaks.

Security and edge#

The front door, encryption, secrets and where data may live.

Cost#

Pay for what you use and know what each user costs.

Outside the game#

Patterns Quorum cannot build, with references worth reading.

Saga

A saga runs a business transaction as a chain of local transactions across services and undoes finished steps with compensating actions when a later step fails.

CQRS

CQRS uses one model to change data and a different one to read it, so each side can be shaped and scaled for its own job.

Event sourcing

Event sourcing stores every change as an event in an append-only log and builds current state by replaying it.

Sidecar

A sidecar runs next to the main application in its own process or container and handles shared chores like logging, proxying and security.

Anti-corruption layer

An anti-corruption layer translates between your model and a legacy or foreign system's, so the old design does not leak into the new one.

Backends for frontends

Backends for frontends gives each kind of client, such as web, iOS and Android, its own small backend instead of one shared API.

API composition

API composition answers a query that spans several services by calling each one and joining the results in memory.

Two-phase commit

Two-phase commit makes several databases commit or abort one transaction together, by voting first and committing second, at the price of blocking when the coordinator fails.

CRDTs

A conflict-free replicated data type lets every replica accept writes on its own and still merge to the same state without coordination.

Sticky sessions

Sticky sessions make a load balancer send a client back to the server it used first, so that server's memory can hold the client's state.

Long polling and server-sent events

Long polling and server-sent events let a server push updates to a browser over plain HTTP, without the two-way connection of a WebSocket.