System Design · step by step

Design Google Docs

Step 1 / 9
The numbers to beatconvergentend stateintentpreservedany ordersame result

In the interview room

How you’d open this design in an interview

Before any boxes: agree what it must do, pin the qualities that shape everything, then build — naming each trade-off as you make it. The walkthrough above is that exact order.

Functional requirements

What it must do — agree on these before drawing a single box.

  • Co-edit: many people edit one doc at once and all see the same text.
  • Send ops: each change is a tiny insert/delete at a position, not a whole-doc save.
  • Broadcast: an accepted op appears on every other editor’s screen in well under a second.
  • Persist: never lose a keystroke, and still load a long doc fast.
  • Presence & permissions: show live cursors, and let only editors edit.

Non-functional requirements

The qualities that shape the whole design — each one names the mechanism that buys it.

Every copy ends identical
OT transforms an incoming op against ops it didn’t see, or CRDTs give each char a stable id — both guarantee convergence: same ops, any order, same doc.
Sub-second live updates
Push each accepted op over WebSockets to every other editor, with pub/sub relaying across servers — real-time is push, not poll.
Never lose work, still load fast
Append every op to a durable op log (the source of truth) and write periodic snapshots, so recovery loads the latest snapshot and replays only the ops since.
A cursor flood can’t risk the doc
Run presence — who’s here, each cursor/selection — on a separate ephemeral path that is broadcast constantly but never persisted to the op log.
Edit offline, merge cleanly on reconnect
Each client tracks its last-seen version; on reconnect it sends buffered ops and pulls ops since, and OT/CRDT merges the two streams — offline is delayed real-time.
Only editors edit, and it scales
Check permissions on join and on every op server-side, and shard by document so all editors of a doc share one authoritative server — no consensus per keystroke.

The trade-offs you say out loud

Senior signal isn’t the boxes — it’s naming what you gave up and why it was the right price.

Tiny insert/delete opsover whole-document saves

Whole-doc saves clobber each other — last write wins — and throw away what changed, so intent can’t be merged. Small ops preserve intent, are cheap to order and merge, and the op stream becomes the source of truth.

OT / CRDTsover a global edit lock

A lock is just "file is locked" again — it kills real-time collaboration. Transforming or merging concurrent ops lets everyone type at once and still converge: same ops, any order, same document.

WebSocket pushover polling for changes

Polling every second is laggy and mostly returns nothing; collaboration needs sub-second updates. The server pushes each accepted op the moment it lands, with pub/sub relaying across servers.

Op log + periodic snapshotsover saving the whole doc per keystroke

Rewriting the full doc every keystroke is massive write amplification and still loses concurrent intent. An append-only log is the truth; snapshots checkpoint it so loads replay only the ops since.

Ephemeral presence pathover cursor moves in the op log

Persisting a flood of high-frequency cursor updates would swamp the durable log with data nobody replays. Presence is throwaway — broadcast but never stored — so a cursor storm can’t risk the document.

What this teaches

Learn system design by building a real-time collaborative editor like Google Docs step by step. An interactive guide covering the document model, conflict resolution with OT and CRDTs, op broadcast over WebSockets, persistence with an op log and snapshots, presence, offline sync, and access control.

Key takeaways

  • Edits are tiny operations on an ordered model, not whole-document saves.
  • OT or CRDTs resolve concurrent edits so every copy converges.
  • Accepted ops broadcast over WebSockets (and pub/sub) for live collaboration.
  • A durable op log plus periodic snapshots give recovery, history and fast loads.
  • Presence (cursors, who’s here) runs on a separate, ephemeral path.
  • Versioned op logs make offline editing and reconnection sync just work.
  • Server-side permissions and per-document sharding handle security and scale.

Concepts covered

  • What is a collaborative editor?
  • Edits are operations
  • Resolving concurrent edits
  • Broadcast to everyone
  • Persist: op log + snapshots
  • Presence & cursors
  • Offline & reconnection
  • The sharp edges

Design Google Docs (Collaborative Editor) — read the full walkthrough as text

the same steps, decisions & trade-offs, for reading, reference & search

The big idea

What is a collaborative editor?

Two people open the same document and type at the same time. Each sees their own edits instantly and the other’s appear live, and somehow both end up looking at the exact same text. No “file is locked”, no overwriting each other’s work.

Stop syncing whole documents and start syncing tiny operations — “insert ‘x’ at position 5”. A server orders everyone’s ops and resolves conflicts so every copy converges to the same state. Get that one idea right and the rest is plumbing.

How to read this: We add one piece at a time, problem then fix, and the diagram grows. Hit Begin.

Step 1 · The model

Edits are operations

If a save means “upload the whole document”, two people’s saves clobber each other and the last one wins. You can’t merge intent from snapshots — by then the information about what changed is gone.

Design decision: Two people edit the same doc simultaneously. What does the client send on each change?

The call: Tiny operations: insert/delete at a position. — Model the doc as an ordered sequence and stream small ops applied locally for instant feedback. Ops preserve intent and are cheap to send, order and merge — the op stream is the source of truth.

Model the doc as an ordered sequence and every edit as a small op (insert/delete at a position). The editor applies ops locally for instant feedback and streams them to an Edit Server over a persistent connection.

Ops, not snapshots: Tiny operations preserve intent and are cheap to send, order, and merge. The document is just the result of applying the op stream — the stream is the source of truth.

Step 2 · The hard part

Resolving concurrent edits

A and B both edit from the same starting text. A inserts at position 2; B deletes at position 1. By the time A’s op reaches B, B’s positions have shifted — apply A’s op blindly and the docs diverge. Forever.

