Part VI β The Blueprint Β· Lesson 11.11
HLD β Google Docs
In one lineTwo people typing in the same paragraph is the entire problem β OT or CRDT is the answer.
Where you've seen itTwo colleagues typing into one sentence, and the result reading correctly for both of them.
Diagram
1β2 Β· SCOPE AND SCALE
in scope rich text editing Β· multi-user cursors Β· comments Β·
offline editing Β· version history
out export pipeline Β· sharing permissions UI Β· print
scale docs up to ~100k words Β· 2β20 concurrent editors
sub-100ms local echo is non-negotiable3 Β· THE CORE PROBLEM β naive sync loses data
both start with: "Hello"
Alice inserts "!" at 5 Bob inserts " world" at 5
"Hello!" "Hello world"
β β
ββββββββββββΊ server ββββββββββββ
β
last-write-wins β ONE EDIT IS LOST β
Text needs index transformation. This is why 11.9's
last-write-wins was fine for shapes and is fatal here.6a Β· DEEP DIVE β OT vs CRDT
OPERATIONAL TRANSFORM (OT)
ops carry positions; the server transforms each incoming op
against ops it has already applied
Bob's insert@5 ββtransform against Alice's insert@5βββΊ insert@6
result: "Hello! world" both edits survive
β compact ops Β· β needs a central server Β· β hard to prove correct
CRDT (Yjs, Automerge)
every character gets a unique, ordered id; merges are
commutative β order of arrival doesn't matter
β works peer-to-peer and offline Β· β provably convergent
β metadata overhead per character Β· β heavier payloads
Choose OT if you already have an authoritative server.
Choose CRDT if offline and P2P matter. Say WHY, either way.6b Β· DEEP DIVE β LATENCY AND PRESENCE
local echo apply the keystroke to the local model IMMEDIATELY
(never wait for the server β typing must feel free)
β
βββΊ render
βββΊ queue op β server β broadcast β peers
presence cursors + selections, ~20Hz, throttled,
sent on a SEPARATE ephemeral channel β never
persisted, never part of the document historyHow it works
- The document is a data model, not the DOM β a tree of blocks with marks. Rendering derives from it.
- Local echo first: apply, render, then send. Any perceptible delay makes typing feel broken.
- Sync via OT or CRDT over WebSocket (2.4), with an explicit reconnect-and-catch-up path.
- Presence is ephemeral and travels separately from document operations.
- Offline buffers ops locally (9.1) and merges on reconnect β CRDTs make this trivial, OT makes it work with server help.
Trade-offs
| Choice | Cost |
|---|---|
| OT | Compact, mature, server-dependent, tricky to implement |
| CRDT | Offline-friendly, convergent, larger payloads |
| contentEditable | Native input/IME; famously inconsistent across browsers |
| Custom-rendered editor | Full control; you rebuild IME, selection, and a11y |
Accessibility (8.3): rich-text editors are one of the hardest a11y surfaces β announce collaborator changes politely without spamming, keep keyboard shortcuts discoverable, and never move the user's cursor because of a remote edit.
Interview angle
"Two users type in the same sentence. How does it not break?"
- Position indices shift under concurrent edits, so raw last-write-wins loses one of them.
- OT transforms incoming operations against already-applied ones; CRDTs give every character a unique id so merges commute.
- I'd pick based on constraints β OT with an authoritative server, CRDT if offline or peer-to-peer editing is required β and I'd apply locally first so typing never waits on the network.
Recap
- Concurrent text editing is the problem; OT and CRDT are the two real answers.
- Local echo immediately; sync in the background.
- Presence is ephemeral and separate from document history.