Free preview

Conflict-Free Replicated Data Types

In one line: CRDTs make operations commute by construction, so no coordinator is needed. The cost is paid in the data — every character grows metadata — and there is a failure mode the design does not mention.

The two properties

CRDTs satisfy commutativity and idempotency by assigning two properties to every character:

  • A globally unique identity
  • A global ordering

Each property fixes exactly one of Lesson 4's two failures

The mapping is one-to-one, which is why the design is elegant.

Globally unique identity fixes idempotency. Lesson 4's diagnosis was that delete(0) names a slot, so applying it twice removes two different characters. With identity, a delete names that specific character — site 123e..., position 1.5. Apply it a hundred times: the character is either present or already gone. Duplicate delivery, retries, and queue failover all become harmless.

Global ordering fixes commutativity. If every character has a position that is total and stable across replicas, then applying a set of inserts in any order yields the same sequence, because each one knows where it belongs independently of when it arrives.

Lesson 4's problem            CRDT's answer
--------------------------    ---------------------------
operations name slots     ->  operations name entities
indexes shift on insert   ->  positions never change

A CRDT is a data structure whose operations commute by construction, so no coordinator is needed to make replicas agree. That is the whole definition, and everything else is engineering to satisfy it for a particular data type.

The SiteID is doing double duty, incidentally: it makes identities unique without a central allocator — each site mints its own — and it breaks ties deterministically when two sites happen to choose the same position.

Fractional positions

The value of PositionalIndex can be in fractions for two main reasons: the PositionalIndex of other characters won't change; the order dependency of operations between different users will be avoided.

A user from site 123e4567-e89b-12d3 inserts A at PositionalIndex 1.5... an insert() between O and T didn't affect the position of T.

The trick is that the rationals are dense and the integers are not

This is the mathematical core of the whole approach, and it is one sentence: between any two rational numbers there is another rational number. Between any two consecutive integers there is nothing.

That is why integer indexes force renumbering. If positions must be integers and you insert between 2 and 3, everything from 3 onward must shift — which is precisely the operation that invalidates every other replica's in-flight indexes. With rationals you insert at 2.5 and nothing else moves.

The consequence is exactly what Lesson 5 identified as OT's weakness:

OT:    an insert at the start rewrites every subsequent index
       -> operations are order-dependent
       -> a coordinator must fix the order

CRDT:  an insert at the start changes nothing else
       -> operations are order-independent
       -> no coordinator needed

Choosing a dense position space instead of a discrete one is what removes order dependence. That single change is worth more than any amount of transformation logic — and it generalizes beyond text: the same trick orders items in Trello lists, Jira backlogs, and any user-reorderable list, precisely so reordering one item does not rewrite the rest.

Fractional positions grow without bound, and the design never says so

The example stops at 1.5. Repeat it.

Suppose several collaborators repeatedly insert at the same point — which is exactly what happens when people type left-to-right in one paragraph, each character going between the previous one and what follows:

insert between 1 and 2      -> 1.5
insert between 1 and 1.5    -> 1.25
insert between 1 and 1.25   -> 1.125
insert between 1 and 1.125  -> 1.0625
...

Each insertion at the same point costs roughly one more bit of precision. Typing a paragraph of 1,000 characters at a single position — the ordinary case, not a pathological one — needs on the order of 1,000 bits per position. A 64-bit float exhausts its precision after about 50 such insertions, after which two distinct characters collide at the same position and the total order breaks.

This is a known and named problem — interleaving/identifier growth — and real CRDT text algorithms are largely defined by how they solve it:

AlgorithmApproach
LogootVariable-length lists of integers as positions, not a single number
LSEQAdaptive allocation that varies the strategy by depth to keep identifiers short
RGAAbandons numeric positions entirely for references to a predecessor character
YATA (Yjs)Predecessor/successor links with integer clocks — what production editors use

The design presents fractional indices as the solution and does not mention that they are only the idea of a solution. In an interview, naming the identifier-growth problem and one algorithm that solves it is the single highest-signal thing you can say about CRDTs — it distinguishes having read about them from having understood them.

The mitigation in practice is periodic renumbering: when a document is quiescent, one site rewrites all positions compactly and broadcasts the new mapping — which requires coordination, so it is done rarely and off the hot path.

The size cost

A CRDT document is several times larger than the text it holds

The design never quantifies this, and it is the decisive practical difference from OT.

Every character carries its metadata:

Value            1 byte  (or a few, for Unicode)
SiteID          16 bytes (a UUID)
PositionalIndex  8 bytes (and GROWING, per the previous callout)
Tombstone/clock  ~8 bytes
                --------
                ~33 bytes of metadata per 1 byte of text

So a 100 KB document becomes roughly 3 MB in memory, and Lesson 2's 32 TB/day of text becomes on the order of a petabyte if stored in CRDT form.

