AlgoInsight

System Design

The CAP Theorem Trade-off I Made in Production

Most candidates can recite 'consistency, availability, partition tolerance - pick two.' Then I ask one follow-up and watch the answer fall apart.

By Admin2026-06-1810 min readcap-theoremdistributed-systemsconsistencyavailabilitysystem-designpacelc

I've run something like three hundred interview loops. There's a moment in the system design round I can almost set my watch by: I ask about a system that spans regions, the candidate reaches for the CAP theorem, and they say the line. "Consistency, availability, partition tolerance - you can only pick two." Confident. Rehearsed.

Then I ask: "Okay. Which two are you giving up, and when?"

And most of the time, that's where it stops. Because "pick two" is what you memorize when you've read about CAP but never had to live inside the decision. I've had to live inside it - my day job is keeping a storage system consistent across regions when the network between those regions is, frankly, not always cooperative. So let me give you the version of CAP that survives the follow-up question, built from the ground up.


What CAP actually says

Three properties. A distributed system would love to have all three:

The CAP theorem, proven formally by Gilbert and Lynch, says you can't have all three at once. The popular framing is "pick two."

Here's why that framing is a trap.


The trap: "pick two" is the wrong mental model

Partitions are not something you choose. A partition is what happens when a network cable gets cut, a switch fails, or a region briefly can't talk to another region. In any system that runs across more than one machine - which is every system worth designing for an interview - partitions are a fact of nature, not a menu option.

So you don't get to "give up P." If you give up partition tolerance, what you actually have is a single-node system that falls over the moment the network hiccups. That's not a design; that's a liability.

Important: In a real distributed system, P is mandatory. The CAP theorem isn't "pick two of three." It's: when a partition happens, you must choose between C and A. That's it. That's the whole theorem, stated honestly.

This reframing is the entire difference between a candidate who memorized CAP and one who understands it. When the network splits and two halves of your system can't talk:

That's the trade I make in production. When two regions can't reach each other, do I reject the write and tell the user "try again," or do I accept it locally and merge later? There is no free answer. There's only the answer that's right for this data.

graph TD
    P{Network partition<br/>happens between nodes} --> Q{Can I still guarantee<br/>every read is current?}
    Q -->|Refuse stale answers| C[CP: stay Consistent<br/>sacrifice Availability]
    Q -->|Keep answering anyway| A[AP: stay Available<br/>sacrifice Consistency]
    C --> C2[e.g. payments, inventory,<br/>anything where wrong = costly]
    A --> A2[e.g. likes, feeds, presence,<br/>anything where stale = fine]
This flowchart shows the real CAP decision. It starts when a network partition happens between nodes. The next question asks whether the system can still guarantee every read is current. If it refuses to give stale answers, it becomes a CP system: consistent but not always available - used for payments and inventory. If it keeps answering anyway, it becomes an AP system: available but possibly inconsistent - used for likes, feeds, and presence.

It's not one global choice - it's per-operation

Here's the second thing that separates levels. CAP is not a single switch you flip for your whole system. A serious architecture makes the C-vs-A call per type of data.

Take a shopping platform during a network partition:

Same system. Same partition. Two different CAP choices, because the cost of being wrong is different for each. When a candidate tells me "this whole system is AP," I know they're reciting. When they say "the cart is AP, the payment path is CP, and here's why," I know they've actually thought about it.


The mechanism: quorums

How do you actually tune where a system sits on the C-vs-A line? The most common lever is the quorum.

Say your data is replicated across N nodes. You decide:

The magic condition is W + R > N. When that holds, the set of nodes you wrote to and the set you read from are guaranteed to overlap by at least one node - so every read is guaranteed to see the latest write. Prove it to yourself:

def strong_reads(N, W, R):
    # W + R > N guarantees the write-set and read-set overlap
    return W + R > N

configs = [(3, 1, 1), (3, 2, 2), (3, 3, 1), (5, 3, 3), (3, 1, 3)]
print(f"{'N':>2} {'W':>2} {'R':>2}  guarantee")
print("-" * 40)
for N, W, R in configs:
    g = "strong (reads always current)" if strong_reads(N, W, R) else "eventual (reads may be stale)"
    print(f"{N:>2} {W:>2} {R:>2}  W+R={W+R:<2} -> {g}")

Run it. N=3, W=1, R=1 gives you fast, highly-available reads and writes - but W+R=2, which is not greater than 3, so you can read stale data. N=3, W=2, R=2 gives W+R=4 > 3 - now reads are always current, but you need two nodes reachable for every operation, so a partition is more likely to make you unavailable. The quorum numbers are your CAP dial. Turn them toward consistency and you sacrifice availability under partition, and vice versa.

Interview: When I'm running the loop, this is the depth that earns a "strong hire." Don't just name CAP - reach for quorums and say something like: "I'll replicate across 3 nodes. For the payment path I'll use W=2, R=2 so reads are always consistent. For the activity feed I'll use W=1, R=1 so it stays fast and available, and accept eventual consistency." That sentence does more for you than ten minutes of definitions.


PACELC: the part nobody mentions

CAP only talks about what happens during a partition. But partitions are rare. What about the 99.9% of the time the network is fine?

That's what the PACELC extension adds: if Partition, then Availability-or-Consistency (PAC); Else, Latency-or-Consistency (ELC). Translation: even when nothing is broken, you still trade consistency against latency. Wanting every read to be perfectly current means coordinating across nodes, and coordination costs milliseconds. Sometimes a lot of them.

This is the honest, complete picture. CAP is the dramatic version that only shows up during failures. PACELC is the everyday version you're actually paying for on every single request.


Where real systems land

System Partition behavior Why
Google Spanner CP (leans consistent) Built for strong global consistency; will sacrifice availability to never serve stale data
Cassandra / DynamoDB AP (tunable) Built to always answer; consistency is tunable per query via quorum settings
A single SQL primary CP Strong consistency; if the primary is partitioned away, that data is unavailable
DNS AP Always answers, propagates updates eventually - staleness is acceptable

Notice there's no "best" row. Spanner and Cassandra were built by brilliant teams who made opposite CAP choices, because they were solving different problems. That's the real lesson: CAP isn't a problem you solve. It's a trade-off you make on purpose, with your eyes open, per piece of data.


The candidates who pass my loop aren't the ones who recite the theorem fastest. They're the ones who, when I cut the network in the hypothetical, calmly tell me which requests they'll fail and which they'll keep serving stale - and why that's the right call for this data. That judgment is learnable. It's most of what System Design Fundamentals is about, and my co-founder Nikhil takes the consistency side of this even deeper in his piece on strong vs eventual consistency. Start there if this clicked.


Go deeper

More articles