The Concurrency Challenge in Real-Time Documents
When multiple users edit the same document concurrently (like Google Docs, Notion, or Figma), edits arrive out of order due to network latency. Without structured conflict resolution, character offsets shift and documents diverge into corrupted states.
The Two Core Paradigms: OT vs. CRDT
Operational Transformation (OT)
OT transforms edit operations (Insert, Delete, Retain) against concurrent operations based on character indices:
- If User A inserts character 'x' at position 2, and User B concurrently inserts 'y' at position 0, User A's operation must be transformed to position 3 to account for User B's insertion.
- Tradeoff: Many production OT systems use an authoritative server to order operations and simplify recovery, but decentralized OT algorithms also exist. Correct transformation, history, acknowledgements, and reconnect behavior remain complex.
CRDTs (Conflict-free Replicated Data Types)
Modern collaborative systems increasingly adopt CRDTs (e.g., RGA, Yjs, Automerge):
- Sequence CRDTs use stable element identities or another mergeable ordering representation; not every CRDT uses fractional positions or stores one identifier per visible character.
- Under the algorithm's delivery and causality assumptions, replicas can apply concurrent operations in different orders and converge without a central serialization point. Transport, persistence, access control, and garbage collection still need coordination.
Worked example: one OT transform
The document is abc. At the same moment, Alice inserts x at index 2 (to get abxc) and Bob deletes index 0 (to get bc). The server receives Bob's operation first.
- The server applies Bob's delete:
bc. - Alice's insert at 2 was based on the old document. Bob deleted a character before index 2, so the transform shifts Alice's index down by one, to
insert(x, 1), givingbxc. - Alice's client receives Bob's delete at 0. Her own insert at 2 is after it, so she applies
delete(0)toabxcand also getsbxc. Both sides converge.
A CRDT reaches the same result differently. Alice's x is recorded as "after the element with ID b", so deleting a does not affect where it goes.
OT vs CRDT
| Aspect | OT (Google Docs, CKEditor, ShareDB) | CRDT (Yjs, Automerge, Loro) |
|---|---|---|
| Ordering | Usually a central server orders operations | No central ordering needed; merges commute |
| Offline editing | Possible but hard after long divergence | Natural; merge on reconnect |
| Metadata overhead | Small (just the operations) | IDs per element; modern encodings (Yjs, Automerge 2) compress runs well |
| Correctness risk | Transform functions are notoriously hard to get right | Interleaving and intent anomalies (Kleppmann) |
| Rich text and structure | Mature in production editors | Improving (Peritext, Yjs types) |
Figma, often cited as a CRDT example, actually uses a server-authoritative model inspired by CRDTs. Each object property is last-writer-wins, and the server decides the order. It is not a peer-to-peer text CRDT.
Service architecture and numbers
- Route all clients of one document to the same session server (consistent hashing on
doc_id). The document state then lives in one process's memory, and ordering is local to that process. - With 50 concurrent editors each sending about 5 operations per second, the server receives 250 operations per second and must broadcast about 12,500 messages per second. Batch outgoing operations every 30 to 50 ms to cut message counts.
- Snapshot every 500 to 1,000 operations, so opening a document loads the latest snapshot plus a short tail of operations instead of the entire history.
- Throttle cursor and selection updates (for example to 10 per second) and never store them; they are ephemeral.