root.system / 0x11 / cap-theorem

You can only
guarantee two.

In 2000 a computer scientist proved that every distributed system makes a silent tradeoff. Consistency. Availability. Partition tolerance. Pick any two. This page is why that choice is unavoidable, and what every system you use quietly chose without telling you.

Beginner// level 01

The three promises

His name was Eric Brewer. In 2000 he stood up at a conference and told the entire tech industry something they did not want to hear: you cannot have all three. Almost nobody believed him. Two years later, Gilbert and Lynch proved it formally.

Every database, app, and blockchain built since then has been forced to live with the consequences. This is not an engineering limitation. This is not a hardware problem. This is mathematics. And once it clicks, you will never look at a loading spinner the same way again.

Every distributed system, every app, database, and blockchain that runs across multiple machines, makes three promises to its users. The CAP theorem says you can fully keep two of them. Never all three. Here is what they mean.

C · consistency
Every machine sees the same truth
Every read receives the most recent write. Every machine sees identical data at the same time. No stale reads, no contradictions, one reality, always. Example: your bank balance is $500 on every server simultaneously. No server can disagree.
A · availability
The system always responds
Every request receives a response. Not an error, not a timeout, not 'try again later'. An actual answer, always, even if that answer is slightly out of date. Example: the app loads instantly even if some servers are struggling.
P · partition tolerance
Survives when machines disconnect
The system keeps working even when the network between machines fails. Cables get cut, packets get dropped, data centres lose connectivity. The system continues anyway. Example: the app keeps working even when two data centres cannot talk to each other.
// the theorem
Brewer proved: during a network partition you can guarantee Consistency or you can guarantee Availability. Not both. Choose one. Partition tolerance is not optional in the real world, so the real choice is always C versus A.

The tradeoff, expressed as types

In a real system the choice is made in the architecture, before a single line of business logic. Here it is as a plain enum: a key-value read that either refuses (CP) or answers with possibly-stale data (AP).

Rust• • •
// The CAP tradeoff expressed as types.
// In a real system you choose your guarantees
// before you write a single line of logic.
enum CapChoice {
    // Strong consistency: may reject requests
    // during a partition to avoid stale data.
    ConsistencyOverAvailability,

    // Always available: may return stale data
    // during a partition to stay responsive.
    AvailabilityOverConsistency,
}

// A key-value read that respects the choice.
fn get_value(
    choice: &CapChoice,
    local_value: u64,
    can_reach_primary: bool,
    primary_value: u64,
) -> Option<u64> {
    match choice {
        // CP: refuse if we cannot verify the latest value.
        CapChoice::ConsistencyOverAvailability => {
            if can_reach_primary {
                Some(primary_value)
            } else {
                None // refuse rather than risk stale data
            }
        }
        // AP: return what we have, even if stale.
        CapChoice::AvailabilityOverConsistency => {
            Some(local_value) // always respond
        }
    }
}
C• • •
#include <stdint.h>

typedef enum {
    CAP_CONSISTENCY, // refuse requests if uncertain
    CAP_AVAILABILITY // always respond, may be stale
} CapChoice;

// Returns -1 if CP and a partition is detected.
int64_t get_value(
    CapChoice choice,
    int64_t local_value,
    int can_reach_primary,
    int64_t primary_value
) {
    if (choice == CAP_CONSISTENCY) {
        // CP: only answer if we can verify.
        return can_reach_primary ? primary_value : -1;
    } else {
        // AP: always answer, even if stale.
        return local_value;
    }
}

Try it: cut the network yourself

This is the whole theorem in one widget. Two data centres holding the same $500 balance. Cut the network between them, choose your guarantee, and try to withdraw money from both sides at once. Watch what each choice costs you.

// CAP visualiser — cut the network and choose a side
Data Centre A · LondonONLINE
$500
synced
Data Centre B · New YorkONLINE
$500
synced
when partitioned, choose:
event log
03:07:24both data centres online and synced at $500

cut the network, pick CP or AP, then withdraw from each city. CP refuses during a partition (consistency wins). AP accepts on both sides and diverges (availability wins), then reconciles, or detects a double-spend, on restore.

// what you just saw
Under CP, the partition makes both servers refuse: your money is safe but the system is down. Under AP, both servers keep taking withdrawals and silently diverge; if the two sides together hand out more than the balance, you have created money out of nothing. That second case is the double-spend problem, and it is exactly why financial systems pick CP.
Intermediate// level 02

What every system actually chose

Every system you use every day has already made this choice. Silently. In the architecture. Before you signed up. Here is what they chose and why.

