Why CRDTs exist
Two people typing into the same document at the same time, possibly offline, possibly across the world. The merge has to come out the same on both screens with no central referee. CRDTs are the data structures that make that arithmetic instead of a fight.
Why it exists
Open a Google Doc with a colleague. You’re both typing. Sometimes one of you is on a flaky train. The cursors move, the text rearranges, and the final document looks the same on both laptops. Nobody fights, nobody loses a sentence, nobody has to click “resolve conflict.”
Now try to write the code that does this.
The naive plan — “send each keystroke to a server, the server picks an order, everyone replays” — falls over the moment one of you is offline, or the server is far away, or two edits land in the same millisecond. The slightly-less-naive plan — “diff the two documents, three-way merge them like Git does” — works fine for source control where humans review the result, and works terribly for a live cursor where the merge has to be instant, automatic, and silent.
What you actually want is something stronger: a way to represent the document such that any two replicas that have seen the same set of edits end up identical, no matter what order the edits arrived in. No central authority, no locking, no asking permission before typing. Edits commute.
That property — concurrent edits always merge to the same answer — is the whole reason CRDTs were invented. They are the data structures that make collaborative editing arithmetic rather than negotiation.
Why it matters now
Real-time multiplayer is no longer a Google-only superpower. The collab features you bump into every week —
- Figma’s multi-cursor canvas
- Linear and Notion editing on a phone with spotty Wi-Fi
- Apple Notes syncing across devices
- Multi-device password vaults and to-do lists
- Local-first apps that work offline first and sync second
— all rest on some form of automatic merge. The Yjs and Automerge libraries are the most visible CRDT implementations, and they underpin a long tail of local-first apps. Figma is often cited in this space, but Figma’s own engineers describe their multiplayer system as CRDT-inspired rather than true CRDTs — it’s a public, well-documented design influenced by the same ideas, not a textbook example. Other products (Google Docs being the canonical one) still use the older OT approach. Either way, the problem they’re solving is the one CRDTs are designed for, and my read is that newer systems mostly start from a CRDT library because the integration story is simpler — that’s a vibe, not a benchmarked claim.
For an engineer in the AI era, there’s a second reason to care, by analogy rather than direct adoption: agent state, shared scratchpads, and replicated context across regions look structurally a lot like collaborative documents. The “two writers, no referee, must converge” shape recurs in agent memory and multi-region vector stores. Whether teams reach for CRDTs there or roll something simpler is mostly anecdote at this point.
The short answer
CRDT = data structure + merge function that is commutative, associative, and idempotent
In English: every operation can be applied in any order, applied more than once, or applied alongside someone else’s operation, and the final state is the same. The merge is forced to be safe by the shape of the data, not by a coordinator deciding who wins.
How it works
There are two main flavors. They look different and are mathematically equivalent in what they can express; the difference is what gets sent over the network.
State-based (CvRDT) — ship the whole value, take the join
Each replica holds a value drawn from a
join-semilattice:
the values can be combined by a merge operation that is commutative
(merge(a, b) = merge(b, a)), associative (merge(a, merge(b, c)) = merge(merge(a, b), c)), and idempotent (merge(a, a) = a).
Those three properties are doing all the work. Together they mean: it doesn’t matter which replicas you merge, in what order, or how many times — the result is the same. You can ship full state around with a gossip protocol, drop messages, deliver them out of order, redeliver them, and the system still converges.
The textbook example is a G-Counter (grow-only counter): each replica
keeps its own slot in a vector of counts, only ever increments its own
slot, and merge takes the elementwise maximum. Two replicas that have
seen different increments will, after exchanging state, agree on every
slot — and therefore agree on the sum.
Replica A: [3, 0, 1] (A=3, B=0, C=1)
Replica B: [0, 2, 1]
merge: [3, 2, 1] (max in each slot)
A PN-Counter (supports decrements) is two G-Counters glued together. A G-Set (grow-only set) just unions. An OR-Set (observed-remove set) tags each insert with a unique ID so you can tell “I’m removing the banana I saw” from “I’m removing all bananas, including ones added later.” Once you start composing these, you can build registers, maps, and sequences.
Operation-based (CmRDT) — ship the operations
Instead of broadcasting full state, replicas broadcast each operation (“insert ‘x’ at position p with ID k”). For this to converge without a total order, every pair of concurrent operations must commute. The data structure is designed so they do.
This is where collaborative text editing gets hard. The operations are “insert character” and “delete character” against an ever-shifting sequence of positions, and they have to commute even when two people are typing into the same spot. The trick is to give every character a position that doesn’t change when other characters are inserted.
The general move: each character gets a globally unique identifier that places it in some well-defined order, and concurrent inserts at “the same spot” produce different identifiers that the data structure knows how to interleave deterministically. Different CRDTs do this differently: Logoot and Treedoc use dense fractional identifiers (a path in a tree, or a list of (replica-id, counter) pairs) so a new ID can always be found between two neighbors; RGA takes a different approach, treating the sequence as a growable linked structure where each insert names the element it sits after. Yjs adapts an algorithm called YATA; Automerge’s list type is RGA-based, and its rich-text type uses a newer scheme called Peritext. The family is less uniform than introductory writing usually implies.
What CRDTs cost
This is the seams part. The math is beautiful; the engineering is real.
- Tombstones. To safely delete a character without breaking concurrent inserts that referenced it, most text CRDTs keep a marker for the deleted item rather than removing it. Over a long-lived doc the structure accumulates tombstones, and storage and merge cost grow with edit history, not just current document size. Aggressive garbage collection of tombstones requires a coordination round (usually “everyone has seen everyone’s edits up to time T”), so it’s back-pressure rather than free. Yjs and Automerge have spent years reducing this overhead; it isn’t gone.
- Metadata weight. Every character carrying a unique identifier isn’t free. CRDT documents carry per-element metadata that plain text doesn’t, and operation logs grow with edit count. In recent years the gap has narrowed considerably — Yjs in particular is impressively compact — but the constant factor is still nonzero and shows up in cold-load times. I’m not citing a specific size ratio here because it depends heavily on the algorithm, edit history, and encoding.
- CRDTs converge to a state, not necessarily the right state. Convergence is a weaker promise than human intent. If you and I both edit the same sentence concurrently, a CRDT will deterministically produce some merged sentence. There is no guarantee it makes sense. For text this is usually fine; for “shopping cart of unique items” or “list of approved transactions” it can be very wrong, which is why financial systems still use coordination instead.
- Causal delivery is usually assumed. Operation-based CRDTs generally assume operations are delivered in causal order — if op B was created on a replica that had already seen op A, then no replica processes B before A. State-based CRDTs don’t need this; they merge full states. Causal delivery is weaker than total order, but it isn’t free; the network layer (often vector clocks plus a buffer) has to enforce it.
- OT is not dead. Operational Transformation, the older approach Google Docs is built on, has its own advantages — particularly that the document representation can be plain text without per-character metadata. The OT-vs-CRDT debate isn’t fully settled in production; Martin Kleppmann’s writing is a good place to read both sides honestly.
- Honest gap. I don’t have a defensible “what fraction of collaborative-editing products use CRDTs vs. OT in 2026” number. My read is that newer systems mostly start from CRDT libraries (Yjs, Automerge) because the integration story is simpler, and that several large incumbents still run OT internally. That’s a vibe, not a benchmark.
The key intuition to leave with: a CRDT isn’t magic, and it isn’t just “eventual consistency for documents.” It’s a deliberate restriction of what the data structure is allowed to be — only shapes where merge is forced to be safe — in exchange for the freedom to write to any replica, at any time, without asking anyone.
Famous related terms
- Eventual consistency —
eventual consistency = replicas may disagree now + guaranteed to converge later. CRDTs are the data-structure-level discipline that makes eventual consistency automatic for specific shapes. - Operational Transformation (OT) —
OT ≈ each incoming edit gets transformed against concurrent edits the sender hadn't seen. The pre-CRDT approach Google Docs is built on; same problem, different mechanism. - Vector clock —
vector clock ≈ per-replica counters that detect "happened before" vs. "concurrent". The plumbing most operation-based CRDTs use to deliver ops in causal order. - Local-first software —
local-first ≈ apps where the local copy is the source of truth and the network is an optimization. The architectural style CRDTs unlock; coined by Ink & Switch. - Yjs / Automerge —
Yjs / Automerge ≈ production CRDT libraries— Yjs is fast and tiny; Automerge has a richer document model and JSON-like API.
Going deeper
- Marc Shapiro, Nuno Preguiça, Carlos Baquero, Marek Zawirski, “Conflict-free Replicated Data Types” (2011). Standardized the modern “Conflict-free” expansion of CRDT and laid out the convergence framework most current implementations cite. The acronym itself appears earlier — Preguiça et al.’s 2009 Treedoc work used “Commutative Replicated Data Type” — so 2011 is the formalization, not the naming.
- Ink & Switch, “Local-first software: You own your data, in spite of the cloud” (2019, Kleppmann et al.). The clearest argument for why you’d want CRDTs as a product engineer, not just as a distributed-systems hobbyist.
- Martin Kleppmann’s blog and talks on CRDTs, OT, and Automerge are the best practitioner-level material I know of, and unusually honest about the limits.
- The Yjs and Automerge documentation. Reading the API of either is a fast way to internalize what a CRDT actually exposes to application code.