System Design Bits

Support the project

System Design Bits is free and always will be. If it helped, a coffee helps back.

Buy Me a Coffee QR codeBuy me a coffee

Concept 3 · Scaling and Trade-offs·23 min read

CAP Theorem & Trade-offs

When the network fails, you must choose — and there is no middle ground.

Big Idea

CAP isn’t a menu you pick freely from. Network partitions happen whether you plan for them or not, so the real choice every distributed system makes is what happens during one: keep answering and risk being wrong, or refuse to answer until you can be sure.

The CAP theorem states that in the presence of a network partition, a distributed system can only provide either Consistency or Availability — but not both. It defines the constraints every distributed data store must accept by forcing a choice about how the system behaves when its components cannot communicate.

This is the first theorem in distributed systems that architects internalize as a hard constraint, not a preference. Understanding it doesn’t give you a recipe — it gives you a lens for evaluating trade-offs every time you choose a data store or design a failure mode.

Why This Exists

The Decision You Cannot Opt Out Of

▸

The instant a system spans more than one machine connected by a network, that network can fail: a link drops, a data center loses connectivity, a router misbehaves. The system has to decide what to do in that moment, and no distributed system avoids that decision entirely.

▸

Consistency sounds like a single obvious good thing to want, but it comes in graded levels, and the strongest form is directly in tension with staying available and staying fast. Treating it as free or all-or-nothing produces designs that quietly promise more than they can deliver.

▸

CAP is one of the most frequently misquoted ideas in system design. Misreading what partition tolerance means leads to conclusions like “we chose CA”, which is not a real option once a network partition is possible at all.

Think Of It Like

Two Stores, One Inventory Count, One Phone Line

Two people run the same small store in two different towns, sharing one inventory count and staying in sync over a phone line. While the line works this is easy — they call before every sale to confirm current stock.

Now the line goes dead. Each of them has a choice: stop selling that item until the line is back (consistent, but that item is now unavailable in both towns), or keep selling from the last number they had (available, but both towns might sell the last unit and one customer gets disappointed later).

The dead phone line is the partition. What each store does during the outage is the whole CAP theorem in one story.

The Framework

Defining the Three Pillars

C
Consistency
Every read receives the most recent write or an error. The system acts as if there is only one copy of the data — even if replicated across nodes. C here means linearizability, not the C in ACID.
A
Availability
Every request receives a non-error response, without the guarantee it contains the most recent write. The system stays operational even if nodes fail.
P
Partition Tolerance
The system continues to operate despite an arbitrary number of messages being dropped or delayed by the network between nodes.
Partition Tolerance (P) is non-negotiable. Networks are unreliable — packets drop, cables get cut, hardware fails. Because you cannot prevent partitions, the real choice is between C and A when a partition occurs.

CAP as a triangle of trade-offs

CAP as a triangle of trade-offsConsistencyAvailabilityPartition toleranceCAassumes a network thatnever partitionsCPrefuse rather thanbe wrongAPanswer now, reconcile laterReal distributed systems keep P,so they live on the CP or AP edge

The Trade-off

CP vs. AP — Choosing Your Failure Mode

When a network partition splits your nodes into isolated groups, they can no longer coordinate. At that point, you must decide: does the system stop serving requests to avoid returning incorrect data, or does it continue serving requests with whatever data it currently holds?

This is not an engineering oversight — it is a fundamental constraint of distributed computing. The choice you make defines your system’s behavior under failure.

CP
Prioritize data integrity over uptime

If a node cannot verify its data is current with the other side of the partition, it returns an error or times out rather than risk serving stale information.

Use for: financial systems, inventory management, distributed locks
AP
Prioritize uptime over data integrity

If a node is partitioned, it continues accepting writes and serving reads using whatever data it has. Nodes may diverge and must reconcile once the partition heals.

Use for: social feeds, recommendations, shopping carts, DNS

Decision tree during a network partition

Network Partition Occurs
↓
Choose Your Strategy
↓
Prioritize Integrity
↓
CP SystemReject read/write requests if sync with other nodes cannot be confirmed
Prioritize Uptime
↓
AP SystemAccept read/write requests using local, potentially stale data

Refining the C

The Spectrum of Consistency Models

CP and AP describe a decision made at the moment a partition hits. But consistency itself is not a single property. It is the contract between a data store and its clients about the ordering and visibility of writes, and that contract comes in degrees.

