Back to blog
Apr 2026·4 min readAI-generated

The CAP Theorem, Explained with Real Examples

#distributed-systems#databases#architecture

Every distributed database vendor claims to be fast, consistent, and always available. The CAP theorem is the reason you should be suspicious of that pitch: in the presence of a network partition, you can only keep two of Consistency, Availability, and Partition tolerance, and partition tolerance isn't actually optional — so the real choice is between consistency and availability.

What the three letters actually mean

  • Consistency (C): every read receives the most recent write, or an error. All nodes see the same data at the same time.
  • Availability (A): every request receives a (non-error) response, without guaranteeing it contains the most recent write.
  • Partition tolerance (P): the system keeps operating despite an arbitrary number of messages being dropped or delayed between nodes.

Network partitions happen — a switch fails, a data-center link saturates, a node's NIC flakes out. Since you can't opt out of the network being unreliable, P isn't a real design choice; it's a fact of distributed systems. That leaves you picking between C and A during a partition. Outside of a partition, a well-built system can give you both.

Why "pick two" is misleading

The classic "pick two of three" framing suggests CA is a valid option, as if partition tolerance were something you could turn off. It isn't, unless your system runs on a single machine with no network calls at all. So in practice the theorem reduces to one question: when a partition happens, does your system stay available at the cost of consistency, or stay consistent at the cost of availability?

CP in practice: a leader-based configuration store

Consider a configuration service backed by a Raft-based store (etcd, Consul, ZooKeeper). Writes go through a leader, which must replicate to a majority of nodes before acknowledging.

Client -> Leader -> [replicate to majority] -> ack -> Client

If a network partition splits the cluster into two groups, only the group containing a majority of nodes can elect a leader and accept writes. The minority side stops accepting writes entirely — it returns errors rather than risk serving stale or conflicting data.

# Simplified: a node in the minority partition rejects writes
def handle_write(request):
    if not self.has_quorum():
        raise Unavailable("no quorum: refusing to risk inconsistent write")
    return self.replicate_and_commit(request)

That's CP: consistency is preserved by sacrificing availability on the minority side. This is exactly what you want for configuration data, leader election, or distributed locks — serving a stale lock owner is worse than a temporary outage.

AP in practice: a shopping cart service

Now consider a shopping cart stored in a Dynamo-style database (Cassandra, DynamoDB) with multiple replicas across regions. During a partition, every replica keeps accepting writes independently.

# Simplified: every reachable replica accepts writes during a partition
def handle_write(request):
    self.local_store.put(request.key, request.value, timestamp=now())
    return Ack()  # never blocks on other replicas

Two regions might now have diverging versions of the same cart. When the partition heals, the system needs a reconciliation strategy — last-write-wins by timestamp, vector clocks, or an application-level merge (union the items instead of picking one side).

# A simple merge strategy: union cart items instead of picking a winner
def merge_carts(cart_a, cart_b):
    return {**cart_a, **cart_b}  # naive union; real systems need care around removals

That's AP: the cart service stays available on both sides of the partition, and correctness is repaired after the fact rather than enforced during the write. For a shopping cart, an available-but-eventually-consistent system beats a consistent-but-down one — nobody wants "service unavailable" between browsing and checkout.

It's not a single system-wide choice

The useful realization is that CAP applies per operation, not to a database product as a whole. Many real systems mix both: DynamoDB offers both eventually-consistent and strongly-consistent reads on the same table. PostgreSQL with synchronous replication is CP for the primary's writes but can be configured with asynchronous standbys that serve stale (AP-leaning) reads.

-- PostgreSQL: synchronous_commit controls where you sit between C and A
SET synchronous_commit = 'on';   -- wait for replica ack: favors consistency
SET synchronous_commit = 'off';  -- don't wait: favors availability/latency

Design at the level of individual operations: does this write need linearizable consistency, or can this read tolerate being a few seconds stale? Answering per-operation, instead of per-database, is where CAP actually earns its keep as a design tool.

PACELC: the theorem's underrated sequel

CAP only describes behavior during a partition. Daniel Abadi's PACELC extension adds the more common case: even without a partition, you trade latency for consistency. If Partitioned, choose Availability or Consistency; Else (normal operation), choose Latency or Consistency.

A system that replicates synchronously to a remote region gets strong consistency but pays a cross-region round-trip on every write. A system that replicates asynchronously gets low latency but readers might see stale data even when the network is perfectly healthy. This trade-off is present on every single request, not just during rare partition events — which is why it matters more for day-to-day system design than the partition case does.

Applying this when you actually design something

  • Identify which operations are locks/leader-election/inventory-decrement style (need CP) versus which are cart/profile/analytics style (can tolerate AP).
  • Don't pick a database because of a marketing claim of "always consistent and always available" — that combination doesn't survive a real partition. Ask specifically what it does when nodes can't reach each other.
  • Check whether the consistency knob is per-cluster or per-request. Systems like DynamoDB and Cassandra let you choose per read, which is usually the right level of granularity.
  • Remember PACELC: your consistency choice has a latency cost on the happy path, not just a risk cost during outages.

The CAP theorem isn't a reason to be anxious about distributed systems — it's a checklist question to ask of any database before you build on it: what exactly happens to my requests when a partition hits, and which side of that trade-off does my data actually need?

NewerBuilding a Simple Pub/Sub System in Node.jsOlderA Practical Guide to JWT Authentication