CP · your bank
Money requires truth
Banks would rather go offline than let two servers disagree about your money. A partition triggers a freeze; transactions wait until consistency is restored. You have felt this: every time online banking just stops working at the worst possible moment. That outage screen is the bank protecting reality itself.
AP · Google Docs
Collaboration over sync
You and a colleague edit simultaneously. Google does not freeze the world; it allows temporary inconsistency, then reconciles the edits afterward. You have felt this: the moment two edits conflict and Google asks you which version to keep.
AP · WhatsApp
Messages over order
You land after a flight, turn aeroplane mode off, and fifty messages arrive at once, some out of order, some hours old. WhatsApp tolerated inconsistency until synchronisation became possible. You have felt this: messages arriving after the conversation they belonged to already ended.
AP · Netflix
Uptime over accuracy
A data centre fails and Netflix keeps streaming. Recommendations might be briefly wrong, watch history might update late. Slightly wrong data beats fifty million spinning wheels. You have felt this: a show you already watched showing as unwatched for a few minutes.
CP · Bitcoin
Trust requires proof
Every node must agree on the same ledger, always, no exceptions. If consensus is uncertain, parts of the network slow down rather than risk conflicting histories, because with money there is no reconciling later. You have felt this: waiting ten minutes for a confirmation. That wait is the price of mathematical truth in a trustless network.

The choices, side by side

systemchoicesacrificeswhy
Your BankCPAvailabilityMoney requires truth
Google DocsAPConsistencyCollaboration > sync
WhatsAppAPConsistencyMessages > order
NetflixAPConsistencyUptime > accuracy
BitcoinCPAvailabilityTrust requires proof
HBaseCPAvailabilityAnalytics > uptime
DynamoDBAPConsistencyScale > accuracy
CassandraAPConsistencyAlways available

CP and AP reads, in code

The difference between CP and AP is not a config flag; it is the shape of the read path. A CP read waits for a quorum and refuses if it cannot get one. An AP read answers immediately from local state and never blocks.

Rust• • •
use std::time::{Duration, Instant};

// A CP database read. Returns None if it cannot
// confirm the latest state across a quorum.
fn cp_read(
    node: &Node,
    key: &str,
    quorum_timeout: Duration,
) -> Option<String> {
    let responses = node.broadcast_read(key);
    let deadline = Instant::now() + quorum_timeout;

    let mut confirmations = 0;
    for response in responses {
        if Instant::now() > deadline {
            // Timeout: refuse rather than risk stale data.
            // This is the CP tradeoff in code.
            return None;
        }
        if response.is_ok() {
            confirmations += 1;
        }
    }

    if confirmations >= node.quorum_size() {
        Some(node.local_value(key))
    } else {
        None // CP: refuse if quorum not reached
    }
}

// An AP database read. Always returns something,
// even if it might be out of date.
fn ap_read(node: &Node, key: &str) -> String {
    node.local_value(key)
        .unwrap_or_else(|| "default".to_string())
}
C• • •
#include <stdbool.h>
#include <time.h>

// CP read: blocks until quorum or timeout.
// Returns NULL if it cannot confirm consistency.
char* cp_read(Node* node, const char* key, int timeout_ms) {
    int confirmations = 0;
    clock_t start = clock();

    while (confirmations < node->quorum_size) {
        int elapsed = (clock() - start) * 1000 / CLOCKS_PER_SEC;
        if (elapsed > timeout_ms) {
            return NULL; // CP: refuse on timeout
        }
        if (poll_node(node, key)) {
            confirmations++;
        }
    }
    return node->local_value; // confirmed consistent
}

// AP read: always returns immediately.
// May be stale, will always respond.
char* ap_read(Node* node, const char* key) {
    return node->local_value ? node->local_value : "default";
}
// the tell
Look for the early return None / return NULL on timeout. That single line is a system declaring itself CP: it would rather give you nothing than give you something wrong. An AP system has no such line; it always has an answer ready.
Advanced// level 03

Beyond CAP: PACELC and real-world nuance

CAP tells you what happens when the network breaks. But networks break rarely. What about the other 99.9% of the time? PACELC answers that.

PACELC extends CAP with one insight: even when there is no partition, you still face a tradeoff.

  • If Partition (P): choose Availability (A) or Consistency (C). This is plain CAP.
  • Else (E), in normal operation: choose Latency (L) or Consistency (C).

network partition?
├── YES → Availability vs Consistency  (the CAP tradeoff)
└── NO  → Latency vs Consistency     (the PACELC tradeoff)

The second tradeoff is the one you live with most of the time. Fast response or guaranteed correctness, on every single request, every single second. A system that double-checks every read with a quorum is correct but slow; a system that answers from the nearest replica is fast but can be briefly wrong.

systempartitionnormalmeaning
DynamoDBPAELFast always, eventually consistent
CassandraPAELAvailable, tunable consistency
HBasePCECConsistent, pay the latency cost
MySQLPCECACID, slower writes
BitcoinPCECTruth costs time

Consensus in code: the heart of every CP system

How does a CP system actually decide a value is safe to commit? It collects votes from its nodes and commits only when a majority (a quorum) agrees. Below is a simplified version of the check at the core of Raft, the algorithm behind etcd and CockroachDB.

Rust• • •
// Simplified Raft-style consensus check.
// Used by etcd, CockroachDB, and similar CP systems.
#[derive(Debug, PartialEq)]
enum ConsensusResult {
    Committed(String),    // majority agreed
    Pending,              // still gathering votes
    Rejected,             // could not reach quorum
}

