Engineering Atlas › System Design Fundamentals

A map of core system design ideas

15 min read · last reviewed 2026-10-17

Open in the interactive app: practice, quiz, mock interview →

In one minute

How it works

The map

#ConceptThe idea in one lineGo deeper
1Client-serverClients ask, servers own the data and answerClient-Server Architecture
2ScalabilityGrow capacity by bigger machines (vertical) or more machines (horizontal)Scalability
3AvailabilityThe fraction of time the system answers correctly; nines multiply down a chainAvailability
4ReliabilityDoing the right thing consistently, measured as failure rate and time between failuresReliability
5Single point of failureAny component whose loss stops the whole systemSingle Point of Failure (SPOF)
6Latency, throughput, bandwidthTime per request, requests per second, and the size of the pipeLatency vs Throughput vs Bandwidth
7Load balancingOne address in front of a pool, routing around dead backendsWhat are Load Balancers?, Load Balancing Algorithms
8DNSNames to addresses, cached at every layer by TTLDomain Name System (DNS), DNS Load Balancing
9ProxiesForward proxies act for clients; reverse proxies act for serversProxy vs Reverse Proxy
10CDNServe bytes from a point of presence near the userContent Delivery Network (CDN)
11CachingKeep repeated answers in fast memoryWhat is Caching?, Caching Strategies
12Eviction and invalidationDecide what leaves the cache and when cached data is wrongCache Eviction Policies, Cache Invalidation, Cache Stampede
13Database typesPick the store by access pattern: relational, document, key-value, wide column, graphCommon Database Types, SQL vs NoSQL
14ACID transactionsAll-or-nothing, isolated, durable changes to several rowsACID Transactions
15IndexingExtra structures that turn scans into lookups at a write costIndexing, B-Trees and B+ Trees, LSM Trees
16ReplicationCopies of data for read scale and survivalDatabase Replication
17Partitioning and shardingSplit data so no machine holds all of itDatabase Partitioning, Sharding
18Consistent hashingPlace keys on a ring so adding a node moves few keysConsistent Hashing
19CAP theoremDuring a partition, choose consistency or availabilityCAP Theorem
20Consistency modelsWhat a reader may see: linearizable, causal, eventual and othersConsistency Models
21Message queuesBuffer work so producers and consumers run at their own paceMessage Queues, Dead Letter Queues
22Pub/sub and eventsPublish once, many independent subscribers reactPub/Sub, Event-Driven Architecture
23API stylesREST, gRPC, GraphQL and when each fitsAPI Styles, REST API Design, gRPC Deep Dive
24API gatewayOne front door for auth, routing and quotasAPI Gateways, API Gateway Pattern
25Rate limitingCap how fast a client may call, to protect capacity and fairnessRate Limiting
26IdempotencyRepeating a request has the same effect as doing it onceIdempotency, Delivery Semantics
27Monolith vs microservicesOne deployable versus many independently owned servicesMonolithic Architecture, Microservices Architecture
28Resilience patternsTimeouts, retries, circuit breakers and bulkheads stop failures spreadingCircuit Breaker Pattern, Bulkhead Pattern, Handling Failures in Distributed Systems
29Consensus and leader electionMachines agree on one value or one leader despite failuresConsensus Algorithms, Raft Algorithm, Leader Election
30ObservabilityLogs, metrics and traces that let you explain behaviour from outsideThree Pillars of Observability, Distributed Tracing

Five families

Moving traffic (1, 2, 6 to 10, 24, 25) is about getting a request from a user to a machine that can answer it, quickly and without overloading anyone. Storing data (11 to 18) is about where bytes live, how many copies exist and how to find them fast. Communicating (21 to 23, 26, 27) is about how services talk: synchronously when the caller needs the answer now, asynchronously when it can wait. Surviving failure (3 to 5, 28, 30) is about the assumption that something is always broken. Coordinating (19, 20, 29) is about what happens when several machines must agree.

The families map onto recurring problem shapes: overload (load balancing, rate limiting, queues), hot spots (caching, CDNs, partition keys), partial failure (replication, timeouts, circuit breakers), duplicates (idempotency) and finding things fast (indexes). Name the shape and the table tells you which rows to read.

How the concepts interact

Each concept obliges you to handle another. Caching (11) forces invalidation (12) and consistency (20). Sharding (17) makes cross-shard transactions (14) expensive, pushing you to sagas and idempotent steps (26). Replication (16) helps availability (3) but CAP (19) then forces a choice during partitions. Retries (28) become a retry storm without rate limits (25). In a review, name the concept each box embodies and the one it now obliges you to handle.

Down the stack

Layer 1The contract

The concepts compose into the two numbers users feel: end-to-end latency and availability. For calls made one after another, latencies add. For calls made in parallel, the slowest one decides. For dependencies that must all work, availabilities multiply: five services at 99.9% each give about 99.5% (System Design Primer). For a request that fans out to n backends, if each is slow with probability p, the chance that at least one is slow is 1 - (1 - p)^n. At p = 1% and n = 100, that is 63%: a rare tail on one server becomes the common case for the page. This is the example Dean and Barroso use to open their paper on tail latency (summary of The Tail at Scale).

Layer 2The mechanism

