This is Chapter 1 of the System Design series.

Architecture reviews and design discussions keep coming back to the same vocabulary regardless of what’s being built. I keep this list so I’m reasoning from consistent definitions each time, not re-deriving the same trade-offs from scratch.


Core concepts

Systems Always Lose Consistency - CAP Is Fundamental

  1. Scalability
  2. Availability
  3. Latency
  4. Consistency
  5. CAP
  6. Idempotency
  7. Fault tolerance

1. Scalability

  • Vertical scaling (bigger machine) buys time but hits a ceiling fast and creates a single point of failure.
  • Horizontal scaling (more machines) is the default for anything expected to grow past a single box, at the cost of needing to handle state, coordination, and partial failure across nodes.

2. Availability

Usually expressed in nines:

AvailabilityDowntime per year
99%~3.65 days
99.9%~8.76 hours
99.99%~52.6 minutes
99.999%~5.26 minutes

Each additional nine costs disproportionately more in redundancy, automation, and operational discipline. Match the target to what the business actually needs - five nines on an internal reporting tool is wasted spend.

3. Latency vs throughput

  • Latency is how long one request takes.
  • Throughput is how many requests the system handles per unit of time.

They trade off: batching improves throughput by adding latency to individual requests. A system tuned for one is rarely optimal for the other.

4. Consistency models

  • Strong consistency means every read sees the latest write, which requires coordination and costs latency.
  • Eventual consistency allows reads to lag behind for a window, in exchange for availability and lower latency.

Most systems don’t need strong consistency everywhere - decide it per data type, not per system.

5. CAP theorem and PACELC

  • CAP: under a network partition (P), a system chooses between consistency (C) and availability (A); it cannot have both.
  • PACELC extends this to the normal case: even without a partition, there’s a trade-off between latency (L) and consistency (C).

CAP gets cited more than it’s useful day to day - PACELC’s latency/consistency trade-off is the one that shows up in almost every real design decision.

6. Idempotency

  • An operation that produces the same result no matter how many times it’s applied.

Required for safe retries - a payment API without idempotency keys will double-charge on a network timeout where the client retries a request the server actually processed.

7. Fault tolerance

  • The system keeps serving correct results when a component fails.

Built from redundancy (no single point of failure), health checks, timeouts, and graceful degradation - not from hoping the failure doesn’t happen.


Building blocks

Layers Compose Distributed Messaging And Caches Over Vectors

  1. Load balancers
  2. Caches
  3. Databases
  4. Message queues
  5. API gateways
  6. CDNs
  7. Object storage
  8. Vector databases

1. Load balancers

Distribute requests across instances:

AlgorithmHow it worksGood fit
Round robinCycles through instances in orderUniform request cost, similar instance capacity
Least connectionsSends to the instance with fewest active connectionsVariable request duration
Consistent hashingMaps requests to instances by hash, minimising remapping when instances changeSticky sessions, cache-friendly routing

2. Caches

Sits between the application and a slower data source. The two decisions that matter:

  • Eviction policy: which entry gets removed when the cache is full.
  • Write strategy: how writes flow between the cache and the backing store.
StrategyHow it worksTrade-off
LRU evictionEvicts least recently used entrySimple; misses stable-but-infrequent hot data
LFU evictionEvicts least frequently used entryBetter for stable hot data; higher bookkeeping cost
Write-throughWrites to cache and store togetherSafe; adds write latency
Write-behindWrites to cache first, flushes to store asyncFast; risks data loss on cache failure

Cache invalidation is the hard part in practice, not lookup speed - stale data from a forgotten invalidation path causes more incidents than the cache ever saves in latency.

3. Databases

  • SQL stores structured, relational data with schemas, joins, and ACID transactions.
  • NoSQL covers a family of non-relational stores, each optimised for a different access pattern.
TypeExamplesGood fit
SQL (relational)PostgreSQL, MySQLStructured data, joins, transactions
Key-valueRedis, DynamoDBSimple lookups by key, high throughput
DocumentMongoDBSemi-structured data, flexible schema
Wide-columnCassandraWrite-heavy, time-series, large scale
GraphNeo4jHighly connected data - social graphs, recommendations

4. Message queues

Kafka, RabbitMQ, SQS, and Pub/Sub decouple producers from consumers, absorb load spikes, and let consumers fail and recover without losing work.

  • At-least-once delivery is simpler and pairs with idempotent consumers.
  • Exactly-once delivery adds real coordination complexity for a guarantee most systems don’t strictly need.

5. API gateways and reverse proxies

In front of backend services, so individual services don’t each re-implement them:

  • Centralise routing
  • Authentication
  • Rate limiting
  • TLS termination

6. CDNs