The C in CAP names the strictest point on that gradient. Real systems rarely apply one point across an entire application — they run different guarantees for different workflows, paying for strictness only where the business actually needs it.

The strict end

Linearizability

The strongest single-object model. Every operation appears to take effect atomically at one instant between its invocation and its completion. Once a write completes, any later read — no matter which node serves it — returns that value or a newer one.

Sequential Consistency

Relaxes real time. Operations do not have to line up with a global clock, but every client observes all operations in the same relative order. Cheaper to maintain, and enough for many coordination problems.

Both models force the CP choice. To hold either one during a network failure, a node cut off from the coordinator has to reject reads and writes rather than answer from state it cannot verify. That refusal is exactly what prevents split-brain.

The loose end

Eventual consistency guarantees only that if writes stop, all replicas converge on the same value. Until then, concurrent reads sent to different nodes can return stale data, mutations out of order, or values that flatly contradict each other.

Between full linearizability and raw eventual convergence sit the client-centric guarantees. Each is far cheaper than linearizability, and each buys one specific property that users actually notice when it is missing.

Read-Your-Own-Writes

A client always sees its own updates. Change your profile photo and a reload shows the new one, even while other users still see the old one.

Monotonic Reads

A client never moves backwards in time. Once it has observed a value, later queries never hand back an older one.

Consistent Prefix Reads

Nobody sees a write without the writes it depends on. A reply never appears before the question it answers.

These three are independent, not synonyms. A store can give you read-your-own-writes and still walk time backwards on the next query. Bundling all three and scoping them to a single client is what most databases label session consistency.
ModelUnder partitionNormal latencyReplication requirementCommon use case
LinearizableRejects reads and writesHighest — multi-node round tripsSynchronous consensus (Raft / Paxos)Financial ledgers, seat booking, inventory holds
CausalStays availableLow — asynchronous propagationVector clocks, dependency trackingComment threads, collaborative editing
EventualStays availableLowest — local read and writeAsynchronous background repairSocial feeds, analytics counters, DNS

Replication behavior under network partition

Client Write Request
set balance = 150
↓
Network Partition
↓
Node A (Isolated)
current state: balance = 100
CP mode
↓
Write Rejected (503)
cannot reach consensus quorum
↓
Node B (Isolated)
current state: balance = 100
AP mode
↓
Write Accepted Locally
divergence risks split-brain

Beyond The Partition

PACELC: The Trade-off That Never Fully Goes Away

CAP only describes behavior during a partition, which is why it is so easy to under-design for: the system looks fine right up until the day the network doesn’t. PACELC adds the other half of the picture.

Keeping replicas strongly consistent means coordinating across them, and coordination costs time. So even with a perfectly healthy network, you are still paying for consistency — just in latency instead of availability.

if Partition     → trade Availability against Consistency
else            → trade Latency      against Consistency
PC/EC — consistent either way

Refuses during a partition, and pays coordination latency the rest of the time. Spanner, etcd and Zookeeper sit here: every write waits on a quorum even when nothing is broken.

PA/EL — responsive either way

Answers during a partition, and answers from the nearest replica the rest of the time. Cassandra and DynamoDB default here, which is why a read can be stale even on a perfectly healthy day.

Most databases expose this as a per-query knob rather than a global setting — DynamoDB’s strongly-consistent read and Cassandra’s tunable consistency levels are both PACELC choices made one call at a time.

Key Terms

The Vocabulary Of The Trade-off

CAP theorem

A distributed system experiencing a network partition can guarantee at most two of consistency, availability, and partition tolerance. Since partitions are unavoidable, the live choice is between consistency and availability.

Partition

A failure where network communication between nodes is lost or delayed, splitting the system into groups that cannot talk to each other.

Strong consistency

Every read reflects the most recent write regardless of which replica answers — the strictest and most expensive model to maintain across replicas.

Eventual consistency

Replicas converge to the same value over time if writes stop, but a read immediately after a write may temporarily see stale data.

PACELC

An extension of CAP: even without a Partition, systems trade Latency against Consistency during normal operation, not just Availability against Consistency during a partition.

Under the Hood

Quorum: How a Node Knows to Say No