Follow one request to a product page. DNS (8) resolves the name from a cache. The CDN (10) serves static assets. A load balancer (7) picks a gateway instance (24), which checks a token, applies a rate limit (25) and routes to the page service. That service calls auth, then fans out in parallel to pricing, stock and reviews, each reading through a cache (11) in front of a replicated, sharded database (16, 17). Every call carries a deadline and a trace ID (30).

gRPC makes the budget concrete. The client's deadline travels as a grpc-timeout header, a number of at most 8 digits plus a unit such as m for milliseconds (gRPC HTTP/2 protocol). When a server makes its own outgoing call, gRPC can pass on the deadline minus the time already spent, so downstream services stop working on requests whose caller has given up. Two details catch teams out: gRPC sets no deadline by default, so a call can wait forever, and propagation is on by default in Java and Go but must be turned on in C++ (gRPC deadlines guide).

Layer 3The machine

Each hop has a hardware floor. A cached DNS answer takes under 1 ms; a full recursive lookup 20 to 100 ms. A new TLS 1.3 connection costs one extra round trip on top of the TCP handshake, about 1 ms in a region and 50 to 150 ms across continents, which is why connection pooling matters. TLS 1.3 also allows a zero round-trip resumption mode, but data sent that way can be replayed, so it suits only idempotent requests (RFC 8446). A Redis GET across the data centre takes 0.2 to 0.5 ms, mostly network, epoll_wait and socket copies rather than Redis. A Postgres page miss to NVMe adds about 100 us, to a network block device 0.5 to 1 ms. A 10 to 50 ms garbage-collection pause shows up as tail latency on every caller.

Why it's built this way

  1. Users notice latency above roughly 100 ms, lose their flow of thought after about 1 second and their attention after about 10 (Nielsen Norman Group). So each request gets a budget, split across hops.
  2. Hops are expensive and the network is unreliable. So systems cache near the user, pool connections and fan out in parallel instead of in series.
  3. Parallel fan-out makes tail latency dominant. So services set per-call timeouts, hedge slow requests, and treat p99 rather than the average as the target.
  4. Every added component multiplies failure probability. So each must earn its place, and the critical path is kept as short as the requirements allow.

Diagram

flowchart LR
    U["User"] --> D["DNS (8)"]
    U --> C["CDN (10)"]
    C --> LB["Load balancer (7)"]
    LB --> G["Gateway + rate limit (24, 25)"]
    G --> P["Page service"]
    P --> A["Auth"]
    P --> PR["Pricing"]
    P --> S["Stock"]
    P --> R["Reviews"]
    PR --> K["Cache (11)"]
    K --> DB["Sharded replicas (16, 17)"]
    S --> Q["Queue (21)"]

Worked example

A product page has a 300 ms p99 target at the load balancer. The team draws the call tree and assigns budgets.

The gateway spends 5 ms on token checks and routing. The page service calls auth (p99 20 ms), then calls pricing (p99 60 ms), stock (p99 35 ms) and reviews (p99 120 ms) in parallel, then spends 25 ms rendering. The critical path is gateway, auth, reviews, render: 5 + 20 + 120 + 25 = 170 ms. That fits, with 130 ms of headroom.

Then reviews adds a recommendation call at p99 150 ms, in series after its 120 ms read. Reviews becomes 270 ms and the page 320 ms. Tuning pricing or stock cannot help: they are off the critical path. Options: run the two reviews calls in parallel (page 200 ms), load recommendations asynchronously, or cap reviews at 100 ms with an empty fallback.

The tail compounds too. Reviews reads 10 shards in parallel; if each is slow 1% of the time, 1 - 0.99^10 is about 9.6% of pages waiting on a slow shard. Hedged requests, a second copy to another replica once the first has been outstanding longer than the p95 latency, cut that for about 5% extra load, because by definition only one request in twenty waits that long (summary of The Tail at Scale). The practice task builds this calculator.

In the wild

Trade-offs

OptionStrengthsWeaknessesChoose when
Sequential callsSimple, each call can use the last one's resultLatencies add; one slow call delays everythingCalls genuinely depend on each other
Parallel fan-outTotal latency is the slowest call, not the sumTail latency dominates; more load per requestIndependent data for one page
Async via queueRemoves the work from the user's pathResult is not ready immediately; needs status handlingWork the user does not need to wait for
Degrade with a timeoutBounded latency, page still rendersPartial results, more client logicOptional parts of a response

Pitfalls in production

Leadership lens

Check yourself

  1. Why does a page that fans out to 100 backends see tail latency far more often than any single backend?

    Show answer

    The page waits for the slowest call. If each backend is slow 1% of the time, the chance that at least one of 100 is slow is 1 - 0.99^100, about 63%.

  2. A service is on the page's critical path at 120 ms; another runs in parallel at 60 ms. Which one should you optimise first?

    Show answer

    The 120 ms one. Speeding up the parallel 60 ms call changes nothing until it becomes the slowest branch.

  3. Why do sharding and idempotency often appear together in a design?

    Show answer

    Sharding makes multi-row transactions span machines, so designs use sagas or retried steps instead, and retried steps are only safe when each one is idempotent.

  4. Why propagate deadlines rather than set a fixed timeout at each hop?

    Show answer

    A shrinking deadline lets downstream services stop work the caller has already abandoned, which saves capacity during overload and keeps the total within the user's budget.

Sources and further study

Primary

Systems docs

Books

Other curricula

Watch

Free resources

Open in the interactive app: practice, quiz, mock interview →