The right tool for anything that doesn’t change per-request:

  • Cache static assets at edge locations
  • Cut latency and offload origin traffic

7. Object storage

Large, immutable blobs that don’t belong in a relational database:

  • Images and video
  • Backups
  • Logs

8. Vector databases and LLM gateways

  • Vector databases store embeddings for similarity search, the backbone of retrieval-augmented generation.
  • LLM gateways centralise model routing, rate limiting, and cost tracking across providers the same way an API gateway does for internal services.

Architecture patterns

Strong Replicas Scale Easily - Microservices Matter

  1. Sharding
  2. Replication
  3. Scaling reads/writes
  4. Event-driven
  5. Microservices
  6. Multi-region

1. Sharding and partitioning

Splits data across nodes so no single node holds the whole dataset:

  • Consistent hashing on a key is the standard distribution mechanism
  • The shard key is the one decision in a sharded system that’s expensive to change later - get it wrong and every cross-shard query becomes a fan-out. Spend the most time here.

2. Replication

Copies data across nodes for redundancy and read scaling:

  • Leader-follower replication has one node accept writes and others replicate from it - simple, but the leader is a bottleneck and a single point of failure for writes.
  • Multi-leader replication accepts writes on multiple nodes, trading that bottleneck for conflict resolution complexity.

3. Scaling reads vs writes

Read and write scaling are different problems:

  • Read-heavy systems scale with replicas and caching.
  • Write-heavy systems scale with sharding, since replication doesn’t help when every node still has to process every write.

4. Event-driven architecture and pub-sub

Decouples producers from consumers through events rather than direct calls:

  • A producer doesn’t need to know who’s listening
  • New consumers can be added without touching the producer

5. Microservices

Trades a simpler deployment and debugging story for independent scaling and deployment per service:

  • Network calls replace function calls
  • Distributed-systems failure modes replace in-process ones
  • The trade-off only pays off once team size and deployment cadence justify the operational overhead

6. Multi-region architecture

Addresses latency and disaster recovery:

  • Latency - serve users from the nearest region
  • Disaster recovery - survive a regional outage

Also reintroduces the CAP trade-off at a larger scale - cross-region writes either wait for consensus or accept eventual consistency.


Capacity estimation anchor points - the numbers worth memorising so back-of-envelope math doesn’t stall a design discussion:

  • 1 day ≈ 86,400 seconds
  • DAU x average requests per user / 86,400 ≈ requests per second
  • Peak traffic is typically 2-5x average - size for peak, not average
  • Storage = record size x record count x replication factor - then add headroom for growth over the system’s expected lifetime

Trade-offs

ChoicePick the first when…Pick the second when…
SQL vs NoSQLData is structured, transactions matterSchema is fluid, scale matters more than joins
Strong vs eventual consistencyCorrectness can’t tolerate a stale read (balances, inventory)Availability and latency matter more (social feeds, view counts)
Monolith vs microservicesTeam is small, product is earlyIndependent scaling and deployment outweigh the operational cost
Normalization vs denormalizationWrite consistency and storage efficiency matterRead performance matters more than write complexity
REST vs RPC vs GraphQLPublic API, caching, simplicity matterInternal service-to-service calls (RPC) or flexible client queries (GraphQL) matter more
Batch vs stream processingLatency doesn’t matter, throughput and cost doResults are needed in near real time
ACID vs BASETransactional correctness is non-negotiableAvailability and scale outweigh strict consistency

What’s different in 2026

  • AI components (embeddings, vector search, RAG) show up in mainstream designs the same way caching and queues do - no longer a separate track.
  • Cost is a first-class constraint, not an afterthought - a design that burns budget unnecessarily is a weaker answer than one that’s cost-aware from the start.
  • Real-time is the default expectation rather than a special requirement to call out.
  • Multi-region reliability, once reserved for a handful of global platforms, is now assumed for anything with a meaningful user base.

How I work through a design problem

Careful Engineers Don’t Skip Deep Analyses

  1. Clarify requirements
  2. Estimate scale
  3. Define the API
  4. Sketch the high-level design
  5. Deep-dive
  6. Address bottlenecks
  • Clarify requirements - functional scope, scale, and the constraints that actually shape the design (latency targets, consistency needs, budget)
  • Estimate scale - rough numbers for traffic, storage, and bandwidth using the anchor points above
  • Define the API - the contract the rest of the design has to support
  • Sketch the high-level design - the major components and how data flows between them
  • Deep-dive - pick the one or two components doing the most interesting work and go deep
  • Address bottlenecks and failure modes - what breaks first under load, and what happens when it does

The order matters more than the content of any single step - skipping straight to a deep-dive before the scale is estimated produces a design that’s solving the wrong problem.


Notes

Next: Chapter 2 - URL Shortener (coming soon)