A partitioned node cannot tell whether the other side is dead or merely unreachable, so it never tries to guess. Consensus protocols such as Raft and Paxos require a strict majority of the cluster, ⌊N/2⌋ + 1 out of N nodes, to agree before a write is committed. A node that cannot reach that many peers refuses to act.

This is what prevents split-brain, the failure mode where two disconnected halves of a cluster both believe they are in charge, both accept writes, and diverge into two irreconcilable versions of the truth. Since only one side of a partition can hold a majority, only one side is ever allowed to make progress.

A 5-node cluster split across two data centers

Quorum is ⌊5/2⌋ + 1 = 3 nodes. The link between the two sites goes down.

DC East
N1
N2
N3
3 of 5 nodes
Majority reached

Elects a leader and keeps serving reads and writes. This side holds the authoritative state.

DC West
N4
N5
2 of 5 nodes
No quorum

Every write and every linearizable read sent to N4 or N5 is rejected or blocked until the partition heals.

Pitfall: always deploy an odd number of voting nodes. With an even cluster, say 4 nodes partitioned 2 and 2, neither side holds a strict majority. Both halves stop accepting writes and the cluster goes fully unavailable, even though every single node is healthy.

Real-World Example

A Banking Application Under Partition

A user has $100 in their account. The system replicates this balance across two data centers. A network partition cuts off communication between them. The user attempts a withdrawal.

CP — Consistency chosen
The system stops accepting withdrawals. It cannot guarantee the user hasn’t already withdrawn $100 from the other data center, so it errors out to ensure the balance remains correct.

The user sees a “Service Unavailable” message — but the data remains accurate.
AP — Availability chosen
The system allows the withdrawal at both data centers simultaneously. It prioritizes the user’s ability to complete the task, even though internal state is now inconsistent — the user effectively withdrew $200 from a $100 balance.

A reconciliation process must resolve this once the partition heals.
CAP is a framework for failure, not normal operation. During normal operation — when there is no partition — systems can generally provide both high consistency and high availability. The trade-off only forces your hand when the network degrades.

The AP Side

Conflict Resolution When Replicas Diverge

Choosing AP is not the end of a decision — it is the start of a second one. Node B accepted the write while it was isolated, so once the link comes back there are two versions of the same record and no node that witnessed both.

Every highly available store therefore ships a reconciliation strategy. Which one it picks decides whether divergence costs you data or merely costs you code.

Approach 1 — Last-Write-Wins

The database stamps every write with a physical wall-clock timestamp. When two conflicting versions meet during reconciliation, the higher timestamp overwrites the lower one. It is simple, deterministic, and adds almost no storage overhead — one timestamp per record.
The cost is silent data loss. LWW trusts physical clocks, and physical clocks drift even under NTP. A node running a few milliseconds fast stamps its write with a higher timestamp and quietly discards a genuinely newer write made elsewhere. Nothing errors and nothing logs — the write is simply gone.

Approach 2 — Vector Clocks and Version Vectors

Rather than trusting wall clocks, vector clocks record causal history. A vector clock is a set of logical counters, one per node, where each entry counts the operations that node has applied. Comparing two vectors answers a question a timestamp cannot: did one version actually descend from the other, or did they happen independently?
// One version descends from the other
V1 = [ a:1, b:1 ]   V2 = [ a:2, b:1 ]
  every entry in V1 is ≤ V2, and one is strictly less
  → V1 happened before V2, so V2 wins and V1 is safe to drop

// Neither descends from the other
V1 = [ a:2, b:1 ]   V2 = [ a:1, b:2 ]
  neither vector dominates the other
  → concurrent write, unresolvable without domain knowledge
  → both versions surface to the application as siblings
Vector clocks never lose a write, but they never decide for you either. They convert silent data loss into an explicit conflict that the application layer has to resolve — strictly more work, and strictly safer.

Approach 3 — Conflict-Free Replicated Data Types

CRDTs sidestep reconciliation entirely by choosing data structures whose merge operation cannot conflict. Every replica merges whatever it receives, in whatever order it arrives, and they all land on identical state — no coordinator, no arbitration, no application-level decision.
Commutative
A ∪ B = B ∪ A

Arrival order does not change the result.

Associative
(A ∪ B) ∪ C = A ∪ (B ∪ C)

Grouping of merges does not change the result.

Idempotent
A ∪ A = A

Applying the same update twice changes nothing.

Those three properties are precisely what make reordering, regrouping, and duplicate delivery harmless — which is exactly the list of things an unreliable network does to your messages.
PN-Counter

