HardProblem 24 of 36 in the system design series
Design Google Docs: Real-Time Collaborative Editing with OT

A collaborative document editor lets several people type into the same file at once, each seeing the others' words and cursors appear within a fraction of a second. It feels like everyone shares one page, but in reality each participant has a separate copy in their browser, and those copies are constantly diverging and being pulled back together over an unreliable network.
Interviewers favour this problem because it cannot be solved with standard CRUD thinking. Saving the whole document on every keystroke loses work whenever two people edit concurrently, and simple locking destroys the real-time feel. The candidate has to reason about concurrent operations, ordering, and how to guarantee that every replica ends up byte-for-byte identical.
The central question is what to do when two edits collide: one person inserts text at the very spot another just deleted. How the system resolves that, and where it resolves it, drives the rest of the architecture, from connection routing to storage layout.
This is an interview practice design for a Google Docs-like service, built from first principles. It is not a description of how Google Docs is actually built.
Functional Requirements
Users must be able to create a document and reopen it later from a laptop, phone or another browser. The headline feature is simultaneous editing: several people change the same document at once and watch each other's changes arrive in real time without refreshing.
Presence makes collaboration feel human. Each participant sees who else has the file open and a coloured cursor marking where they are typing. Version history, the ability to view the document as it stood at an earlier moment, is a nice extra that the design will provide almost for free. Comments, permission sharing and detailed rich-text formatting are left out so the discussion stays on the concurrency problem.
Create & open docs
Create a document, and open it later from any device.
create · open
Edit together
Many users edit at once and see changes in real time.
concurrent edits
Cursors & presence
See who is in the doc and where they are typing.
live cursors
Version history
Go back and see the document at an earlier point.
optional
- Out of scope: comments, sharing permissions, rich formatting details
Non-Functional Requirements
Two kinds of latency matter. Local keystrokes must render immediately, with no round trip, otherwise the editor feels broken. Remote collaborators should see those keystrokes within about 200 ms. Convergence is the correctness requirement: once everyone pauses, all copies must be identical. That is eventual consistency, but with a strict guarantee that the end state matches everywhere.
Durability means an edit the server has acknowledged survives any crash. Scale has an unusual shape. There are millions of documents, yet each one has a small editing group, capped at about 100 people. That cap is not arbitrary; it is what makes a single-owner-per-document design feasible, because no single document ever needs more than one machine's worth of throughput.
Low latency
Your own typing is instant; others see it fast.
< 200 ms to others
Convergence
Every editor ends up with exactly the same document.
eventual consistency
Durability
An edit that was accepted is never lost.
no lost edits
Scale
Millions of documents, a small group of editors each.
≤ 100 editors / doc
Scale Estimation
Assume 1 billion stored documents averaging 50 KB, roughly 50 TB of content. At peak, assume around 2 million people editing concurrently. Clients batch keystrokes, so each editor sends about one operation per second, giving roughly 2 million operations per second across the system.
If each operation is about 100 bytes, the operation log grows at around 200 MB per second, so it must be compacted rather than kept forever in raw form. Yet a single document sees at most a few dozen operations per second even when busy. Huge totals combined with tiny per-document load point straight to partitioning everything by document ID, so that each document's traffic, state and storage stay together.
1billion
Documents stored
≈ 50 TB at 50 KB each
2million
Concurrent editors
at peak
2million
Edit ops per second
≈ 1 batched op / editor / s
200MB / s
Op log growth
100 B per op → compact it
Huge in total, tiny per document → partition everything by doc ID
Core Entities & API
Four entities define the model. A Document holds metadata. An Operation is one small edit, such as inserting or deleting characters. A Snapshot is a complete saved copy at some version. Presence records who is connected and where their cursor sits.
Ordinary actions use REST: POST /docs with a title returns a doc_id, and GET /docs/{doc_id} returns metadata, the latest content and its version number. Editing runs over a WebSocket at /docs/{doc_id}/edit. The client sends each operation along with the base_version it was applied on top of, plus cursor movements. The server sends back acknowledgements, operations from other editors and their cursor positions. That base version is what lets the server detect and resolve concurrent edits.
Core entities
DocumentOperationSnapshotPresence
API
- POST /docs { title }→ { doc_id }
- GET /docs/{doc_id}→ { metadata, snapshot, version }
- WS /docs/{doc_id}/edit send { op, base_version } · cursor→ acks · others' ops · cursors
High-Level Design
Clients reach the system through a gateway that terminates both REST and WebSocket traffic. Routine requests such as creating, listing or sharing files go to a stateless document service. Live editing is different: the gateway consults a coordinator, for example ZooKeeper, to learn which collaboration server currently owns the document, and routes every editor of that file to the same machine.
That owner serialises incoming edits, transforms them where they conflict, appends each to an append-only operation log, and fans it out to the other connected editors. A snapshot worker trails the log and, every few hundred operations, persists a complete copy into blob storage such as S3. Opening a document loads the newest snapshot and replays the short tail of operations after it. Having one owner per document gives a single authority on edit order, which is what makes convergence tractable.
One server per document → one place decides the order of edits
Deep Dive 1 — OT vs CRDT
Locking the document or letting the last save win is easy to build, but it silently discards someone's work and is not true co-editing. CRDTs take a different route: each character carries a unique identifier, so replicas can merge in any order without a central authority. That suits offline-first and peer-to-peer apps, but every character drags extra metadata, and deletions leave tombstones that accumulate over time.
Operational transformation with a central server is our choice. Consider two concurrent edits: Alice inserts "X" at index 5 while Bob removes the character at index 2. If the server applies Bob's deletion first, everything after index 2 moves left by one, so Alice's insert must be rewritten as index 4. That rewrite is the transform. Each client submits operations with their base version; the server transforms them past anything newer and assigns the next version.
Libraries such as Yjs and Automerge make CRDTs a legitimate answer too. What interviewers want is the reasoning: since this design already routes each document through one owner, OT keeps both operations and stored data compact.
Lock / last write wins
- + Trivial to build
- − Edits get lost
- − No real co-editing
CRDT
- + No central ordering
- + Great for offline, P2P
- − Extra metadata per char
- − Tombstones grow
OT + central serverChosen
- + Small, simple operations
- + Proven in large production editors
- − Transform logic is tricky
- − Needs one server per doc
- A: insert "X" at 5 · B: delete at 2 first → A becomes insert at 4
- Client sends op + base version; server transforms it past newer ops
- CRDT (Yjs, Automerge) is also a valid answer — say why you chose
Deep Dive 2 — Life of an Edit
Follow a single keystroke. The client applies it to its own copy at once so the typist never waits, then sends the operation and its base version over the WebSocket. On arrival, the collaboration server rewrites it to account for operations accepted after that base version and stamps it with the next version number.
Before acknowledging, the server appends the operation to the log, so an acknowledged edit is always durable. It then forwards the operation to every other connected editor, and each of them transforms it against their own not-yet-sent edits before applying it. When the original sender receives its ack, it releases the next operation from its local queue. Allowing only one unacknowledged operation per client at a time keeps transformation logic manageable, because the server never has to reconcile a long chain of speculative edits from one source.
- 1Apply locallyThe user's screen updates instantly, no waiting
- 2Send with versionOp + base version over the WebSocket
- 3Server transformsShift past newer ops; assign the next version
- 4Append to the logPersist the op before acknowledging it
- 5BroadcastSend the op to every other editor of the doc
- 6Ack & rebaseClient transforms its pending ops on the ack
- One op in flight per client; queue the rest → transforms stay simple
Deep Dive 3 — Storage & Presence
The operation log is the system of record: append-only and keyed by document ID plus version. A wide-column store like Cassandra handles the steady append rate well, and because the key starts with the document ID, each document's history sits in one partition. Snapshots exist purely for speed, so opening a file reads one snapshot and a handful of recent operations instead of replaying years of edits.
Cursors and presence never touch disk. They travel over the same WebSocket and live in the owner's memory; a lost cursor update simply gets replaced by the next one. If a collaboration server fails, the coordinator reassigns its documents. The new owner loads the latest snapshot, replays the log tail, and clients reconnect and resend anything that was never acknowledged. Version history falls out naturally, since older snapshots plus the log can reconstruct any past state.
The operation log is the source of truth
Append-only, keyed by doc ID and version; every accepted edit lands here.
Snapshots keep loading fast
Open = latest snapshot + a short tail of ops, not the whole history.
Cursors and presence stay in memory
Sent over the same WebSocket, never stored; losing one is harmless.
Server failover
The coordinator moves the doc; the new owner rebuilds it from snapshot + log.
Version history for free
Old snapshots plus the log let us show any earlier version.
Trade-offs
OT keeps operations small and relies on a central server, whereas CRDTs enable offline and peer-to-peer editing at the price of heavier metadata. Pinning each document to one server makes ordering straightforward, but an extremely busy document cannot spread beyond one machine, which is why the editor cap exists.
Snapshotting more often makes documents open faster while using more storage and background compute. Keeping presence only in memory is cheap and fast, though cursors can briefly jump after a reconnect. The overall recipe: let one owner decide the order of edits, persist every operation before acknowledging it, and lean on periodic snapshots so loading stays quick.
OT vs CRDT
Small data and a central server — or offline-first with heavier metadata.
One server per document
Simple ordering — but one very hot doc is limited to one machine.
Snapshot frequency
Faster loads — but more storage and more background work.
Presence in memory only
Cheap and fast — but cursors may jump briefly after a reconnect.
Order edits in one place. Log every op. Snapshot for speed.
Key takeaways
- Totals are huge but each document is small, so partition state, connections and storage by document ID.
- One collaboration server per document gives a single order of edits and makes convergence achievable.
- OT transforms concurrent operations by version; CRDTs are a valid alternative if you explain the trade-off.
- Write each operation to the log before acknowledging it, and keep one op in flight per client.
- Snapshots plus a short log tail make loading fast and give version history; presence stays in memory.
Interview questions
How does real-time collaborative editing like Google Docs work?
Each user edits a local copy and sends small operations, such as insert or delete at a position, to a server that owns the document. The server orders operations, transforms any that were made concurrently so their positions still make sense, stores them in a log, and broadcasts them to other editors. Every copy applies the same ordered operations and converges to identical text.
What is the difference between OT and CRDT for collaborative editing?
Operational transformation adjusts concurrent operations against each other, usually through a central server that defines a single order. CRDTs give every element a unique ID so edits commute and can merge in any order without a coordinator. OT keeps data compact but needs a central authority; CRDTs suit offline and peer-to-peer use but carry extra metadata and tombstones.
Why route every editor of a document to the same server?
A single owner provides one authoritative order for edits, which makes operational transformation simple and guarantees convergence. It also keeps the document's live state and connected clients in one place, so broadcasting is a local operation. Because each document has at most around 100 editors, one machine has more than enough capacity, and a coordinator reassigns ownership if that server fails.
How do you store document history efficiently in a collaborative editor?
Keep an append-only operation log keyed by document ID and version as the source of truth, and periodically write full snapshots to blob storage. Loading a document reads the latest snapshot plus the few operations after it. Older snapshots combined with the log let you rebuild any past version, which gives version history with little extra work.
What happens when a collaboration server crashes mid-edit?
The coordinator assigns the document to a healthy server, which loads the newest snapshot and replays the log tail to rebuild current state. Clients reconnect, learn the latest version, and resend any operations that were never acknowledged. Because the server only acknowledges an edit after writing it to the log, no acknowledged work is lost, and resent edits are transformed normally.
Keep practising
- Learn the building blocks in the free system design course.
- Rehearse answers with system design interview questions.
- See which open jobs fit your skills: upload your resume.