CRDT vs operational transform for real-time editing #
You are building collaborative editing — a shared document, a whiteboard, a spreadsheet — and the architecture decision comes down to two families of algorithms. Operational transform (OT) powers Google Docs and many classic editors: clients send operations to a central server, which transforms concurrent operations against each other so they apply cleanly. Conflict-free replicated data types (CRDTs), implemented by libraries such as Yjs and Automerge, give every replica a data structure whose updates merge deterministically in any order, with no central transformation step. Both produce convergent documents. They differ in where the complexity lives, what the server must do, how well they work offline, and what they cost in memory — and those differences decide which fits your WebSocket architecture.
Root cause #
The problem both solve is concurrent edits to the same data. Alice inserts “x” at position 5 while Bob deletes the character at position 3. If each simply applies the other’s operation as received, Alice’s insert lands one position off on Bob’s screen, and the replicas diverge. Some mechanism must adjust for concurrency.
OT adjusts the operations. When the server receives Alice’s insert after having applied Bob’s delete, it transforms the insert — position 5 becomes 4 — before applying and forwarding it. Correctness depends on transformation functions for every pair of operation types, which are notoriously hard to get right beyond plain text, and on a central server that establishes one total order of operations.
CRDTs adjust the data model. Instead of positions, every character (or element) gets a unique, immutable identifier, and an insert says “after element X”. Concurrent inserts after the same element are ordered by a deterministic tiebreaker. Because operations reference identities rather than positions, they commute: any replica can apply them in any order and reach the same state. No server-side transformation is needed.
Resolution #
Choose by your constraints rather than by reputation. The matrix below summarises the practical differences for a WebSocket-based product; the paragraphs after it explain the ones that usually decide.
Server role and scaling. With OT, the server is on the critical path of every edit: it must receive operations for a document in order, transform them, and broadcast the results, so each document is effectively pinned to one process. That is manageable — it is how the big hosted editors work — but it makes the server complex and stateful. With a CRDT, the server relays and stores updates. It still benefits from holding a document replica to answer sync requests, as in syncing Yjs documents over WebSockets, but it never has to understand edit semantics, and relaying between instances is safe in any order.
Offline and flaky connections. CRDT updates made offline merge on reconnect with no special handling. OT clients that edit offline accumulate operations against a stale base and must rebase them through every operation they missed, which works for short gaps and becomes fragile for long ones.
Metadata and memory. CRDTs pay for commutativity with identifiers and tombstones: every inserted element carries an id, and deleted elements often leave markers. Modern implementations compress this heavily — Yjs merges runs of consecutive insertions from one client into single items — so typical text documents cost a small multiple of their plain size, but documents with long, churny histories grow until compacted.
Validation and authority. If every edit must be checked against business rules — permissions per section, schema constraints, locked fields — OT’s central server is a natural enforcement point. With CRDTs, clients apply updates locally before any server sees them, so rejecting an edit means reverting it after the fact. Enforce coarse permissions (who may edit this document) on the connection, and design documents so fine-grained rules are rarely needed.
A practical client for either approach sends edits through the same WebSocket as everything else. With a CRDT library the payloads are opaque binary updates; with OT they are operation objects with revision numbers:
// OT client essentials: operations carry the revision they were made against.
interface TextOp { rev: number; ops: Array<{ retain?: number; insert?: string; delete?: number }> }
class OtClient {
private rev = 0; // last server revision applied
private inflight: TextOp | null = null;
private buffer: TextOp['ops'] | null = null;
constructor(private ws: WebSocket, private transform: (a: any, b: any) => [any, any]) {}
localEdit(ops: TextOp['ops']) {
if (this.inflight) { this.buffer = this.buffer ? compose(this.buffer, ops) : ops; return; }
this.inflight = { rev: this.rev, ops };
this.ws.send(JSON.stringify({ type: 'op', ...this.inflight })); // one op in flight at a time
}
onServerOp(op: TextOp, isAck: boolean) {
this.rev = op.rev;
if (isAck) { // our op was accepted at op.rev
this.inflight = null;
if (this.buffer) { const b = this.buffer; this.buffer = null; this.localEdit(b); }
return;
}
// Someone else's op: transform it against our pending work before applying.
if (this.inflight) [this.inflight.ops, op.ops] = this.transform(this.inflight.ops, op.ops);
if (this.buffer) [this.buffer, op.ops] = this.transform(this.buffer, op.ops);
applyToEditor(op.ops);
}
}
declare function compose(a: any, b: any): any;
declare function applyToEditor(ops: any): void;
The “one operation in flight, buffer the rest” discipline is what keeps OT tractable on the client; the server does the other half of the transformation. A CRDT client, by contrast, just sends every local update and applies every remote one.
Edge cases #
Undo. Collaborative undo should revert my last change, not the document’s last change. Both approaches support this, but it must be built in: Yjs provides an UndoManager scoped to local transactions; OT systems invert and transform the user’s own operations.
Rich text and structure. Text with formatting, nested lists and embedded objects multiplies OT’s transformation pairs. CRDT libraries model these as nested shared types, which is one reason editors such as ProseMirror, TipTap and Lexical have mature Yjs bindings.
Non-text data. For forms and records with independent fields, neither is necessary: per-field last-write-wins is simpler and adequate.
Verification #
Whichever you choose, test convergence with randomized concurrent edits: simulate several clients making random edits while messages are delayed and reordered, deliver everything, and assert every replica ends identical. Run thousands of iterations with a seeded random generator so failures are reproducible. CRDT libraries ship extensive fuzzing of their own, but your integration — how updates are relayed, persisted and replayed — still needs this test.
Operational checklist #
FAQ #
Is a CRDT always better than OT? #
No. CRDTs make offline editing, peer-to-peer sync and horizontal scaling simpler, which is why most new products choose them. OT remains a good fit when a central server must validate every edit or when per-document memory must stay minimal.
Does Google Docs use OT or CRDTs? #
Google Docs is built on operational transform with a central server establishing operation order. Many newer collaborative tools use CRDT libraries such as Yjs or Automerge, or hybrid designs.
Can I use a CRDT with a central server? #
Yes, and most production deployments do. The server relays and persists updates and answers sync requests; it simply never needs to transform operations.
What about Automerge versus Yjs? #
Both are mature CRDT libraries. Yjs is optimised for text editing performance and has broad editor bindings; Automerge emphasises a JSON-like document model with rich history. Evaluate them on your data shape and editor.
Related #
- Syncing Yjs Documents over WebSockets — the CRDT transport in practice.
- Persisting CRDT Updates on the Server — storage and compaction.
- Last-Write-Wins Conflict Resolution over WebSockets — the simpler rule for independent fields.
- Handling Out-of-Order WebSocket Messages — ordering outside collaborative documents.
Back to Collaborative Editing with CRDTs.