A counter supporting increments and decrements, by keeping one grow-only tally per node for each direction and summing them.

LWW-Element-Set

A set supporting adds and removes, where each element carries a timestamp that settles membership conflicts.

OR-Set

An observed-removed set, where each add is tagged with a unique id so a concurrent add always beats a concurrent delete.

Choosing between them is a question about the field, not the database. Use LWW where the last edit genuinely should win and losing one is survivable, such as a display name. Use vector clocks where no write may be lost and something can arbitrate, such as a shopping cart. Use a CRDT where convergence must happen with no arbitration at all, such as a like counter or a collaborative document.

Seen In The Wild

Which Side Real Systems Picked

▪

DynamoDB and Cassandra are classic AP-leaning systems. They default to eventual consistency and stay available during network trouble, which fits shopping carts and activity feeds.

▪

Relational databases configured with synchronous replication, and systems like Google Spanner (which uses tightly synchronized clocks to approximate global strong consistency), lean CP — correctness first, at some availability and latency cost.

▪

Zookeeper and etcd, used for distributed configuration and leader election in systems like Kubernetes, are explicitly CP: they would rather stop answering than risk two nodes both believing they are the leader.

▪

Amazon’s original Dynamo paper, the ancestor of DynamoDB and Cassandra, was built to keep the shopping cart available during partitions — preferring “the cart might briefly show slightly stale items” over “the customer cannot shop right now”.

Key Points

What To Carry Forward

✓

CAP says a distributed system can guarantee at most two of consistency, availability, and partition tolerance, but partition tolerance is not optional — so the live trade-off is CP versus AP during an actual partition.

✓

CP systems refuse or delay answers during a partition to avoid returning wrong data; AP systems keep answering with possibly stale data and reconcile afterwards.

✓

Strong consistency is the easiest model to reason about and the most expensive to provide across distant replicas; eventual consistency is weaker, far cheaper, and often sufficient.

✓

The trade-off only bites during an actual partition. Under a healthy network most systems offer both consistency and availability, which is exactly why under-designing for it stays invisible until the one day it does not.

✓

Different data in the same system deserves different points on the spectrum — a balance and a like count should not share a consistency model.

✓

PACELC extends the idea: even with no partition, there is a latency-versus-consistency trade-off during ordinary operation.

Common Mistakes

Where This Usually Goes Wrong

✕

Claiming a system “chooses CA”, treating partition tolerance as optional, when any real distributed system eventually faces a partition and needs defined behavior for it.

✕

Assuming eventual consistency is “basically strong consistency, just a little slower”, when the actual guarantee — convergence only if writes stop — allows meaningfully stale or conflicting reads under continuous load.

✕

Applying one consistency model to an entire system, when a financial balance and a like count clearly belong at different points on the spectrum.

✕

Using CAP to argue about normal-operation latency trade-offs. CAP is about partition-time behavior; the latency argument is PACELC.

“Both of my humans feed me dinner. The weekend they stopped comparing notes, I got fed twice a night. Each of them stayed available, neither of them was wrong, and the divergence was only detected at the vet.”

Try It Yourself

Two Features, Two Failure Modes

Think of two features in the same app: one where wrong data would genuinely upset you (a bank balance) and one where slightly stale data wouldn’t bother you (a follower count). For each, write down whether you’d rather it go briefly unavailable or briefly show an outdated number during a network hiccup — you just picked CP for one and AP for the other, inside a single product.

Summary

Choose The Failure You Can Live With

Partitions are not a scenario you opt into, so every distributed system already has an answer for them — the only question is whether that answer was chosen deliberately. Pick CP where being wrong is worse than being down, AP where being down is worse than being briefly wrong, and pick it per workflow rather than once for the whole system. These constraints form the basis for database replication strategies and distributed transaction patterns — topics covered in the concepts ahead.

Quiz Review

Check your understanding

Question 1 of 16

Why is it inaccurate to say a system 'chooses CA' in the CAP theorem?

  • ✓Because partition tolerance isn't a feature you can opt out of — network partitions are a real, unavoidable failure mode in any system with more than one node communicating over a network. 'Choosing CA' would mean assuming partitions never happen. The actual choice a system makes is what to do when one does: stay consistent by refusing some requests (CP), or stay available by answering with possibly stale data (AP).