system-design

CAP Theorem (and why PACELC is better)

The most cited and most misunderstood idea in distributed systems. Here’s what it actually says, what it doesn’t, and the framework that’s more useful in practice.

Prerequisites: Consistency Models, Availability Time to read: ~16 minutes


What CAP actually says

Three properties:

The theorem: during a network partition, you must choose between consistency and availability. You cannot have both.


🚨 The misconception you must not repeat

Almost everyone says: “CAP says pick 2 of 3.” This framing is wrong and interviewers notice.

You do not get to choose P. Network partitions are a fact of the physical world — cables get cut, switches fail, a datacenter link saturates, a routing change blackholes traffic. If your system runs on more than one machine, partitions will happen to you whether you planned for them or not.

So “CA” is not an option for a distributed system. The real statement is narrower:

When a partition happens, do you sacrifice consistency or availability?

flowchart TB
    P{Network partition<br/>happens} --> C["CP: refuse to serve<br/>on the minority side<br/><br/>Stay correct, become unavailable"]
    P --> A["AP: keep serving<br/>on both sides<br/><br/>Stay up, risk divergence"]

And note that the whole theorem only describes the partitioned state, which is a rare condition. It says nothing about how your system behaves the other 99.9% of the time — which is precisely why PACELC exists.


🧠 Mental model: the two shops

You run a shop with two branches sharing one inventory ledger. The phone line between them goes down.

CP — close the second branch. Only the branch that can reach head office keeps selling. You will never sell the same item twice, but half your customers are turned away.

AP — keep both open. Both branches keep selling from their last known inventory. Everyone gets served. But you might sell the same last item twice, and when the line comes back you must reconcile — refund someone, apologize, or backorder.

Neither is wrong. Which one is correct depends entirely on what the item is. Concert tickets: CP. Coffee: AP.


CP systems: consistency over availability

When partitioned, the minority side stops answering. Typically the side that can’t reach a quorum.

How they behave: writes require a majority acknowledgment. If you can’t reach a majority, you return an error rather than a possibly-stale or possibly-conflicting answer.

Systems: etcd, ZooKeeper, Consul, HBase, MongoDB (with majority write concern), Google Spanner, CockroachDB, most relational databases with synchronous replication.

Use when: correctness is non-negotiable — configuration and service discovery, distributed locks, leader election, financial ledgers, inventory in a limited sale, unique constraints.

🎙️ “I’d use a CP store for the coordination layer. If we can’t reach a quorum, I’d rather return an error than hand out two leases for the same lock.”


AP systems: availability over consistency

When partitioned, every node keeps accepting reads and writes. Divergence is expected and resolved afterwards.

How they behave: any replica accepts a write. Conflicts are reconciled later via last-write-wins, vector clocks, or CRDTs. → Conflict Resolution

Systems: Cassandra, DynamoDB (default), Riak, CouchDB, DNS, most CDNs.

Use when: being unavailable costs more than being temporarily wrong — shopping carts, social feeds, view counts, user sessions, product catalogues, analytics ingestion, IoT telemetry.

🎙️ “The cart is AP. Amazon’s own conclusion was that a cart item briefly reappearing is a far better customer experience than ‘add to cart’ failing during a network event.”


The tunable middle ground

Most modern databases don’t force a global choice — they let you tune it per query, which is how you should think about it too.

Cassandra / DynamoDB quorum tuning:

N = replicas,  W = replicas that must ack a write,  R = replicas that must respond to a read

W + R > N   →   strong consistency (the read set overlaps the write set)
W + R ≤ N   →   eventual consistency, lower latency, higher availability

N=3, W=1, R=1  →  fastest, most available, weakest guarantee
N=3, W=2, R=2  →  strong, still survives one node failing
N=3, W=3, R=1  →  fast reads, but any node failure blocks all writes

Quorums

MongoDB: writeConcern: majority + readConcern: majority gives you CP behaviour; writeConcern: 1 gives you faster, weaker writes.

DynamoDB: eventually consistent reads by default (cheap and fast); ConsistentRead=true costs 2× the read capacity and goes to the leader.

🎙️ The strong answer: “This isn’t one choice for the whole system. Payments use quorum reads and writes; the product catalogue uses eventual reads at a third of the cost and better latency.”


PACELC: the more useful framework

CAP’s weakness is that it only describes partitions, which are rare. PACELC (Daniel Abadi, 2010) extends it to the normal case:

If there is a Partition, choose between Availability and Consistency; Else (normally), choose between Latency and Consistency.

The “else” half is the part that matters day to day. Even with a perfectly healthy network, strong consistency costs latency, because agreement requires round trips — and if your replicas are in different regions, that’s 100–300 ms per write.

System PACELC Meaning
DynamoDB, Cassandra (default) PA/EL Available when partitioned; prioritizes latency normally
Cassandra (quorum) PA/EC Available when partitioned; pays latency for consistency normally
MongoDB (majority) PC/EC Consistent always, at the cost of latency
Google Spanner PC/EC Consistent always — and buys atomic clocks to keep the latency cost small
etcd, ZooKeeper PC/EC Consistency is the entire product
Most RDBMS with async replicas PC/EL Primary is consistent; replicas are fast and stale