Real implementations attack this hard — run-length encoding of adjacent characters from the same site, short site identifiers assigned per document instead of UUIDs, and compression — and get the overhead down substantially. But the structural point stands:

OT:    complexity in the CODE,  document stays plain text
CRDT:  complexity in the DATA,  document carries per-character metadata

That trade is the answer to "why does Google Docs use OT?", and it is why the design can call CRDTs simpler while conceding that the major platforms do not use them.

There is a second, subtler cost: tombstones. A deleted character usually cannot be removed outright, because a concurrent operation may still reference it — so it is marked deleted and kept. A document that has been heavily edited accumulates more tombstones than live characters, and garbage-collecting them safely requires knowing that every replica has seen the deletion, which is a coordination problem.

What CRDTs actually guarantee

CRDTs guarantee eventual consistency. Even if users go offline, their local replicas converge with other replicas once they reconnect and synchronize updates. Major platforms such as Google Docs and Etherpad rely on operational transformation. CRDTs are gaining popularity because they support serverless and peer-to-peer collaboration models.

Eventual consistency is the honest claim, and Lesson 8 contradicts it

This paragraph is careful and correct. Hold onto it, because the evaluation lesson says something incompatible.

"Eventual consistency" is exactly right, and it is not a weakness — it is the point. CRDTs give up on all replicas agreeing at every instant in exchange for never needing to ask anyone's permission. Given the same set of operations, in any order, with any delays, every replica computes the same document. What you cannot say is when.

Offline editing works, and this is the property OT struggles with most. A CRDT client can be disconnected for a week, accumulate thousands of operations, reconnect, and merge with no transformation chain — because the operations were never order-dependent.

"Serverless and peer-to-peer" is the real headline. If operations commute, there is nothing for a server to decide. Clients can sync directly with each other, in any topology, and still converge. That is why CRDTs power local-first software, offline-capable mobile apps, and collaborative tools with no central authority.

Now set that beside Lesson 8's compliance claim: "We ensure strong consistency for conflict resolution using OTs or CRDTs."

Eventual consistency and strong consistency are not the same guarantee, and CRDTs provide the first. Lesson 7 works through what that contradiction costs the design.

PropertyOTCRDT
Where complexity livesThe algorithm — transformation rulesThe data — per-character metadata
Central coordinatorRequired — someone fixes the orderNot required — operations commute
Document sizePlain textMultiples of the text, plus tombstones
Order dependenceYes — network variance becomes correctness workNo — any order converges
Offline editingPainful — long transformation chainsNatural
ConsistencyCC model — causality + convergence, via a coordinatorEventual
Used in production byGoogle Docs, EtherpadYjs, Automerge, Figma, Riak

The design's own closing question is the best argument for CRDTs

"What happens if two users collaborating have different internet speeds? Which is better suited?" "Operations are order-dependent in OT, whereas operations in CRDTs are order-independent. This is why CRDTs are a suitable solution."

Correct — and worth noticing what it implies about the design, since Lesson 8's latency section is entirely about geographically distributed collaborators and the design nonetheless runs a centralized ordering queue, which is OT's architecture.

The generalizable point: the technique that tolerates network variance best is the one whose correctness does not depend on arrival order. That is a property of the algebra, not of the implementation quality, so no amount of engineering makes OT equally tolerant.

Cursor highlighting is conflict avoidance, not conflict resolution

"While OT and CRDTs handle algorithmic conflicts, UI features like real-time cursor highlighting help users naturally avoid editing the same text simultaneously."

This is a good observation and easy to undervalue. Showing every collaborator's cursor and selection reduces the rate at which conflicts occur at all, because people can see where others are working and instinctively stay out of the way.

It changes nothing about correctness — the algorithm must still handle every conflict — but it moves the conflict rate from "constant" to "rare," which matters for a mechanism whose cost grows with contention.

Design the interface so the expensive case becomes uncommon. The same instinct as the typeahead chapter's debouncing: the cheapest conflict is the one that never happens.

Key takeaway

CRDTs give every character a globally unique identity (which fixes idempotency, because operations name entities rather than slots) and a global ordering via fractional positions (which fixes commutativity, because the rationals are dense and the integers are not, so an insert moves nothing else). That removes the coordinator entirely and makes offline and peer-to-peer editing natural. The costs are real and the design states neither: fractional positions grow unboundedly — roughly a bit per insertion at the same point, which real algorithms like Logoot, LSEQ, RGA, and YATA exist to solve — and per-character metadata makes the document several times its text size, plus tombstones. OT puts the complexity in the code; CRDTs put it in the data, and CRDTs guarantee eventual, not strong, consistency.

Next: choosing between them, and the contradiction in the evaluation.

Enjoying the preview?

Create a free account to unlock the rest of this course, the in-browser judge, and live AI mock interviews.

Sign up free to continue