fn check_consensus(
    votes: &[Option<String>],
    quorum: usize,
) -> ConsensusResult {
    let total = votes.len();
    let agreements: Vec<&String> = votes.iter().flatten().collect();

    if agreements.len() >= quorum {
        // Majority agreed on a value: this is how
        // CP systems commit a write.
        ConsensusResult::Committed(agreements[0].clone())
    } else if total - agreements.len() > total - quorum {
        // Too many nodes unreachable to ever reach quorum.
        // CP: reject rather than risk inconsistency.
        ConsensusResult::Rejected
    } else {
        ConsensusResult::Pending
    }
}

fn main() {
    // 5 nodes, need 3 for quorum.
    let votes = vec![
        Some("value_42".to_string()),
        Some("value_42".to_string()),
        Some("value_42".to_string()),
        None, // node unreachable
        None, // node unreachable
    ];

    match check_consensus(&votes, 3) {
        ConsensusResult::Committed(v) => println!("Committed: {}", v),
        ConsensusResult::Rejected => println!("Rejected: partition detected"),
        ConsensusResult::Pending => println!("Waiting for quorum..."),
    }
    // Output: Committed: value_42
}
C• • •
#include <stdio.h>
#include <string.h>
#include <stddef.h>

typedef enum { COMMITTED, PENDING, REJECTED } ConsensusResult;

ConsensusResult check_consensus(
    const char** votes, // NULL = unreachable node
    size_t total,
    size_t quorum,
    char* out_value,
    size_t out_size
) {
    size_t agreements = 0;
    const char* agreed_value = NULL;

    for (size_t i = 0; i < total; i++) {
        if (votes[i] != NULL) {
            if (!agreed_value) agreed_value = votes[i];
            if (strcmp(votes[i], agreed_value) == 0) agreements++;
        }
    }

    if (agreements >= quorum) {
        if (out_value && agreed_value)
            strncpy(out_value, agreed_value, out_size);
        return COMMITTED;
    }

    size_t unreachable = total - agreements;
    if (unreachable > total - quorum) return REJECTED;
    return PENDING;
}

int main(void) {
    const char* votes[] = { "value_42", "value_42", "value_42", NULL, NULL };
    char result[64];
    ConsensusResult r = check_consensus(votes, 5, 3, result, sizeof(result));
    if (r == COMMITTED)      printf("Committed: %s\n", result);
    else if (r == REJECTED)  printf("Rejected: partition detected\n");
    else                     printf("Pending: waiting for quorum\n");
    return 0;
}

Bitcoin and the CAP theorem

Bitcoin is the most famous CP system ever built. Consistency above everything. Every node must agree on the same ledger. No exceptions, no stale reads, no temporary inconsistency that reconciles later.

The double-spend problem is just the CAP theorem applied to a financial ledger. If two nodes accept the same Bitcoin in two different transactions simultaneously, the ledger is inconsistent and someone gets money they should not have. That is the exact failure the AP path in the widget above produced.

Satoshi solved it with Proof of Work. Instead of a central authority deciding which transaction is valid, every node independently runs the same algorithm and the longest valid chain wins. Not because someone decided, but because every node applies the same rule. This is consensus without coordination: consistency without a coordinator, the CAP theorem solved using computation as the arbiter.

// Bitcoin's CAP choice, in pseudocode
if cannot_reach_consensus() {
    halt_new_blocks();    // go offline
    // never serve stale state
    // consistency is non-negotiable
    // availability is the sacrifice
}

This is why you wait for confirmations. This is why Bitcoin is slow. This is why it has never been successfully double-spent in over fifteen years. Every second of waiting is the price of consistency in a trustless world.

Where the CAP theorem appears in BitRoot

CAP is not an isolated result; it touches almost every layer of the stack below it. The shortest path from each:

0x02 / binary
Disagreement is binary
Every value a distributed system stores is ultimately binary. When two servers disagree they hold different binary states at the same logical address.
0x06 / memory
Two copies, one truth
The CAP tradeoff is a memory problem: two machines, two copies of data, one truth, impossible to keep perfectly aligned when the network between them fails.
0x0F / networking
The P in CAP
A network partition is the P in CAP. TCP/IP carries the consistency messages between nodes; when packets drop, the theorem activates.
0x0D / hashing
Consistency by math
Bitcoin uses SHA-256 to enforce consistency. Each block hash is an immutable link; changing history means recomputing every hash that follows, which is computationally impossible.
0x0C / linked lists
Tamper-evident by structure
A blockchain is a linked list where the pointer between nodes is a hash. CAP explains why changing any node is detectable by every other node; the consistency guarantee is structural.
0x0B / arrays
Shards are arrays
Distributed arrays are called shards. CAP decides whether all shards must agree before responding, or whether each can answer independently.
0x10 / distributed systems
The fundamental constraint
CAP is the fundamental constraint of distributed systems. Every architecture decision (replication, consensus, sharding) is an answer to the same CAP question.
0x12 / blockchain
Crypto chose CP
Bitcoin chose CP. Ethereum chose CP. Both sacrifice availability to guarantee consistency, because financial ledgers have no room for reconciling later. CAP is why crypto takes time to confirm.
next up / 0x12
CAP told you what breaks. PACELC tells you what you choose every second.
pacelc