Vibe Engines is free, interactive AI engineering & system design — runs in your browser. Pick a learning path →
The CAP theorem,
and the spectrum of consistency.
CAP is the most quoted and least understood idea in distributed systems. It's not "pick two of three" — it's a single forced choice that only matters in one specific moment. Here's what it really says, and the consistency models that live underneath it.
What CAP actually says
A distributed system tries to offer three things. Consistency — every read returns the most recent write (or an error). Availability — every request receives a non-error response, even if it isn't the latest. Partition tolerance — the system keeps working when the network drops or delays messages between nodes.
The theorem (Brewer's conjecture, proved by Gilbert & Lynch) says you can't have all three at the same time. But the framing "pick two of three" is misleading. In any real network, partitions will happen — cables fail, switches reboot, packets vanish. So partition tolerance isn't something you choose; it's something reality imposes. The actual decision is what to do during a partition: stay consistent, or stay available.
Consistency + Partition
Refuse or block requests that can't be confirmed correct. Some nodes go unavailable, but no one ever reads stale data.
Availability + Partition
Answer every request from whatever replica is reachable, even if it's behind. Reconcile the divergence once the partition heals.
Why "CA" is a myth
People often label a single-node SQL database as "CA". That's only true because a single node can't be partitioned from itself. The moment you replicate that database across two machines to survive failure, you've entered the distributed world, and a partition between them forces the same CP-or-AP choice.
So the honest reading of CAP is: P is mandatory, and you trade C against A only when P strikes. When the network is healthy, a well-built system can deliver both strong consistency and high availability — CAP says nothing about the happy path. Which is exactly the gap PACELC fills.
If you say "I'll build a CA system," a sharp interviewer will ask what happens when two replicas can't reach each other. Always frame your answer as CP or AP, and say which operations need which.
What "available" means in the proof
CAP's three words have narrow, formal meanings. Hold real databases to them and most are neither CP nor AP.
Gilbert and Lynch proved the theorem for exact definitions. Consistency means linearizability: the system behaves as if there were one copy of the data, and every read returns the most recent completed write. Availability means every request received by a node that hasn't crashed eventually gets a non-error response. Not most requests, and not only requests on the majority side: every live node, on both sides of the split. Partition tolerance means the network may lose any number of messages between nodes. The theorem then says one precise thing: while the network is split, no system can be linearizable and also answer on every live node.
Real systems rarely fit either label under those definitions:
| Setup | Linearizable? | Every live node answers during a partition? | Strict label |
|---|---|---|---|
| Raft cluster, reads checked by the leader (etcd's default) | Yes | No: nodes on the minority side refuse requests | CP |
| Leaderless store, quorum reads and writes | No (section 09 shows why) | No: a node that can't reach W or R replicas returns an error | Neither |
| Single leader, reads from async followers | No: followers lag even with no partition | No: the side without the leader can't write | Neither |
| Every node keeps a full copy, accepts local writes, merges later | No | Yes | AP |
So use CAP for the one thing it proves, and describe a real system by what it actually guarantees: which consistency model, for which operations, and what a client sees when its node is cut off. CAP is also silent on most of what you design around day to day: latency, crashed nodes, and every consistency model weaker than linearizability. Brewer made the same point in his 2012 retrospective, "CAP Twelve Years Later", and Martin Kleppmann's essay "Please stop calling databases CP or AP" argues it straight from the definitions.
Instead of "Cassandra is AP", describe the configuration: "At QUORUM it keeps serving a key while a majority of that key's replicas is reachable, and it isn't linearizable." That sentence survives the follow-up questions. The label doesn't.
PACELC — the part CAP forgets
PACELC completes the picture: if there is a Partition, trade Availability vs Consistency; Else (normal operation), trade Latency vs Consistency. The insight is that consistency has a cost even with no failures: to guarantee a read sees the latest write, replicas must coordinate, and coordination takes round-trips. Lower latency means accepting weaker guarantees.
| System | On partition | Else (normal) | Classification |
|---|---|---|---|
| Dynamo / Cassandra | Availability | Latency | PA / EL |
| BigTable / HBase | Consistency | Consistency | PC / EC |
| MongoDB (as rated in 2012) | Availability | Consistency | PA / EC |
| Spanner | Consistency | Consistency | PC / EC |
These labels describe default configurations, and defaults move. MongoDB 5.0 made majority write concern the default, which changes what a primary cut off from its majority can acknowledge. Treat a PACELC label as a question to ask of a configuration, not a property of a product.
Some stores let you slide the trade-off per request: Cassandra through its consistency level (how many replicas must answer), DynamoDB by choosing an eventually consistent or a strongly consistent read. For leaderless stores that knob is the quorum, worked through below; the replication mechanics behind it are in partitioning and replication.
The consistency spectrum
"Consistency" isn't one thing. It's a ladder of guarantees, each weaker and cheaper than the one above.
Pick the weakest model that still keeps your product correct. A bank balance needs linearizability. A "likes" counter is fine with eventual consistency. A chat app usually wants causal, so a reply never appears before the message it answers.
Consistency is chosen per operation, not per system. The same database can require a linearizable write for an account balance and serve an eventually-consistent read for that account's activity feed.
Linearizability is not serializability
One is about time, the other about transactions. Swapping them is the most common consistency mistake in interviews.
Linearizability is a recency guarantee for single operations on one object, such as a key or a register. Once a write completes, every read that starts afterwards returns that value or a newer one, and once any read has returned the new value, no later read may return the old one. It says nothing about grouping operations together. Serializability is an isolation guarantee for transactions that touch many objects: the outcome equals running the transactions one at a time in some order. That order need not match real time, so a serializable database may legally answer a read-only transaction from a slightly old snapshot.
| Linearizability | Serializability | |
|---|---|---|
| Applies to | Single reads and writes of one object | Transactions over many objects |
| Promise | Real-time recency: no read goes back in time | Same result as some serial order |
| Rules out | Stale reads; a value flipping back to an older one | Write skew, phantoms, lost updates |
| Needed for | Leader election, locks, unique usernames, compare-and-set | Invariants across rows: balances, bookings, stock |
| Typical mechanism | Consensus, or a single leader on the read path | Two-phase locking, serial execution, SSI |
Get both and you have strict serializability: transactions appear to run one at a time, in an order consistent with real time. Google's Spanner provides it under the name external consistency, using its TrueTime clock bounds to order commits. Two-phase locking and actual serial execution are typically strictly serializable too. Serializable snapshot isolation (PostgreSQL's SERIALIZABLE level) is not, by design: every read in a transaction comes from the snapshot taken at its first statement, so a write that commits while the transaction runs stays invisible to it. Isolation levels and their anomalies are covered in concurrency and locking.
"Is it consistent?" needs two answers. For single operations: is it linearizable? For transactions: what isolation level? A system can say yes to one and no to the other. The C in CAP means linearizability; the C in ACID means something else again, the application invariants that atomicity and isolation help you keep.
Tunable consistency & quorums
Leaderless stores let you dial consistency at query time. With N replicas, a write waits for W acknowledgements and a read queries R replicas. If W + R > N, every read set overlaps every write set, so each read reaches at least one replica holding the latest acknowledged write. That overlap is what people usually mean by "strong consistency" here, but it is a weaker promise than linearizability, and section 09 lists where it breaks. Lower the numbers and you trade that freshness for latency and availability.
W=3, R=1 · W+R>N
- Every read overlaps every write
- Fast reads, slow writes
- Write fails if any replica is down
W=1, R=1 · W+R≤N
- Eventually consistent
- Fast and highly available
- Reads may be stale
This is the bridge between CAP and the physical mechanics of replication — and when you need a single agreed value rather than a tunable one, you've crossed into consensus.
The quorum math, played out
Run the failure case by hand. Three replicas hold x = 1. A client writes x = 2 with W = 1: node A acknowledges, the write returns "success," and A now disagrees with B and C. A read with R = 1 lands on node C — it returns x = 1. Stale, and the client has no way to know.
Now the same write with W = 2 (A and B ack) and a read with R = 2 (hits B and C). The read set must include B — a node with the new value — because two sets of size 2 drawn from 3 nodes cannot avoid each other. The read compares versions and returns 2. That's the entire theorem behind tunable consistency:
Every configuration for N = 3, worked out (this table is exhaustively checkable — the runnable proof is below):
| W | R | W+R>3? | Guarantee | Profile |
|---|---|---|---|---|
| 1 | 1 | no (2) | eventual — stale reads possible | fastest, most available |
| 2 | 1 | no (3) | still stale-able — a common misconfig | write-safe ≠ read-safe |
| 2 | 2 | yes (4) | guaranteed overlap | balanced; survives 1 node down |
| 3 | 1 | yes (4) | guaranteed overlap | fast reads; writes fail if any node down |
| 1 | 3 | yes (4) | guaranteed overlap | fast writes; reads fail if any node down |
Note the trap in row two: W = 2, R = 1 "feels" safe — a majority acked the write! — but 2 + 1 = 3 is not greater than 3, and the single-node read can land on the one replica that missed the write. This is exactly how Cassandra's QUORUM write + ONE read surprises people in production. And the "yes" rows promise overlap, not linearizability; the next section shows what still goes wrong.
Where W + R > N stops protecting you
The overlap proof is airtight. The systems running it are not, and each gap below returns stale or lost data with the quorum rule satisfied.
The brute-force proof assumes every write lands on W of the key's own N replicas, writes never race, and a write either reaches W replicas or none. Production breaks all three assumptions:
| Case | What happens | Result |
|---|---|---|
| Sloppy quorum | The key's home replicas are unreachable, so other nodes accept the write and hand it back later (hinted handoff) | The W acks came from nodes a read never asks, so the read can miss the write until handoff |
| Concurrent writes | Two clients write one key at once, replicas see the writes in different orders, and last-write-wins keeps the higher timestamp | One acknowledged write silently disappears, and with clock skew it can be the later one |
| Partly failed write | The write reaches fewer than W replicas and the client is told it failed, but nothing rolls back the replicas that took it | Later reads may return a write the client believes never happened |
| Read during a write | The write is still spreading when the read arrives | Either value can come back, and two reads in a row can disagree |
| Stale restore | A replica holding the new value dies and is rebuilt from one holding the old value | Fewer than W replicas now hold the write, so overlap no longer covers it |
The fourth row is why quorums aren't linearizable even when nothing fails. With N = 3, a writer sends x = 2 with W = 3, and the write reaches replica C first:
t1 C has x=2; A and B still have x=1 (write in flight)
t2 reader 1: R=2, asks B and C -> 1, 2 -> returns 2
t3 reader 2 starts after reader 1 returned
R=2, asks A and B -> 1, 1 -> returns 1
Reader 2 started after reader 1 had seen the new value, and still got the old one. Every quorum rule held, and linearizability forbids exactly this.
Read repair is the partial fix: when a read sees replicas disagree, the coordinator writes the newest version back to the stale ones, and a background anti-entropy process (Dynamo and Cassandra compare Merkle trees) catches the keys nobody reads. If the reader finishes that repair before returning, and every writer first reads the latest version from a quorum, plain reads and writes on one key become linearizable, at a cost in latency. Riak skips the synchronous repair for performance. Cassandra does wait for repair on quorum reads, but still loses linearizability under concurrent writes because it resolves them by last-write-wins. And a linearizable compare-and-set ("create this username only if it's free") can't be built this way at all: it needs consensus. Try the failure cases in the quorum simulator, or size N, W and R in the quorum explorer.
"With W + R > N on strict quorums, every read reaches a replica holding the latest write acknowledged before it began, as long as writes don't race and replicas don't lose data. It isn't linearizable, so I wouldn't build uniqueness or locks on it."
Prove W + R > N by brute force
Don't take the overlap rule on faith — enumerate it. This checks every possible write set and read set for a given N, W, R and reports whether a stale read is even constructible. Then it replays the failure story from above. Change N to 5 or 7 and the rule keeps holding — that's why it's a theorem, not a heuristic.
Quick answers
What is the CAP theorem?
During a network partition, a distributed system can guarantee either Consistency (every read returns the latest write) or Availability (every request gets a non-error response), but not both. Since partitions are unavoidable, the real choice is CP vs AP.
Why can't a system be CA?
Partition tolerance isn't optional — networks fail. A pure CA system only exists on a single node. Once partitioned, it must drop either availability or consistency, collapsing into CP or AP.
What is PACELC?
If Partition, trade Availability vs Consistency; Else, trade Latency vs Consistency. It captures that stronger consistency costs latency even when nothing has failed.
Strong vs eventual consistency?
Strong (linearizable) consistency means every read reflects the latest write, at the cost of coordination latency. Eventual consistency lets replicas disagree briefly but converge, trading freshness for speed and availability.
Does W + R > N guarantee strong consistency?
No. It guarantees that every read set overlaps every write set, so each read reaches at least one replica holding the latest acknowledged write. That is weaker than linearizability: sloppy quorums, concurrent writes resolved by last-write-wins, writes that fail on some replicas but not others, and replicas restored from stale copies can all return stale or lost data. Linearizable reads and writes need synchronous read repair, and a compare-and-set still needs consensus.
What is the difference between linearizability and serializability?
Linearizability is a recency guarantee for single reads and writes: once a write completes, every later read sees it or something newer. Serializability is an isolation guarantee for transactions over many objects: the result equals some serial order, which need not match real time. Strict serializability, which Spanner calls external consistency, provides both.
The CAP theorem: why a database can't be both consistent and available
This idea in other formats
Explore the topic
See this alongside everything else on the same subject — handbooks, system designs, challenges and tools, in one place.
More Handbooks
- The System Design Fundamentals HandbookThe load-bearing ideas behind every distributed system — the CAP theorem and consistency models, concurrency and locking, partitioning and replication, and consensus and coordination — each tied to a real worked design you can study interactively.Read →
- Partitioning, Sharding & ReplicationHow one dataset becomes many — range vs hash partitioning, consistent hashing and virtual nodes, hot partitions, replication topologies, replication lag, and quorums. Part of System Design Fundamentals.Read →
- Consensus, Transactions & CoordinationHow nodes that can crash still agree on one truth — majority quorums, Raft and Paxos, leader election, two-phase commit vs the saga pattern, and idempotency for exactly-once effects. Part of System Design Fundamentals.Read →
- The Kubernetes HandbookThe one idea under all the YAML — declare desired state, and a control loop makes reality match. Covers the orchestration problem, pods, deployments and replicasets, services and networking, the reconciliation loop and self-healing, the scheduler, config/secrets and health probes, autoscaling, and when you actually need Kubernetes.Read →
- The Observability HandbookSeeing inside production — monitoring vs observability, the three pillars (metrics, logs, traces) and what each answers, structured logging, metric types and the cardinality trap, distributed tracing, the golden signals and SLIs/SLOs/error budgets, alerting on symptoms not causes, and correlating all three during an incident.Read →
- The Networking HandbookHow a request actually travels — the layered model, IP addressing and routing, TCP vs UDP, the TCP handshake and head-of-line blocking, DNS resolution and caching, HTTP/1.1 vs HTTP/2 vs HTTP/3 (QUIC), TLS/HTTPS, and why round-trip latency, not bandwidth, dominates.Read →