Design decision: A inserts at position 2; B deletes at position 1. By the time A’s op reaches B, positions have shifted. How do they stay identical?

The call: OT or CRDTs: transform/merge concurrent ops to converge. — OT rewrites an incoming op against ops it didn’t see (shift A’s "insert at 2" past B’s delete); CRDTs give each char a stable orderable id so edits merge commutatively. Both guarantee convergence — same ops, any order, same doc.

Use Operational Transformation (OT) or CRDTs. OT rewrites an incoming op against ops it didn’t see (shift A’s “insert at 2” to account for B’s delete). CRDTs give each character a stable, orderable ID so concurrent edits merge commutatively — no transform needed.

OT vs CRDT: OT centralizes ordering on a server and keeps ops small; CRDTs need no central transform but carry more per-character metadata. Both guarantee convergence: same ops, any order, same document.

Step 3 · Make it live

Broadcast to everyone

A’s edit is resolved on the server — but B is still staring at stale text. Collaboration only feels real if the other person’s changes show up in well under a second.

Design decision: A’s edit is resolved on the server, but B still sees stale text. How does B’s screen update live?

The call: Broadcast each accepted op over WebSockets (pub/sub across servers). — After accepting an op the server pushes it to every other editor’s open socket; when editors are on different servers, pub/sub relays it to whoever holds each connection. Real-time = push, not poll.

After accepting an op, the server broadcasts it to every other editor of the doc over their WebSockets. When editors sit on different servers, a pub/sub layer relays the op to whichever server holds each connection.

Persistent connections: Real-time means push, not poll. WebSockets keep a channel open both ways; a session registry (and pub/sub across servers) ensures an op reaches exactly the people editing that doc.

Step 4 · Never lose a keystroke

Persist: op log + snapshots

The live document lives in server memory. A crash, and hours of collaborative work vanish. But you also can’t rebuild a long doc by replaying millions of ops on every open.

Design decision: The live doc is in server memory and a long doc has millions of ops. How do you avoid losing work AND loading fast?

The call: Append ops to a durable log + periodic snapshots. — The append-only op log is the truth (nothing lost on a crash); periodic snapshots let you load the latest checkpoint and replay only ops since — fast recovery, full history, free version control.

Append every accepted op to a durable Op Log, and periodically write a Snapshot of the full state. To load a doc, take the latest snapshot and replay only the ops since — fast recovery, full history, and free version control.

Log + checkpoint: The append-only log is the truth; snapshots are a performance cache of it. This same pattern (event log + periodic materialization) shows up in databases and stream processors everywhere.

Step 5 · Who else is here?

Presence & cursors

Collaboration feels lonely — and people collide — if you can’t see where others are. Those colored cursors and name flags are core to the experience, but they’re high-frequency and disposable.

Design decision: Colored cursors and "3 people viewing" update constantly and are disposable. How do you handle them?

The call: A separate ephemeral path: broadcast presence, never persist it. — Track who’s connected and each cursor/selection, broadcast constantly, but never store it — if everyone leaves, presence evaporates while the doc remains. A cursor flood can’t risk the op log.

Track Presence separately from the document: who’s connected, and each person’s cursor/selection. Broadcast these constantly but never persist them — if everyone leaves, presence simply evaporates while the doc remains.

Ephemeral vs durable: Cursors and “3 people viewing” are throwaway state; the document is sacred. Keeping them on separate paths means a flood of cursor updates never risks or slows the durable op log.

Step 6 · The flaky network

Offline & reconnection

Connections drop in tunnels and on planes. A user keeps typing offline, then reconnects — meanwhile others changed the doc. Their queued edits must merge cleanly, not overwrite or get lost.

Design decision: A user edits offline in a tunnel, then reconnects — others changed the doc meanwhile. How do the edits merge?

The call: Client tracks its last version; sends buffered ops + pulls ops since. — On reconnect the client sends queued ops and pulls everything from the op log after its last-seen version; OT/CRDT merges the two streams. Offline editing is just real-time editing with a longer delay — same convergence machinery.

Each client tracks the last version it saw. On reconnect it sends its buffered ops and pulls everything from the Op Log since that version; OT/CRDT merges the two streams. The same convergence machinery that powers live editing powers catch-up.

Versions make sync trivial: Because the doc is an ordered op log, “sync” is just “give me ops after version N and transform mine against them”. Offline editing is only real-time editing with a longer delay.

Step 7 · Permissions & scale

The sharp edges

Not everyone may edit — some can only view or comment — and a viewer must not be able to sneak in an op. And a server can only hold so many live docs and connections before it tips over.

Check Permissions on join and on every op, so the server rejects edits from non-editors. Scale by sharding by document: all editors of one doc land on the same server (consistent hashing), keeping its in-memory state authoritative; pub/sub bridges across shards.

One doc, one owner server: Pinning a document to a single authoritative server avoids distributed consensus on every keystroke. Permissions enforced server-side mean the client is never trusted with what it’s allowed to do.

You did it

You just designed Google Docs.

  • Edits are tiny operations on an ordered model, not whole-document saves.
  • OT or CRDTs resolve concurrent edits so every copy converges.
  • Accepted ops broadcast over WebSockets (and pub/sub) for live collaboration.
  • A durable op log plus periodic snapshots give recovery, history and fast loads.
  • Presence (cursors, who’s here) runs on a separate, ephemeral path.
  • Versioned op logs make offline editing and reconnection sync just work.
  • Server-side permissions and per-document sharding handle security and scale.
built to be co-edited, not memorized — make the calls, cut the broadcast, run the gauntlet.
Finished this one? 0 / 62 System Designs done

Explore the topic

See this alongside everything else on the same subject — handbooks, system designs, challenges and tools, in one place.