Designing Collaborative Document Editing
Design real-time collaborative editing for 50 concurrent editors per document using operational transform or CRDTs, sessions, snapshots and offline sync.
Collaborative editors let many people change the same document at once and converge on the same result. The core problem is merging concurrent edits without losing intent, while keeping latency low enough that typing feels local.
Requirements
Functional
- Multiple users edit the same document simultaneously and see each other's changes live.
- Show cursors and selections of collaborators.
- Version history with restore; comments and sharing permissions.
- Offline editing that merges on reconnect.
Non-functional
- 100M documents, 10M daily active users, up to 50 concurrent editors per document.
- Remote edits visible within 200 ms; local edits applied instantly.
- Convergence: all replicas end in an identical state.
- No lost edits; durability of every acknowledged change.
Capacity estimation
- 10M DAU, 1M concurrent editing sessions at peak, each typing roughly 1 operation per second: 1M operations per second across the fleet.
- Operation size around 100 bytes: 100 MB per second ingest.
- Persistent log: 5B operations per day at 100 bytes is 500 GB per day; compact into snapshots.
- Connections: 1M WebSockets at 100,000 per node requires only about 10 to 20 nodes, but sessions are sticky per document.
High-level design
Client (local replica) <==WebSocket==> Edge Gateway
| route by document_id
v
Document Session Server (one owner per doc)
| | |
v v v
Op Log (append) Snapshot store Presence (ephemeral)
|
v
Async: search index, version history, comments
Data model
ops(doc_id, seq, client_id, op_payload, ts) -- append-only, ordered by seq
snapshots(doc_id, version_seq, content_blob)
acl(doc_id, principal, role)
Current state equals the latest snapshot plus the ops after it. Snapshot every N operations (for example 500) or on session close, so loading never replays a long log.
Deep dives
Operational Transform (OT). Clients send operations such as insert at position 5. A central server assigns a total order and transforms each incoming operation against operations it has not seen, so intent is preserved (two inserts at the same place shift each other). OT needs a central sequencer, which suits a server-owned document session, and the transform functions are subtle to get right, especially for rich text.
CRDTs. Each character gets a unique, ordered identifier, so operations commute and replicas converge without a central transformer. This supports peer-to-peer and offline use naturally. The costs are metadata overhead (identifiers per character, mitigated by run-length encoding and garbage collection) and more complex history and permissions.
Which to choose. A server-centric product with strong access control often uses OT or a server-ordered CRDT variant. Offline-first or local-first products lean on CRDTs. Both give convergence; the trade-off is simplicity of the server against flexibility of the clients.
Session ownership. All editors of a document connect to the same session server, chosen by consistent hashing on doc_id. Single ownership gives a natural serialisation point, avoids distributed locking, and lets the server hold the document in memory. A coordination service stores the doc to server lease for failover.
Client behaviour. Apply the local edit immediately, send it with the last known server revision, keep unacknowledged operations in a pending buffer, and transform incoming remote operations against pending ones. Retransmit on reconnect; the server deduplicates by client_id and per-client sequence number.
Presence. Cursors and selections are ephemeral, sent over the same socket at a throttled rate, and never persisted. Losing them is harmless.
Offline. The client stores its pending operations locally. On reconnect it sends them with the base revision, and the server merges via transform or CRDT merge.
Permissions and history. Check the ACL at connect and on each change to roles. Version history is built from snapshots and the op log, grouped into named versions by time or author.
Failure modes and trade-offs
- Session server crash: another server takes the lease, loads the latest snapshot, replays the log tail, and clients resend pending operations. Writes are acknowledged only after the log append is durable.
- Hot document: a document with thousands of viewers and few editors should serve viewers read-only through a fan-out tier, so the session server handles only editors.
- Log growth: compaction into snapshots, and CRDT tombstone garbage collection once all replicas have seen a deletion.
- Large paste or import: treat as a bulk operation, chunk it, and rate limit per user.
- Undo semantics: undo must reverse only the user's own operations, which is hard in both models and needs inverse operations transformed forward.
Key takeaways
- Apply edits locally first and reconcile in the background to keep typing instant.
- OT relies on a central order; CRDTs remove that need at the cost of metadata.
- One session server per document gives a single serialisation point and simple in-memory state.
- Store an append-only operation log plus periodic snapshots for fast loads and history.
- Presence is ephemeral; durability applies only to document operations.