Under the hood · interactive
How CRDTs work
Two people edit the same list on two phones, both offline. They reconnect. No "resolve conflict" dialog, no lost edits, no server refereeing — just one agreed list. That's a CRDT, and the trick is simpler than it sounds.
Local-first apps (Linear's sync, collaborative editors, offline notes) share a hard problem: two replicas drift apart while disconnected, and something has to reconcile them. The usual answers are a central server that decides, or a merge dialog that dumps the problem on the user. A Conflict-free Replicated Data Type takes a third path: design the data so that merging is automatic and always agrees, no matter what happened in what order. Merge two replicas — or the same one twice — and you get the identical result. Below is one of the most useful CRDTs, a set you can add to and remove from, running as two offline replicas you control.
An Observed-Remove Set, live
Replica A · phone 1
Replica B · phone 2
bread. On B, add bread
again. Now Sync. It stays — a concurrent add beats a remove, so you never
silently lose the thing someone just re-added. Toggle "show the plumbing" to see why.
The idea: merge by rules, not by refereeing
A naive shared set breaks the instant edits cross: if A deletes an item and B keeps
it, whose wins? An OR-Set sidesteps the question by never storing a bare item. Every
add stamps the item with a unique, invisible tag (bread#a3). A
remove doesn't delete the item — it records the specific tags it has
observed as removed. An item is "present" if it has at least one tag that
nobody has removed. Merging two replicas is then trivial and can't conflict: take the
union of all adds, and the union of all removes. Order doesn't matter; running it twice
changes nothing.
Why "add wins," and why that's the safe default
Play the hard case above and watch the tags. When A removes bread, it
tombstones the tag it could see — say bread#a1. But B's re-add minted a
fresh tag, bread#b7, that A's remove never observed. After the
union, bread#b7 is still live, so bread stays. The rule falls
out of the design, not a special case: a remove only cancels what it actually saw.
"Add wins" is the humane choice — recovering a deleted item is one tap; recovering data
a merge silently ate is impossible.
The property that makes it work
Every CRDT merge is built to be commutative, associative, and idempotent — order-independent, grouping-independent, and duplicate-proof. That trio is exactly what an unreliable network hands you: messages arrive out of order, get batched differently, and get redelivered. Because merge shrugs all three off, replicas that have seen the same set of edits always reach the same state, with no coordination — the guarantee is called strong eventual consistency. Counters, registers, maps, JSON, and whole text documents all have CRDT designs (Yjs and Automerge are the libraries you'd reach for); the OR-Set is just the one small enough to watch.
One honest caveat: the toy keeps every tag forever, which would grow without bound — real CRDTs add tombstone garbage-collection and compression, and sequence CRDTs for text do more work to keep characters in the right order. The core you're clicking on — unique tags, remove-what-you-saw, merge-by-union — is the genuine mechanism.