What the theorem actually says
Three properties, with the definitions that make the proof work:
- Consistency — specifically linearizability. Every read sees the most recent completed write, as if there were a single copy of the data and operations happened one at a time in real-time order. This is a much stronger property than "the data is eventually the same everywhere," and much stronger than an ACID database's "C".
- Availability — every request to a non-failing node returns a non-error response. Not "mostly up". Not "up with degraded latency". A node that is reachable must answer.
- Partition tolerance — the system keeps working when the network arbitrarily drops or delays messages between nodes.
The theorem: you cannot have all three simultaneously.
The proof is a paragraph. Partition the network into two halves. A client writes to one half, another client reads from the other. If both must respond (availability), the reader cannot know about the write (no messages cross), so it returns stale data — consistency lost. If it refuses to answer to avoid staleness, availability is lost.
The misreading that ruins it
"Pick two of three" implies all three are on the menu. They aren't.
You do not choose P. Partitions are a property of networks, not an architectural option. Cables get cut, switches misconfigure, a GC pause makes a node indistinguishable from an unreachable one. If you're running on more than one machine, you must tolerate partitions or you simply lose the data when one happens.
So the real statement is much narrower:
That last part is why CAP is a poor design tool on its own. It describes a rare emergency, and most of your engineering effort goes into the other 99.9% of the time.
What CP and AP look like in practice
| Choice during partition | Concretely | |
|---|---|---|
| CP | Minority side stops serving | ZooKeeper/etcd: without a quorum, nodes reject requests. HBase: the region is unavailable until reassigned. |
| AP | Every side keeps answering | Cassandra/Dynamo with low quorums: all replicas answer, divergence repaired later. DNS: famously stale, famously up. |
Two honest caveats, both worth saying out loud:
- Most "CP" systems are not truly linearizable. Many databases advertised as CP default to snapshot isolation or allow stale reads from followers unless you explicitly ask for a leader read.
- Most "AP" systems are not truly always-available. If enough nodes are down, a Cassandra
read at
QUORUMfails too. "AP" describes a preference under partition, not a promise.
PACELC: the part you'll actually use
Daniel Abadi's extension is the more useful formulation, because it covers the normal case:
If there is a Partition, choose Availability or Consistency; Else, choose Latency or Consistency.
The "else" branch is the real design decision. Even with a perfectly healthy network, keeping replicas consistent means waiting for them — and that wait is measured in the round-trip times you can't argue with. A cross-region linearizable read costs the speed of light whether or not anything is broken.
| System | PACELC | Reading |
|---|---|---|
| Cassandra (default) | PA/EL | Stays up during partition; favours latency normally |
| DynamoDB | PA/EL | Same, with per-request ConsistentRead to opt into C |
| Spanner | PC/EC | Refuses rather than diverge; pays coordination cost always |
| MongoDB (default) | PC/EC | Primary-only writes; consistency preferred both branches |
| PostgreSQL + async replica | PC/EL | Single primary is consistent; follower reads trade C for L |
It's a dial, not a switch
The framing that actually survives contact with a real design: consistency is chosen per operation, not per system.
In one product you might have:
- Payment capture — linearizable, single region, refuse rather than double-charge. The cost of a wrong answer dwarfs the cost of an error page.
- Follower count — eventually consistent, served from the nearest replica. Nobody is harmed by a count that's 3 seconds stale.
- Session token check — read from a local replica, accept a small staleness window on revocation, with a short TTL bounding the damage.
Cassandra exposes this as tunable consistency per query (ONE, QUORUM, ALL); DynamoDB
as a per-read flag. Saying "CAP is chosen per operation, and here's the one operation in
this design where I'd pay for linearizability" is a genuinely senior answer.
Walking a partition, concretely
A cluster of 5 nodes across two datacenters — 3 in Zurich, 2 in Frankfurt. The link between them dies. Both sides are healthy internally.
CP choice (quorum = 3): Zurich has 3 nodes, forms a quorum, keeps serving reads and writes. Frankfurt has 2, cannot reach quorum, and rejects everything — including reads, because it cannot know whether Zurich has newer data. Frankfurt users see errors. When the link heals, Frankfurt catches up from the log. No data was lost, no conflicting writes exist.
AP choice: Both sides serve. A Zurich user and a Frankfurt user update the same record. Both succeed. When the link heals, you have two versions of one record and no ordering between them — you now need last-write-wins (silent data loss), version vectors plus application merge, or a CRDT.
Neither is correct in the abstract. Which is right depends entirely on whether a wrong answer is worse than no answer — and that's a product question, not an engineering one. The interview answer that lands is: "For the ledger, CP — I'd rather show an error than double-spend. For the feed, AP — stale posts are fine, an error page is not."
What to take away
- CAP's C is linearizability and its A is every non-failing node answers. Both are stricter than the everyday meanings.
- You never choose P. The real choice is C-or-A during a partition, and CAP is silent the rest of the time.
- PACELC adds the branch that matters daily: consistency costs latency even when nothing is broken.
- The decision is per operation. Real systems mix linearizable money paths with eventually consistent everything-else.
- Anything requiring uniqueness or an atomic invariant needs coordination. No AP trick substitutes.
Check yourself
-
In the CAP theorem, what does the C stand for precisely?
CAP's C is linearizability, which is far stronger than eventual consistency and unrelated to the ACID C. Conflating them is the most common source of muddled CAP answers.
-
Why is 'pick two of three' a misleading way to describe CAP?
You do not get to opt out of partition tolerance on a multi-machine system. The meaningful statement is conditional: when a partition happens, either refuse requests or serve possibly-stale ones. Outside a partition, CAP constrains nothing.
-
What does the 'EL' in a PACELC classification of PA/EL mean?
PACELC reads: if Partition, choose A or C; Else, choose L or C. The Else branch describes the healthy-network case, where keeping replicas in sync still costs round trips. It is the branch that applies almost all the time.
-
Which requirement cannot be satisfied by an AP design, no matter the conflict-resolution strategy?
Uniqueness is an invariant across the whole keyspace. Two partitioned halves can each accept the same username while believing they are correct, and no merge function can retroactively make one of them not have happened. Uniqueness requires coordination.