The system-level view of collaborative editing: how documents are hosted, how edits flow, how state is persisted. For the algorithm that reconciles concurrent edits in detail, see the collaborative editor.
Requirements
Functional
- Multiple users edit one document simultaneously and see each other's changes live.
- Live cursors and presence.
- Full version history; comments and suggestions.
- Offline editing that reconciles on reconnect.
Non-functional
- Edits appear to collaborators in well under a second.
- Local typing is never blocked by the network.
- Never lose an edit; the document must converge to one state for everyone.
100M documents, 10M concurrent editing sessions
Most documents have 1 editor; a small fraction have 2-10; rarely more
Edit rate: a fast typist is ~5 operations/secondThat distribution matters: optimise for the single-editor case, and make collaboration correct rather than fast for the rare 50-person document.
Architecture
Clients ⇄ WebSocket ⇄ Session server (one per ACTIVE document)
│ authoritative in-memory doc state
│ transforms + orders incoming operations
├──→ operation log (append-only, durable)
└──→ snapshot store (periodic materialised doc)The key structural decision: **all editors of one document are routed to the same session server.** That gives a single point of ordering, which is what makes reconciliation tractable — you get a total order of operations for free instead of needing distributed consensus.
A routing layer maps document_id → session server, in the same shape as the session
registry in WhatsApp. Consistent hashing places documents
across servers; a document with no active editors has no session at all.
The operation log
Documents are stored as an append-only log of operations, not as repeatedly overwritten blobs.
doc:abc ops [1] insert "H" @0
[2] insert "i" @1
[3] delete @0
…
snapshot @1000: {content, version: 1000}Why a log: it gives version history and undo for free, it makes each write small and appendable rather than rewriting a whole document, and it is exactly what a reconnecting client needs — "send me everything after version N".
Snapshots every N operations keep load time bounded: materialise the snapshot, then replay the tail. Without them, opening an old document means replaying a million operations.
Concurrency control
Two clients type at the same position simultaneously. Naively applying both produces different results on different machines. The two solutions:
Operational transformation (OT). Each operation is transformed against the concurrent
operations it did not see, adjusting indices so the intent is preserved. The central server does the transformation and assigns a version. It is what Google Docs actually uses; the transformation functions are notoriously subtle to get right, and it depends on the central server for ordering.
CRDTs. Give every character a unique, globally ordered identifier so operations commute
— any order of application converges to the same document. Simpler to reason about and works peer-to-peer without a central server, at the cost of per-character metadata (mitigated but not eliminated by modern designs like RGA and Yjs).
For a system-design interview: **name both, say OT for a centralised server with a large document, CRDTs when offline-first or peer-to-peer matters**, and move on to the systems consequences. The full mechanics are in the LLD article.
Presence and cursors
Cursor positions and selections are high-frequency and worthless once superseded — the opposite of document operations.
Document operations → durable log, must never be lost
Cursor positions → in-memory only, broadcast, dropped freelyKeeping the two paths separate is the design point. Throttle cursor broadcasts to a few per second; nobody perceives the difference, and it removes most of the message volume.
Cursor positions must also be transformed alongside operations — if someone inserts text above your cursor, your cursor moves down, or every collaborator's caret drifts.
Offline
A client accumulates operations locally while offline and sends them on reconnect, where they are transformed against everything that happened meanwhile. Long offline periods make that transformation expensive and increase the chance of a semantically confusing merge — which is why CRDTs are attractive for offline-first products.
Note the contrast with file sync, where an opaque binary cannot be merged and the answer is a conflicted copy. Text with structure can be merged, and that is the whole reason this class of algorithm exists.
Follow-ups to expect
- Session server failure — clients reconnect to a new server, which loads the last
snapshot plus the log tail; unacknowledged client operations are resent.
- Access control — permissions checked at session join and re-evaluated on change, so
revoking access ejects the editor.
- Large documents — chunk by section so the whole document need not be in memory.
- Comments and suggestions — anchored to positions that must be transformed too, and
which can become orphaned when the anchored text is deleted.
- Export and import — a rendering pipeline separate from the editing path.
Track this problem on the System Design sheet.
Frequently asked
Why route all editors of a document to the same server?
Because reconciliation needs a total order of operations. A single session server holding the authoritative in-memory state provides that ordering directly, which is far simpler than running a consensus protocol across servers. A routing layer maps document ids to session servers by consistent hashing, and a document with no active editors needs no session at all.
Should collaborative editing use OT or CRDTs?
OT when there is a central server and documents are large — it is what Google Docs uses, and it avoids per-character metadata, at the cost of subtle transformation functions and a dependency on central ordering. CRDTs when offline-first or peer-to-peer operation matters, since operations commute and converge without a coordinator, at the cost of extra metadata per character.
How are documents stored for collaborative editing?
As an append-only log of operations with periodic snapshots. The log gives version history, undo and cheap appends, and lets a reconnecting client ask for everything after the version it last saw. Snapshots bound the cost of opening a document, since you materialise the snapshot and replay only the tail rather than a million operations.