System DesignIntermediate7 min readPart 8 of 14

CAP Theorem

The CAP theorem is the core trade-off of distributed systems. A distributed system can guarantee at most two of three properties: Consistency, Availability and Partition tolerance. You need to understand it to design any distributed system.

Can you explain cap theorem out loud?

I'm Alex. Read this, then I'll interview you on a design problem and push on the parts you skip.

The three propertiesCore idea

%%{init: {"look":"handDrawn","handDrawnSeed":7,"theme":"base","fontFamily":"Virgil, \"Segoe Print\", \"Comic Sans MS\", cursive","themeVariables":{"fontFamily":"Virgil, \"Segoe Print\", \"Comic Sans MS\", cursive","fontSize":"16px","primaryColor":"#fff4e6","primaryBorderColor":"#1e1e1e","primaryTextColor":"#1e1e1e","secondaryColor":"#e7f5ff","tertiaryColor":"#ebfbee","lineColor":"#1e1e1e","textColor":"#1e1e1e","mainBkg":"#fff4e6","nodeBorder":"#1e1e1e","clusterBkg":"#f8f9fa","edgeLabelBackground":"#ffffff","actorBkg":"#e7f5ff","actorBorder":"#1e1e1e","noteBkgColor":"#fff9db","noteBorderColor":"#1e1e1e"}}}%%
graph TD
  C["Consistency<br/>every read sees the latest write"] --- A["Availability<br/>every request gets a response"]
  A --- P["Partition tolerance<br/>works despite network splits"]
  P --- C
  class C blue
  class A green
  class P orange
classDef orange fill:#ffd8a8,stroke:#1e1e1e,color:#1e1e1e
classDef blue fill:#a5d8ff,stroke:#1e1e1e,color:#1e1e1e
classDef green fill:#b2f2bb,stroke:#1e1e1e,color:#1e1e1e
classDef red fill:#ffc9c9,stroke:#1e1e1e,color:#1e1e1e
classDef violet fill:#d0bfff,stroke:#1e1e1e,color:#1e1e1e
classDef yellow fill:#ffec99,stroke:#1e1e1e,color:#1e1e1e
classDef grey fill:#e9ecef,stroke:#1e1e1e,color:#1e1e1e
DiagramPick two. Since partitions always happen, the real choice is C or A.

Consistency (C)

Every read gets the most recent write, or an error. All nodes see the same data at the same time. Example: after you change your profile picture, every request shows the new one.

Availability (A)

Every request gets a non-error answer, though maybe not the latest data. Example: even before your new picture reaches every server, profile requests still work, briefly showing the old one.

Partition tolerance (P)

The system keeps working when the network splits nodes apart. Machines crash, cables get cut, switches fail: in a distributed system this is not optional.

The trade-off: C or A during a partitionCompare

Because network partitions can't be avoided, the real choice is what to do while one is happening:

%%{init: {"look":"handDrawn","handDrawnSeed":7,"theme":"base","fontFamily":"Virgil, \"Segoe Print\", \"Comic Sans MS\", cursive","themeVariables":{"fontFamily":"Virgil, \"Segoe Print\", \"Comic Sans MS\", cursive","fontSize":"16px","primaryColor":"#fff4e6","primaryBorderColor":"#1e1e1e","primaryTextColor":"#1e1e1e","secondaryColor":"#e7f5ff","tertiaryColor":"#ebfbee","lineColor":"#1e1e1e","textColor":"#1e1e1e","mainBkg":"#fff4e6","nodeBorder":"#1e1e1e","clusterBkg":"#f8f9fa","edgeLabelBackground":"#ffffff","actorBkg":"#e7f5ff","actorBorder":"#1e1e1e","noteBkgColor":"#fff9db","noteBorderColor":"#1e1e1e"}}}%%
graph TB
  subgraph CP["CP: stay consistent"]
    direction LR
    c1["Node 1"] -.-|link down| c2["Node 2"] --> r1["reject:<br/>error or timeout"]
  end
  subgraph AP["AP: stay available"]
    direction LR
    a1["Node 1"] -.-|link down| a2["Node 2"] --> r2["answer anyway:<br/>may be stale"]
  end
  CP ~~~ AP
  class c1,c2 blue
  class a1,a2 green
  class r1 red
  class r2 yellow
classDef orange fill:#ffd8a8,stroke:#1e1e1e,color:#1e1e1e
classDef blue fill:#a5d8ff,stroke:#1e1e1e,color:#1e1e1e
classDef green fill:#b2f2bb,stroke:#1e1e1e,color:#1e1e1e
classDef red fill:#ffc9c9,stroke:#1e1e1e,color:#1e1e1e
classDef violet fill:#d0bfff,stroke:#1e1e1e,color:#1e1e1e
classDef yellow fill:#ffec99,stroke:#1e1e1e,color:#1e1e1e
classDef grey fill:#e9ecef,stroke:#1e1e1e,color:#1e1e1e
DiagramThe same network split, handled two ways.

CP systems (consistency + partition tolerance)

During a partition, refuse requests rather than serve stale data. Availability is given up to stay consistent.

HBase

Strongly consistent; requests fail during partitions.

MongoDB (majority writes)

Guarantees consistency but may reject writes.

ZooKeeper

Consensus-based; unavailable during leader election.

Google Spanner

Globally consistent, using TrueTime clocks.

