Design Google Docs / Collaborative Editing
Several people type into the same paragraph at once, and every copy has to end up identical without anyone's keystrokes vanishing. The design gives each document one server that orders its edits, and keeps an operation log that doubles as version history.
1 · Requirements
The Prompt
Let several people edit one document at once and see each other's cursors live, with every copy converging, offline edits, and full version history.
Functional
- Several users edit one document at the same time
- See other editors' cursors and selections live
- Edit offline and merge on reconnect
- Browse version history and restore an earlier version
- Share with viewer, commenter, and editor roles
Non-Functional
- A remote edit appears in under 200 ms
- Every copy converges to the same text, and no acknowledged edit is lost
- Large documents open in under 2 seconds
2 · Estimates
Drill This Phase First
Show the Answer and Estimates
Answer
About 4.3 TB a day
Half of 1M is 500,000 operations a second. At 100 bytes that is 50 MB a second, and times 86,400 seconds is about 4.3 TB a day.
- Editors
- 1M concurrent editors at peak, each sending about 1 batched operation a second: 1M operations a second
- Fan-Out
- About 3 editors per active document, so each operation goes to 2 others: about 2M outbound messages a second
- Op Log
- Averaging 500,000 operations a second at 100 bytes: about 4.3 TB of operation log a day
- Snapshots
- 1B documents at about 20 KB per snapshot: roughly 20 TB in object storage, plus older versions
- Sockets
- About 50,000 WebSockets per server: 20 servers at peak, and 40 to survive a zone failure
- Latency
- Under 200 ms for a remote edit: one hop to the document's server, a transform, and one hop out
3 · API
WebSocket /docs/:id/live → client sends { op, base_revision, client_seq }; server acks and broadcasts { op, revision, author }
GET /docs/:id → { snapshot, revision, ops_since_snapshot[] }
GET /docs/:id/revisions?before= → { revisions[{ revision, author, ts }] }
POST /docs/:id/restore { revision } → { revision } (applied as a new operation)
PUT /docs/:id/permissions { user_id, role: viewer|commenter|editor } → 204Data Model
documents (SQL):
doc_id (PK) : uuid
owner_id : uuid
title : string
head_revision : bigint
snapshot_revision : bigint
snapshot_key : string object storage key
operations (Cassandra, partition = doc_id, cluster = revision):
doc_id, revision (PK)
author_id : uuid
client_seq : int dedupes resends
op : bytes insert or delete at a position
ts : timestamp
permissions (SQL):
doc_id, user_id (PK)
role : enum(viewer|commenter|editor)4 · High-Level Design
5 · Deep Dives
Operational Transformation
Every operation names the revision it was based on. When two edits based on revision 41 arrive, the server applies the first as revision 42 and transforms the second against it, shifting its position so both intents survive. One server ordering each document keeps the transform simple, and clients transform incoming operations against their own pending ones.
CRDTs as the Alternative
A CRDT gives every character a unique, ordered ID, so replicas converge in any order with no central server. That suits offline and peer-to-peer editing, at the cost of metadata that can be several times the size of the text. Server-backed editors such as Google Docs use OT; local-first editors lean on CRDTs.
Snapshots, History, and Offline
Replaying a year of operations on every open would be slow, so a worker writes a snapshot every 1,000 operations and the client loads the snapshot plus the tail. Version history is the operation log grouped by author and time. An offline client queues edits against its last revision and sends them on reconnect, where the server transforms them like any late edit.
6 · Wrap Up
The Short Version
Each document has one owning server, picked by consistent hashing, that orders edits with operational transformation (or a CRDT). Clients hold a WebSocket to that server. Operations append to a log keyed by (doc_id, revision), with snapshots in object storage.