Design a collaborative editor
Two people type into the same paragraph at the same moment, each on their own copy, with a network in between. Within a second both screens must show the same text, and it must be the text both of them meant. This design is about the algorithms that merge concurrent edits (operational transformation and CRDTs) and the system around them: real-time connections, an operation log, snapshots and presence. It follows the interview framework phase by phase.
1. The prompt
"Design Google Docs: many users edit the same document at once and see each other's changes and cursors live; documents keep a full version history."
Most designs in this track scale out by spreading load. This one does the opposite for each document: it funnels every edit through one place so there is a single order. The interesting part is what happens between two copies that disagree.
2. Frame the problem
How many editors per document?
Usually 1–10; rarely up to 100.
Per-document load is small; one server per document works.
How many documents and open sessions?
100M documents; ~5M editors connected at once.
The connection tier is the big fleet, not the documents.
Must it work offline?
Short disconnections, yes; long offline editing is a stretch goal.
Pushes towards OT with a central server, with CRDTs as the offline option.
Plain or rich text?
Rich text, but discuss plain text first.
The merge algorithm is clearest on characters.
Version history?
Yes, back to the start.
An append-only operation log plus snapshots.
Do cursors need saving?
No, only live.
Presence stays in memory, throttled.
3. What it must do
- Open a document and edit it; see other people's edits and cursors in real time.
- Merge concurrent edits automatically; nobody's typing is lost.
- Keep version history; share with view, comment or edit permissions.
Not now:
- Rich formatting details.
- Comments.
- Spreadsheets (the same ideas on cells).
4. How well it must do it
| Target | Why this number |
|---|---|
| Instant local typing; others see it within a few hundred ms speed | Waiting for a server round trip per keystroke feels broken. |
| Convergence: every copy ends up identical and preserves each person's intent consistency | Diverged copies are silent data loss. |
| Durable: an acknowledged edit is never lost durability | People trust the editor with their work. |
| Scale: millions of open sessions load shape | Lots of connections, each fairly quiet. |
5. Numbers that change the design
| Quantity | Working | → so we… |
|---|---|---|
| Open connections | Say 5M concurrent editors | 5M WebSockets → so a gateway tier holding ~50–100K connections a machine |
| Operations | A typist makes ~5 edits a second while active; say 20% typing at once | ~5M ops a second in total, but each document sees only a handful → so route each document to one server |
| Op log | Each op ~100 bytes | Hundreds of TB a year of ops → so snapshot and compact; keep recent ops hot |
| Presence | Cursor moves can be 20+ a second per user | → so throttle to a few a second and never store them |
6. The nouns
- Document: metadata, permissions, current revision.
- Operation: one edit, with the revision it was based on.
- Snapshot: the full content at a revision.
- Presence: a user's cursor and selection, in memory only.
documents doc_id, owner_id, acl, current_rev, latest_snapshot_rev ops (doc_id, rev) → author_id, op: insert "s" at 3 | delete 2 at 7, base_rev, created_at (append-only) snapshots (doc_id, rev) → blob key of the full content presence doc_id → user_id → cursor, selection (memory, never stored)
7. The contract
GET /v1/docs/{id} → {snapshot, revision, ops since snapshot}
WS /v1/docs/{id}/session
client → {op: insert "s" at 3, base_rev: 41}
server → {ack: 42} (to the author)
server → {op: insert "s" at 5, rev: 42} (to everyone else, transformed)
both ↔ {cursor: 17} (presence, not saved)
Data flow: (1) open: load a snapshot and the ops after it → (2) type: apply locally at once, send the op with its base revision → (3) the document server transforms it against ops the client hadn't seen, assigns the next revision, appends it to the log → (4) ack the author, broadcast to everyone else → (5) snapshot every N ops.
8. First working version
WebSockets to a server that saves the document and relays edits as positions.
8.1 Two people type at once
Each edit is applied locally and sent as "insert at position". When edits don't overlap in time this works; when they do, the copies diverge.
8.2 Opening a document
Load the latest saved text.
Parked for later (section 9 fixes each)
- Concurrent edits shift positions, so copies diverge.
- Editors of one document connect to different servers with no single order.
- Saving the whole document on every keystroke, and no history.
- Cursors sent at full rate to everyone.
- What happens when the document's server dies.
9. Make it hold up
9.1 Merging concurrent edits convergence
| Approach | Why | |
|---|---|---|
| ✗ Bad | Lock the document (or paragraph) while someone types, or keep the last write | Locks make collaboration feel broken; last-write-wins silently throws away someone's typing. |
| ~ Good | Send edits as positions ("insert s at 3") and apply them as they arrive | Works while edits don't overlap in time. As soon as two people edit concurrently, positions shift under each other and the copies diverge. Try it in section 13. |
| ✓ Great | Operational transformation (OT) with a central server ordering ops, or a CRDT that gives every character a unique id | OT rewrites each incoming op against concurrent ones ("insert at 3" becomes "insert at 5" because two letters were inserted before it). A CRDT refers to characters by id ("insert after t#3"), so concurrent ops commute and never need rewriting. |
- OT is what Google described for Docs: the server is the single source of order, each client keeps its unacknowledged ops and transforms incoming ones against them. It needs a central server, which a cloud editor has anyway.
- CRDTs (libraries such as Yjs and Automerge) converge without any central order, which suits offline-first and peer-to-peer apps; the cost is metadata per character and tombstones for deleted ones.
- Figma, for comparison, says it uses neither true OT nor true CRDTs: it took ideas from CRDTs and relies on its server as the central authority, which keeps its system simpler.
9.2 One document, one server convergence
Transforming ops needs one place that sees every op of a document in order. Route all sessions of a document to the same document server (consistent hashing on the document id, with a lease so only one server owns it; Module 21). That server loads the latest snapshot plus recent ops into memory, assigns revision numbers, transforms, appends to the op log, then broadcasts. Gateways hold the WebSockets and forward a document's messages to its owner.
9.3 Op log, snapshots and history durable
- An op is acknowledged only after it is durably appended to the log (a replicated database or log, partitioned by document id).
- Every N ops (or minutes), save a full snapshot to blob storage, so opening a document reads one snapshot plus a few ops instead of replaying years.
- Version history comes for free: any past revision is a snapshot plus ops. Old ops can be compacted into coarser history entries.
9.4 When a document server dies durable
Its lease expires; another server takes it and rebuilds the document from the latest snapshot and the log. Clients reconnect, send their base revision, receive the ops they missed, and resend their unacknowledged ops; duplicates are recognised by a client op id, so an op that was logged but not acknowledged isn't applied twice. A fencing token (the lease number) stops a slow old owner from appending after it lost the lease.
9.5 Presence, offline and permissions speed
Cursors and selections are frequent and disposable: broadcast them through the document server, throttle them to a few a second, and never store them. Offline edits queue on the client and are sent as ops with their old base revision when the connection returns; OT or the CRDT merges them like any other concurrent edit. Permissions are checked when the session opens and on every op (a viewer's ops are rejected).
9.6 Very busy documents scale
A doc with hundreds of editors concentrates load on one server. Batch keystrokes for ~50 ms (several times fewer ops, no visible lag), throttle presence harder, and serve pure viewers from a read-only stream fanned out by the gateways instead of the owner.
9.7 The whole design after the deep dives
Two concurrent edits through the final design. Press Play or drag the timeline.
10. Interviewer follow-ups
OT or CRDT: which would you pick?
For a cloud editor with a server anyway, OT (or a server-ordered approach) keeps documents compact. For offline-first or peer-to-peer, a CRDT. Mature libraries matter more than theory: say which you'd use and why.
How does undo work?
Undo is your own last op, inverted and transformed against everything since, so it undoes only your change, not someone else's.
A user is offline for a week.
Their ops are based on an old revision; the server transforms them against a week of ops (or the CRDT merges them). If that's too costly, show a merge view.
Search across documents.
Index snapshots asynchronously into a search engine, with the document's ACL, a few seconds behind.
Comments anchored to text.
Anchors are positions too, so they're transformed like ops (or anchored to CRDT character ids).
Multi-region.
Each document has a home region where its owner runs; users far away have higher latency for others' edits, but their own typing is still instant locally.
Variation: a shared whiteboard
"Design a collaborative whiteboard" (sticky notes, shapes, arrows on an endless canvas) reuses the one-server-per-document design above, but the document is a different shape, and that makes merging much simpler.
- A set of objects, not a sequence of characters. Each shape has an ID and properties (x, y, colour, text). Almost every edit is "set property P of object X". Two people changing different properties never conflict; two changing the same property just need everyone to agree on one winner.
- The server's order decides. The board's server applies changes in the order they arrive and broadcasts that order, so the last value it received wins everywhere. Timestamps or peer IDs are only needed if replicas merge without a server (offline or peer-to-peer).
- Text inside a note is still a sequence, so it uses the text-merging method from 9.1. Only there.
- Stacking order uses a sortable key between neighbours (between "a" and "b" is "an"), so "bring to front" changes one object, not every object's position number.
- Dragging and cursors are not saved. A drag sends dozens of positions a second; send them over the live channel, throttled, and save only where the shape ends up. Cursors are never saved.
- Big boards load by area. A board with 50,000 objects keeps a spatial index (Design D14), and the client loads what's on screen first.
- Undo is per person: it undoes your own last change, as a new change, so it never removes someone else's work.
Read more: Figma's posts on how its multiplayer editing works and ordering objects with sortable keys.
11. When things break
| Failure | What users see | How we detect it | What limits the damage |
|---|---|---|---|
| Document server dies | A second's pause | Lease expiry | New owner rebuilds; clients resend |
| Gateway dies | Reconnect | Socket drop | Client reconnects with its base revision |
| Op log slow | Edits wait for acks | Append latency | Local typing stays instant; acks queue |
| Divergence bug | Different text on two screens | Periodic checksum of copies | Client reloads from the server's snapshot |
12. Wrap-up and the clock
Trade-offs we made:
- OT vs CRDT: OT keeps documents compact but needs a central server and is notoriously hard to get right for rich text; CRDTs work offline and peer-to-peer but carry per-character metadata and need garbage collection.
- One owner per document: a single order, at the cost of failover logic.
- Granularity: every keystroke is the most live; batching for ~50 ms cuts ops by several times.
- Presence never stored.
What we'd watch:
- Op round-trip p99.
- Divergence checksum failures.
- Ops per document (hot docs).
- Snapshot age.
- Reconnect rate.
At 10×:
- More gateways.
- More document servers.
- More aggressive batching and viewer streams for hot docs.
A 45-minute interview
| 0–4 | Frame the problem (editors per doc, offline, history) |
| 4–9 | Must do, how well, numbers |
| 9–12 | Nouns, contract, data flow |
| 12–18 | First working version, parked list |
| 18–40 | Deep dives: merging (9.1), one owner (9.2), op log (9.3), failover (9.4) |
| 40–45 | Follow-ups and wrap-up |
A 60-minute interview
| 0–5 | Frame the problem |
| 5–11 | Must do, how well, numbers |
| 11–15 | Nouns, contract, data flow |
| 15–23 | First working version |
| 23–50 | Deep dives 9.1–9.6 and the final picture |
| 50–60 | Follow-ups: OT vs CRDT, undo, offline; wrap-up |
| Level | What a strong answer shows |
|---|---|
| Mid-level | WebSockets, a server that broadcasts edits, a database for documents, and awareness that concurrent edits conflict. |
| Senior | OT or CRDT explained with an example, one server per document via consistent hashing, revision numbers and acks, op log plus snapshots, presence kept in memory. |
| Staff+ | OT vs CRDT trade-offs for the product (offline, rich text, scale), server failover and leases, compaction and history, very large and very busy documents, permissions on every op. |
Common mistakes:
- Locks or last-write-wins.
- Raw positions without transformation.
- Editors of one doc on different servers.
- Saving full text per keystroke.
- Storing cursors.
13. Try it, check yourself, read more
Alice and Bob start with the same text and make one edit each at the same moment. Each edit is applied locally at once (blue for Alice, green for Bob), then travels through the server to the other person.
Why does sending raw positions ("insert at 3") break with two editors?
How does a CRDT avoid transforming operations?
Why route all editors of a document to one server?
What does opening a years-old document read?
| Resource | Why |
|---|---|
| What's different about the new Google Docs: conflict resolution (Google) | OT explained with examples by the team that built it. |
| How Figma's multiplayer technology works (Figma) | A central-server design inspired by CRDTs. |
| crdt.tech | Papers, talks and libraries about CRDTs. |