🎙️ Using PACELC in an interview is a genuine differentiator: “CAP only tells us what happens during a partition. The more relevant question here is the ‘else’ case — do we pay cross-region latency on every write for strong consistency, or accept eventual consistency and keep writes local? Given a write path that’s user-facing, I’d keep writes local and reconcile.”


What CAP doesn’t tell you

Worth knowing, because these are the follow-up questions:

1. It’s not binary in practice. Real systems degrade gradually. A “CP” system might serve stale reads while refusing writes. An “AP” system might restrict which operations it accepts during a partition. Real behaviour is a spectrum.

2. “Available” in CAP is a very strong definition. It means every non-failed node answers every request. A system that answers 99.9% of requests is not “available” in CAP’s formal sense, but is obviously available in the sense you care about.

3. It ignores latency entirely. A system that responds in 30 seconds is “available” by CAP and useless in practice. This is PACELC’s whole point.

4. It says nothing about partial failures, degraded modes, or what happens under load — which is where most real outages live.

5. Partitions are rarer than people imply, and also more common than people think. Within a single datacenter, true partitions are uncommon. Across regions they’re routine. And a GC pause or an overloaded node is indistinguishable from a partition to everyone else — which is a much more frequent event than a cut cable.


Applying it: a worked decision

You’re designing a hotel booking system. Two data paths, two answers.

Room availability and booking: if two users book the last room during a partition, you have double-booked a real, physical room. Someone arrives at midnight to no bed. → CP. Use a linearizable store for the booking transaction. If we can’t reach quorum, show “we can’t complete your booking right now” — annoying, but recoverable. Overselling isn’t.

Hotel descriptions, photos, reviews, ratings: a stale description harms nobody. → AP. Serve from replicas and a CDN, eventually consistent, always available.

Search results: slightly stale availability in search is fine, as long as the booking step re-checks against the CP store. → AP for search, CP at commit. This pattern — optimistic display, authoritative check at the moment of truth — is extremely common and worth naming explicitly.

🎙️ “I’d make search eventually consistent for speed and availability, then do a strongly consistent check at the point of booking. That gives us fast browsing and correct commits, and the only cost is the occasional ‘sorry, just taken’ at checkout — which users already understand.”


⚖️ Trade-offs

Choice Gain Cost
CP No incorrect data, ever; simple reasoning Downtime during partitions; higher write latency; harder multi-region
AP Always writable; low latency; easy geo-distribution Conflicts you must resolve; application handles staleness
Tunable per query Pay only where needed Two mental models in one codebase; easy to get wrong
PC/EC with atomic clocks (Spanner) Global strong consistency Cost, vendor lock-in, still pays a few ms of commit wait

In the real world


🚨 Interview traps


🎙️ Soundbites


🛠️ Try it

1. Partition something. Run a 3-node etcd or Cassandra cluster in Docker. Use iptables (or just pause a container) to isolate one node. Then:

This 30-minute exercise makes CAP permanent in a way no diagram does.

2. Classify five systems you use. For each, write down: what happens during a partition, and what the “else” (latency vs consistency) choice is. If you can’t answer, read that system’s docs on consistency — it’s always documented and rarely read.


Check yourself

1. Why is "CA" not a real option for a distributed system? Because partition tolerance isn't a property you choose — it's a property of the physical world. Networks drop packets, links fail, and a GC pause or overloaded node is indistinguishable from a partition. Any system spanning more than one machine will experience partitions, so it must have *some* behaviour when they occur. Claiming "CA" just means you haven't decided what that behaviour is — which means it'll be whatever your code accidentally does, usually silent divergence.
2. What does PACELC add to CAP, and why does it matter more day to day? It adds the "else" branch: when there is *no* partition, you still trade latency against consistency. That matters because partitions are rare and the normal case is constant. Strong consistency needs coordination round trips, so a globally strongly-consistent write costs 100–300 ms regardless of network health. PACELC forces you to be explicit about the cost you pay 99.9% of the time, not just the rare case.
3. Cassandra is usually called AP. When does it behave like a CP system? When you configure quorum on both sides: with N=3, W=QUORUM(2), R=QUORUM(2), the read set always overlaps the write set, so reads see the latest write. And during a partition, the minority side can't reach a quorum, so it refuses — CP behaviour. This is why "Cassandra is AP" is an incomplete statement: consistency is a per-query setting, and the CAP classification follows the setting.
4. Design a flash sale for 100 limited items. What's your CAP choice and why? CP for the inventory decrement. Overselling a limited item means cancelling real orders and angering customers who believed they'd bought something — much worse than a brief "try again." Use a linearizable counter or a conditional write with a quorum. *But* keep the surrounding experience AP: the product page, images, and the "items remaining" display can all be eventually consistent and cached, since only the final decrement must be correct. Bonus: put a queue in front to smooth the spike so the CP store isn't hammered.
5. Why is a long GC pause equivalent to a network partition, and why does that matter? Because from every other node's perspective, they're identical: the node stops responding for several seconds. Failure detectors can't distinguish "the network dropped our messages" from "that process is frozen." It matters because it means partition-like events are far more *frequent* than cable cuts suggest — any stop-the-world pause, a saturated NIC, a CPU-starved container, or a slow disk triggers the same code paths. So your partition-handling behaviour isn't a rare-disaster path; it runs regularly, and you should test it.

Further reading