CRDTs (Conflict-Free Replicated Data Types)

Problem

Eventual consistency converges replicas only if you hand it a merge rule, and the available rules are poor. Last-writer-wins silently discards a concurrent write, and version vectors dump a pile of siblings on the application to reconcile by hand on every read. For anything richer than a single opaque value, a set, a counter, a shared document, you want a merge that loses no update, needs no human intervention, and produces the same result at every replica no matter what order the updates arrive in.

Hand-written merge code gets this wrong because an asynchronous network reorders messages, delivers them twice, and delays some indefinitely. A merge that depends on order or fires differently on a duplicate will leave two replicas holding different values forever, which defeats the point of converging at all.

Solution

Constrain the data type so its merge is mathematically forced to converge regardless of order, duplication, or delay. Two constructions achieve this. A state-based CRDT keeps its value in a join-semilattice and merges by taking the least upper bound, an operation that is commutative, associative, and idempotent by the lattice's structure. An operation-based CRDT instead ships individual operations that are defined to commute, relying on a delivery layer that hands each operation to each replica exactly once (often in causal order).

Because the merge is commutative, associative, and idempotent, any two replicas that have observed the same set of updates compute the same value, and a replica that receives the same update twice is unaffected. That gives convergence with no coordination, no central authority, and no conflict ever surfaced to the application: concurrent edits resolve to one result the type defines in advance.

The convergence is structural, not semantic. The type guarantees every replica agrees on an answer; it does not guarantee the answer matches what a user intended. An observed-remove set, for example, defines that a concurrent add and remove resolve in the add's favor, which is a deliberate choice baked into the type rather than a judgment about the user's wish.

Tradeoffs

PropertyEffect
CoordinationNone required; replicas accept writes offline and converge once they exchange state, the reason to use a CRDT
MetadataTombstones, per-replica counters, and unique element identifiers accumulate and can dwarf the data, requiring garbage collection that itself needs some causal stability to be safe
SemanticsConvergence is guaranteed, but the converged value follows the type's built-in rule, which may not match user intent
ExpressivenessNot every operation commutes, and global invariants such as uniqueness or a non-negative balance can't be held across replicas without coordination
TransportState-based CRDTs ship large states unless you use delta variants; operation-based CRDTs need exactly-once causal delivery
SequencesText and list CRDTs carry an identifier per element and can interleave concurrent inserts in surprising ways

Implementations

Minimal pseudocode (state-based grow-only counter)

# each replica tallies its own increments; merge takes the per-replica max
def inc(state, replica):
state[replica] = state.get(replica, 0) + 1
def value(state):
return sum(state.values())
def merge(a, b): # join: per-key maximum
return {k: max(a.get(k, 0), b.get(k, 0)) for k in a.keys() | b.keys()}

The per-key max is commutative, associative, and idempotent, so replicas converge no matter how increments and merges interleave. A set or text type follows the same principle with richer state: an observed-remove set tags each add with a unique id and removes only the tags a replica has actually seen, so concurrent adds and removes still merge to one deterministic result.

Redis CRDTs

Redis Enterprise's Active-Active geo-distribution makes every region a full read-write replica, with conflicts between regions resolved by CRDT semantics rather than rejected. Standard Redis types are backed by conflict-free implementations: counters add up across regions, sets merge by an observed-remove discipline, and concurrent string writes settle by last-writer-wins. A write taken in one region during a link failure to another still merges cleanly once connectivity returns.

Automerge and Yjs

Two libraries for local-first and collaborative applications. Yjs is a high-performance sequence CRDT (the YATA algorithm) widely used to back collaborative text and rich-text editors through bindings for editors like ProseMirror, CodeMirror, and Monaco. Automerge is a JSON-document CRDT that lets each client edit an offline copy and syncs changes peer to peer, merging concurrent edits into a single document automatically. Both let multiple users work disconnected and converge on reconnection without a central coordinator.

Figma multiplayer

Figma's multiplayer is CRDT-influenced rather than a pure CRDT. A central server provides ordering and acts as the source of truth, and each object property resolves concurrent changes by last-writer-wins keyed through that server, which sidesteps the metadata growth and complexity of a fully decentralized design while keeping the convergent merge behavior for individual properties. It's a useful illustration that production systems often borrow CRDT ideas without adopting the decentralized model wholesale.

Riak

Riak ships server-side CRDTs as Riak Data Types: counters, sets, maps, registers, and flags. A client issues updates and the database merges concurrent versions using the type's convergence rules instead of returning siblings for the application to resolve, which moves the conflict-free guarantee from a library into the datastore itself.