AP systems (availability + partition tolerance)

During a partition, keep answering, possibly with stale data. Consistency is given up to stay available.

Cassandra

Always writable; eventually consistent.

DynamoDB

Highly available; eventually consistent by default.

CouchDB

Multi-master replication with eventual consistency.

DNS

Always resolves, but may return stale records.

CA systems

Only possible when there are no partitions: a single node, or a perfectly reliable network (which does not exist). A single PostgreSQL server is CA.

A real-world decision frameworkHow it works

%%{init: {"look":"handDrawn","handDrawnSeed":7,"theme":"base","fontFamily":"Virgil, \"Segoe Print\", \"Comic Sans MS\", cursive","themeVariables":{"fontFamily":"Virgil, \"Segoe Print\", \"Comic Sans MS\", cursive","fontSize":"16px","primaryColor":"#fff4e6","primaryBorderColor":"#1e1e1e","primaryTextColor":"#1e1e1e","secondaryColor":"#e7f5ff","tertiaryColor":"#ebfbee","lineColor":"#1e1e1e","textColor":"#1e1e1e","mainBkg":"#fff4e6","nodeBorder":"#1e1e1e","clusterBkg":"#f8f9fa","edgeLabelBackground":"#ffffff","actorBkg":"#e7f5ff","actorBorder":"#1e1e1e","noteBkgColor":"#fff9db","noteBorderColor":"#1e1e1e"}}}%%
graph TD
  Q1{"is stale data<br/>acceptable?"} -->|no| CP["choose CP<br/>ZooKeeper, HBase"]
  Q1 -->|yes| Q2{"is downtime<br/>acceptable?"}
  Q2 -->|no| AP["choose AP<br/>Cassandra, DynamoDB"]
  Q2 -->|"yes, single node"| CA["CA<br/>single-node PostgreSQL"]
  class Q1,Q2 yellow
  class CP blue
  class AP green
  class CA grey
classDef orange fill:#ffd8a8,stroke:#1e1e1e,color:#1e1e1e
classDef blue fill:#a5d8ff,stroke:#1e1e1e,color:#1e1e1e
classDef green fill:#b2f2bb,stroke:#1e1e1e,color:#1e1e1e
classDef red fill:#ffc9c9,stroke:#1e1e1e,color:#1e1e1e
classDef violet fill:#d0bfff,stroke:#1e1e1e,color:#1e1e1e
classDef yellow fill:#ffec99,stroke:#1e1e1e,color:#1e1e1e
classDef grey fill:#e9ecef,stroke:#1e1e1e,color:#1e1e1e
DiagramTwo questions pick the side.
SystemChoiceWhy
BankingCPYou can’t show a wrong balance. Reject the transaction instead.
Social media feedAPA feed a few seconds old is fine; users care that it always loads.
Shopping cartAPFrom the Amazon Dynamo paper: better an item appears twice than disappears.
DNSAPA slightly old IP address beats no answer.
Leader electionCPThere must be exactly one leader, even if that means a short outage.

PACELC: the extension to CAPScale

PACELC adds the normal case: if there is a Partition, choose A or C; Else, choose Latency or Consistency. Even without failures, keeping replicas in sync costs time.

ClassDuring a partitionNormallyExamples
PA/ELAvailabilityLow latencyCassandra, DynamoDB
PA/ECAvailabilityConsistencyMongoDB
PC/ELConsistencyLow latencyPNUTS (Yahoo)
PC/ECConsistencyConsistency (higher latency)Google Spanner

Common interview mistakesPitfalls

Claiming all threeYou can’t. During a partition you must pick C or A.
Confusing consistency with consensusConsistency is about fresh data; consensus is about nodes agreeing on a value.
Ignoring that most web systems are APIn practice, most apps choose availability over strong consistency.
Not mentioning PACELCIt shows you understand the trade-off beyond failures.
Forgetting single-node systemsOne PostgreSQL server is CA: it has no partitions to handle.

SummarySummary

TypeDuring a partitionNormal operationExamples
CPReject requestsStrong consistencyZooKeeper, HBase, Spanner
APServe stale dataLow latencyCassandra, DynamoDB, DNS
CAN/A (no partitions)Strong consistency + high availabilitySingle-node PostgreSQL

Frequently asked questionsFAQ

What does the CAP theorem actually guarantee?

During a network partition between nodes, a distributed system must choose between consistency, every read returning the latest write, and availability, every request returning a valid though possibly stale response. It cannot promise both while the partition lasts; after it heals, nothing prevents both.

Is the CAP trade-off relevant in normal operation?

Not most of the time: CAP applies only during partitions, so the choice describes behavior in failure. Labels like CP and AP say what happens when nodes split, not a permanent property in steady state, which is why engineers treat it as failure design rather than a daily toggle.

How do engineers actually choose CP versus AP?

Decide from the product: a payment ledger prefers CP, while a like counter tolerates AP eventual consistency. In practice systems combine both, such as a strong-consistency primary for critical data plus asynchronously replicated caches, so the answer is per-feature rather than system-wide.

Now explain cap theorem to me out loud

I'll give you a design problem and push on the trade-offs, like a real interviewer.

Practice with Alex

Keep readingKeep reading

Ready to practice?

Reading is step one. Saying it out loud is step two.

Start a mock interview with Alex, get pushed on the parts you skip, and see your hiring signal right after.