Skip to lesson
‹

Courses / System Design Interview / Chapter 34

Module 3 · Design Real Products · Chapter 34 of 39

Design Collaborative Document Editing

20 sections · ~40 min✓ Fact-checked

Builds on Databases: Replication, Partitioning and Choosing a Store, Consensus, Leader Election and Coordination, Designing a Key-Value Store, Blob Stores, Publish-Subscribe and Design a Group Chat App. Next up: Design a Notification System.

Part 1 · Requirements and Scale

The brief

The night before the college fest, the organising team's plan lives in a document file that hops around a WhatsApp group: plan_final.docx, plan_final_v2.docx, plan_final_ravi.docx. At 10 pm two copies disagree. One tells volunteers to meet at 5, the other at 6, and Meera's whole stage schedule exists only in a copy nobody else opened. A shared document ends this. All twelve volunteers type into one plan at the same time, see each other's cursors, and every screen ends up showing the same text. Making that hold when two people type in the same spot at the same moment is what this chapter designs.

The product: a collaborative document editor. Several people edit one document at once and see each other's changes and cursors live. Plan for about 10 million daily users. The fest team's plan, document d_91, edited by Asha on a laptop and Ravi on a phone, is the example we follow throughout.

Advanced

This is one of the harder chapters. On a first pass through the course, read the brief, the high-level design and the 45-minute answer, then come back for the deep dives once the other products feel easy.

What we are building

The fest plan shows the whole product in miniature. Collaborative editing means many people changing one shared document at the same time, with every copy ending up identical. Sending keystrokes around is the easy part. The hard part is deciding what the document should say when two people change the same spot at once. Here is what the fest team, and 10 million people like them, need.

Requirement
FunctionalCreate, open and share a document with view or edit rights
FunctionalSeveral people edit at once and see each other's changes live
FunctionalSee who else is in the document and where their cursors are
FunctionalBrowse version history and restore an older version
FunctionalKeep editing offline and merge on reconnect
Non-functionalYour own typing appears instantly; other people's edits within about 200 ms
Non-functionalAll copies converge to the same text, and an acknowledged edit is never lost
Non-functionalMost documents have 1 to 5 editors; support up to about 100 at once
Non-functionalMost of a document's editors are in one country; a few may join from another continent

Two rows shape everything that follows: every copy must end up identical, and an acknowledged edit, one the server has confirmed as saved, must never be lost. The first rules out simply keeping the last save; the second decides when the server may say "saved".

Out of scope today

Rich formatting, images and tables inside documents, comments, and search across documents. This chapter designs plain text edited by many people at once; the Going further part shows what rich text changes.

How big it gets

Before choosing machines, find out how much editing there really is, and how it spreads across documents. Assume 10 million daily users. At the evening peak about 10% of them have a document open: 1 million live connections, most of them reading or thinking rather than typing.

Try it first: about 20% of those 1 million people are typing at any moment, and each sends about 2 edit batches a second (keystrokes are grouped every 100 ms, and people pause). A busy document has about 2 typists. How many edits a second reach the whole system, and how many reach one document?
  • 200,000 typists × 2 batches ≈ 400,000 edits a second across the system.
  • 200,000 typists ÷ 2 per document ≈ 100,000 documents being edited, so each one sees about 4 edits a second.
  • Huge in total, tiny per document.

The total is large, but each document is small. Four edits a second is nothing for one process, so every busy document can have a single owner that puts its edits in order. Scale comes from spreading 100,000 busy documents across many servers, not from splitting one document.

QuantityWorkingResult
Collaboration servers1 M connections ÷ about 20,000 each (an assumption)≈ 50 servers, plus spares
Edit log writes400,000 /s × about 100 B≈ 40 MB/s at peak
A typical document10 pages ≈ 5,000 words≈ 30 KB of text
Snapshot writes400,000 edits/s ÷ one snapshot per 500 edits≈ 800 per second, about 24 MB/s

Why 20,000 connections and not the 100,000 mostly idle sockets a chat server holds (the group chat chapter)? These connections are busy with keystrokes and cursor moves, and the server also holds documents in memory and transforms edits. As for latency, the 200 ms target splits into up to 100 ms waiting for a batch, 20 to 50 ms from Asha to the server, a few milliseconds for the log write, and 20 to 50 ms on to Ravi.

Part 2 · API, Data and the Big Picture

The API

Those numbers say each edit must be small and must go to the one server that orders its document. Opening a document is an ordinary HTTP call. Live editing uses a WebSocket, a long-lived two-way connection (the group chat chapter covers how servers hold many of them), because both sides push small messages at any moment.

http
GET /v1/docs/d_91
200 OK   { "doc_id": "d_91", "version": 1840, "text": "Meet at 5 ..." }

WS /v1/docs/d_91/live
client → server  { "type": "op", "base": 1840, "seq": 17, "op": { "insert": " pm", "at": 9 } }
server → client  { "type": "ack", "seq": 17, "version": 1841 }
server → client  { "type": "op", "version": 1842, "author": "u_7", "op": { "delete": 4, "at": 120 } }
server → client  { "type": "presence", "user": "u_7", "cursor": 120 }

GET  /v1/docs/d_91/history?before=1800
POST /v1/docs/d_91/restore   { "version": 1500 }

`base` is the version the edit was made on; `seq` lets the client match each ack to its own edit.

  • Edits are operations, not whole documents. "Insert ' pm' at position 9" is a few bytes; sending the 30 KB plan on every keystroke would multiply the traffic about 300 times.
  • Every acknowledged edit gets the next version number, assigned by the server: 1841, 1842, and so on. The version is how both sides know what an edit was based on.
  • Restore is a new edit, not a rewrite of history. It replaces the text with the old version's text as the next version, so every copy moves forward together.

The data model

Those version numbers need a durable home. Two kinds of data matter. An append-only edit log is the source of truth: every acknowledged operation, in order. Snapshots are copies of the whole text at some version, so nobody replays a year of edits to open the plan.

documentsdoc_id (pk)titlecreated_bylatest_versionlatest_snapshot_versioncreated_at
doc_opsdoc_id (pk)version (pk)author_idclient_idclient_seqopcreated_at
snapshotsdoc_id (pk)version (pk)blob_keycreated_at
permissionsdoc_id (pk)user_id (pk)role: view or edit

Partition everything by doc_id (the databases chapter covers partitioning). All of one document's operations then live together, ordered by version, so opening one is a single blob read for the latest snapshot plus a single range read for the operations after it. The primary key (doc_id, version) does a second job. Every append is an insert-if-absent, which relational databases and many key-value stores support, so two writers can never both store version 1841. The high-level design uses that to stop a stale owner.

DataWhere it livesWhy there
Edit loga partitioned, replicated database with insert-if-absent, keyed by (doc_id, version)ordered appends, range reads, durability, fencing
Snapshotsthe blob store, one file per snapshotcheap, written once, always read whole
Documents and permissionsa relational databasesmall rows; joins for sharing screens
Presence and cursorsthe owner server's memory onlychanges many times a second, useless a minute later

Permissions are checked on connect and on change. The owner checks the user's role when the WebSocket opens and rejects edits from viewers. When someone's access is removed, the owner closes their connection at once instead of waiting for a reconnect.

The high-level design

With operations, versions and a log in place, the question is who puts each document's edits in order. The answer is one owner per document. The gateway routes every WebSocket for d_91 to the collaboration server that owns it. That server keeps the plan in memory, orders each incoming edit, writes it to the log, and only then acknowledges it and forwards it to the other editors.

Animated sceneTwo editors, one owner0:21

Watch for the owner's order: log the edit, then ack it, then forward it

Read it as steps

The owner of the fest plan logs, acks and forwards every edit. When it crashes, a new owner rebuilds the plan from the log and fences the old one out.

The order is the lesson: log, then acknowledge, then forward. Ack before logging, and a crash leaves Asha believing a lost edit was saved. Forward before logging, and Ravi keeps an edit a new owner never heard of. Because the log holds every acknowledged edit, any server can rebuild the plan exactly.

  • Finding the owner. The gateway hashes doc_id onto a ring of live collaboration servers, the consistent hashing idea from the key-value store chapter, so adding a server moves only a small share of documents.
  • Loading and unloading. The first editor to open a document makes its owner read the latest snapshot and later operations into memory; a few idle minutes after the last editor leaves, the owner drops it.
  • Exactly one owner. Each server holds a lease from a coordination service (the coordination chapter) and renews it every few seconds. A new owner's first append is a takeover record, an entry with no text change, in the next version slot. An old owner that was only paused then fails its next insert-if-absent and steps down: the version number plays the fencing token.

Part 3 · Deep Dives

Two people typing at once

Core deep dive

This is the problem that makes the product hard: a chat app only appends messages, but here two people can change the same spot at once.

The owner puts edits in order, but order alone does not settle what the text should say. Edits name positions: "insert ' pm' at position 9" means after the ninth character. Think of directions that say "the ninth house on the street". If six new houses go up at the start of the street, the same house is now the fifteenth, and the old directions lead to the wrong door.

Say the plan's first line reads Meet at 5. At the same moment, Asha types Let's at the start and Ravi types pm at the end, position 9. Each app applies its own edit at once, called local echo, so typing never lags, then sends it to the other through the server.

Asha's screenRavi's screen
StartMeet at 5Meet at 5
After their own editLet's Meet at 5Meet at 5 pm
After the other's edit, as sentLet's Mee pmt at 5Let's Meet at 5 pm
Resultgarbled, and differentcorrect

The copies have diverged: the two now see different text and neither knows it. Ravi's position 9 was right for the text he saw, but Asha's copy had six more characters in front. His edit must be transformed to 9 + 6 = 15 before Asha applies it.

The goal has a name

Convergence: once everyone has received every edit, all copies are identical. A good system also tries to keep intent: Ravi wanted ' pm' right after the 5, so it should land right after the 5 on every screen. Who shifts which edit, and against what, is the next page's question.

Operational transformation

Operational transformation (OT) rewrites a late edit so that it means, on the current text, what it meant to its author. The owner decides the order: when an edit arrives with a base version older than the latest, the owner transforms it against every edit applied since that base, then logs and forwards the result.

Predict before you watch: the line reads Meet at 5 at version 1. At the same moment, Asha inserts ' pm' at position 9 and Ravi inserts ' sharp' at position 9. Asha's edit reaches the server first. What does the server store for Ravi's edit, and what does every screen end with?
  • A position alone cannot say whose text goes first, so every copy needs the same tie-break: here, the edit the server ordered first goes on the left.
  • The server stores Ravi's insert at 9 + 3 = 12, just after ' pm'.
  • Every screen ends with Meet at 5 pm sharp.
Animated sceneSame spot, same moment0:15

Watch for the tie-break: both insert at position 9 of "Meet at 5" at once

Read it as steps

Two inserts at the same spot: the server orders Asha's first and shifts Ravi's past it, and Ravi's app shifts his waiting edit the same way.

Both sides transform. The owner transforms a late edit against everything logged since its base. Each app transforms incoming edits against its own unacked ones, and shifts those too: Ravi's waiting ' sharp' moved from 9 to 12 on his own screen. To keep this simple, an app has at most one batch in flight and buffers new keystrokes until the ack arrives.

The work is small. At about 4 edits a second per document and 100 to 200 ms for a round trip to the owner, an edit usually overlaps zero or one other, so it needs one transform or none, each a few comparisons. The single owner keeps it this simple: OT with no central order is hard to get right, and some published peer-to-peer designs were later shown to fail in rare cases.

A transform by hand

The scene used one rule. Here is the full set for plain text, then one transform to do yourself. Each row says what happens to edit B when edit A, made on the same version, was applied first.

Edit A, applied firstWhat happens to edit B
Insert of n characters before B's positionB moves right by n
Insert at the same position as B's inserttie-break: the edit ordered first stays left, so B moves right by n
Insert inside the range B deletesB is split around A's text, so A's text survives
Delete of m characters before B's positionB moves left by m
Delete of a range that contains B's insert positionB moves to where the deleted range started
Delete that overlaps B's deleteB deletes only the characters still there
Any edit after B's positionno change

Check any rule with one test: applying A and then the transformed B must give the same text as applying B and then the transformed A. When both orders agree, every copy converges whichever edit it receives first.

Try it by hand: the line reads Meet at 5 at version 1. Asha deletes Meet (5 characters at position 0) while Ravi inserts ' pm' at position 9. The owner orders Asha's delete first. What does it store for Ravi's insert, and what does each screen show?
  • The delete removes 5 characters wholly before position 9, so Ravi's insert moves left by 5: insert ' pm' at 4.
  • The owner's copy is at 5; ' pm' at 4 makes at 5 pm.
  • Ravi's screen shows Meet at 5 pm. The delete lies wholly before his insert, so it applies unchanged and leaves at 5 pm. Both orders agree.
python
def transform_insert(b, a, a_first):
    # b: an insert. a: applied first, made on the same version.
    if a.kind == "insert":
        if a.at < b.at or (a.at == b.at and a_first):
            return Insert(b.at + len(a.text), b.text)
        return b
    if a.at + a.n <= b.at:              # whole deleted range is before b
        return Insert(b.at - a.n, b.text)
    if a.at < b.at:                     # b sat inside the deleted range
        return Insert(a.at, b.text)
    return b                            # deleted range is after b

Four rows of the table as code. The owner and every app run the same function, which is why they agree.

CRDTs: ids instead of positions

OT repairs positions after the fact. A CRDT (conflict-free replicated data type) avoids positions altogether. Instead of "the ninth house on the street", every house gets a permanent number painted on its gate, so new houses never change anyone's address.

In a text CRDT every character gets a permanent unique id: here, the device that typed it plus that device's counter. An insert says "put ' pm' after character a9", not "at position 9". This is the plan's line as a CRDT holds it:

text
id     a1  a2  a3  a4  a5  a6  a7  a8  a9
char   M   e   e   t   _   a   t   _   5

a is Asha's laptop and 1 to 9 its counter; _ marks a space.

Now Asha types ' pm' and Ravi types ' sharp', both after a9, at the same moment. Each new character is anchored to the one before it, so each word is a chain hanging off a9. A device's counter always moves past the highest it has seen, so Asha's chain starts at id a10 and Ravi's at r10. Every copy orders two chains on one anchor by a fixed rule: higher counter nearer the anchor, and on a tie, the higher device name. r10 beats a10, so every copy reads Meet at 5 sharp pm, whichever edit arrives first, with no server deciding.

Deletes do not remove a character; they mark it deleted, a tombstone, because a slower edit may still be anchored to it. If Ravi deletes the 5 while Asha's ' pm' is in flight, a9 stays as a tombstone and ' pm' still lands after it. An edit anchored to a character a copy has not received yet waits until that character arrives.

Meet at 5 sharp pm may not be what either person wanted, but OT's tie-break cannot read minds either: both approaches promise identical copies and leave wording to people. Many CRDT apps still use a server to relay and store edits; they just do not need it to decide the order. What does all this bookkeeping cost?

What a CRDT costs

The permanent ids that free a CRDT from a central order are also its price. Count them for the fest plan, a 30 KB document. Three costs show up in practice.

  • Metadata per character. Each character carries its own id and its anchor's id, roughly 8 to 16 bytes each: 16 to 32 bytes of bookkeeping for one byte of text. Stored naively, the 30 KB plan carries about 1 MB of ids, and several times that in memory.
  • Tombstones that pile up. Every character ever typed stays. If the team typed 300 KB over a month of rewrites and kept 30 KB, the CRDT holds ten times the visible text.
  • Clean-up needs everyone. A tombstone can be dropped only when every copy has seen the delete, since an older edit may still point at it. One phone that stays offline for a month blocks clean-up for that document.

Libraries cut the first cost sharply by storing a run of characters typed in one go as a single item with a range of ids, which brings the metadata for normal typing down to a small multiple of the text. They cannot remove the history: a heavily rewritten document stays large, and opening it means loading that state, not just the text.

Operational transformationCRDT
Who orders editsa central owner per documentnobody: concurrent edits merge in any order
Offline and peer to peerpossible, but a long absence means many transforms at one ownera natural fit: copies merge whenever they meet
Extra datasmall: the text plus a version numberan id per character, plus every tombstone
Main difficultygetting the transform rules rightmemory, load time and tombstone clean-up

Choose OT with an owner when a server is always in the path; choose a CRDT when copies must merge without one. The fest plan is edited in browsers and apps that are almost always online, so this design keeps OT and its small per-edit cost.

Edit log, snapshots and history

Whichever method orders the edits, the ordered result must survive crashes and answer "what did the plan say yesterday?". The edit log is the document's diary: every acknowledged operation is appended with its version number and never changed. Replaying it from version 1 rebuilds the plan exactly, but after months that could mean millions of operations, so the owner also writes snapshots, bookmarks that hold the whole text at one version.

  • When to snapshot: every 500 operations, or after a few minutes of quiet, whichever comes first. The owner writes the full text to the blob store and records the version in snapshots.
  • Opening a document: read the latest snapshot, then the operations after it, rarely more than about 500.
  • Version history: group operations into sessions (a 30-minute gap starts a new one) and show each session's authors and time. An old version is the nearest snapshot before it, replayed forward.
  • Compaction: after, say, 30 days, keep hourly or daily snapshots and drop the operations between them. Old history becomes coarser but cheap.
text
open d_91
  latest version      1840
  latest snapshot     1500   (30 KB file from the blob store)
  ops to replay       1501 to 1840   (340 ops, about 34 KB)
  time to apply       a few milliseconds in memory

Snapshots keep opening fast no matter how long a document has existed.

Undo is per person. Undo should take back your own last edit, not whatever anyone typed last. The app keeps a stack of the user's own operations; undo builds the inverse (a delete for an insert), transforms it past every edit made since, and sends it as a new edit. Restore works the same way, so history only moves forward.

Durability before speed

Acknowledge an edit only after the log write has succeeded on more than one replica, in different zones. It costs a few milliseconds. A fast ack that is later lost means a volunteer's sentence silently disappears, the worst bug this product can have.

Presence, cursors and offline edits

Two kinds of data are left: the kind that is useless a minute later, and the kind that arrives hours late. Presence (who is here) and cursors (where they are) change many times a second, so they live only in the owner's memory and never touch the log. The owner forwards cursor moves a few times a second at most and drops a user whose connection misses heartbeats for about 30 seconds.

Cursors shift the same way edits do. If Ravi's cursor is at position 40 and Asha inserts 10 characters at position 5, Ravi's cursor must move to 50 on everyone's screen. The same transform rules apply.

Offline edits. When the connection drops, the app keeps working: it applies edits locally and queues them in the device's storage with the last version it saw. Say Ravi edits the plan on a two-hour train ride with no signal and returns with 500 queued operations while the plan moved on by 2,000. On reconnect the owner transforms his queue against those 2,000, and his app transforms the 2,000 against his queue: about a million rule checks, well under a second of CPU.

  • The real cost is meaning. The merge converges, but where both sides rewrote the same paragraph the text may read oddly. Show the user what changed while they were away.
  • Lost access. If the user's edit right was removed meanwhile, the owner rejects the queue. Offer to save their text as a new document rather than throwing it away.
  • Repeats. A reconnecting app may resend an edit the owner already stored. Each edit carries a client id and sequence number, so the owner recognises repeats, skips them and acks them again.

Part 4 · Failures and Trade-offs

What can fail

Everything so far assumed machines stay up. Most failures here pause editing for a few seconds; the promise to keep through all of them is that no acknowledged edit is lost.

ProblemEffectFix
Owner server crasheseditors pause for a few secondsa new owner loads snapshot plus log and writes its takeover record; apps reconnect and resend unacked edits
A paused owner wakes uptwo servers think they own a documentits next append hits a taken version slot, so it steps down and closes its connections
A document with thousands of viewersone server holds thousands of socketsserve viewers through a fan-out tier; keep editors on the owner
A slow or flaky clientits outgoing buffer grows on the serverdisconnect it past a limit; it reloads from a snapshot on reconnect
Log store slows downacks slow down for every editorbatch log writes per document; alert on write latency
A zone failsdocuments owned there pausethe log is replicated across three zones, so owners in other zones take over in seconds
A region failsevery document homed there stopspromote a standby region fed by an asynchronous copy of the log; it may lack the last second or two, so an app that sees the version go backwards offers to re-add its missing text
A deploy restarts the fleet1 M sockets reconnect and every open document reloadsrestart a few servers at a time; each stops taking edits, finishes its log writes and hands off its documents; apps reconnect with jitter over 30 to 60 s

The region row is the one place the promise bends. Copying to another region before every ack would add a cross-region round trip, about 30 ms even from Mumbai to Delhi, and let a slow standby slow every editor, so most designs accept losing the last second or two in a rare whole-region loss. The viewer row matters on fest day, when the schedule is projected around campus and 5,000 phones follow it while 3 organisers edit. Editors need the owner to order their edits; viewers only need the ordered stream. The owner publishes each version to a topic (the pub-sub chapter), a fan-out tier pushes it to viewers, and the owner keeps 3 sockets, not 5,003.

Trade-offs and running it

Each choice above buys something and costs something. Here they are side by side, with the one that remote editors feel explained below.

DecisionChoiceCost
Conflict handlingOT with a per-document ownerneeds the owner; long offline merges read oddly
When to acknowledgeafter a log write in more than one zonea few milliseconds more per edit
Snapshot frequencyevery 500 operationsmore blob writes, but fast opens
Presencememory only, throttledlost on a crash, which nobody minds
Where the owner livesone region per document, near most of its editorsabout 150 to 200 ms more per ack for editors on other continents

Editors far away. A New York editor on a plan owned in Mumbai still sees their own letters at once, thanks to local echo; only the ack takes about 200 ms, and the app simply sends bigger batches meanwhile. Others' edits reach them within about 250 ms. Do not give each region its own owner: two owners mean two orders of the same edits, which needs consensus between regions or a CRDT. If the team moves for weeks, hand ownership to a nearer region at a version boundary.

The replicated edit log dominates the bill: about 400,000 writes a second at peak, each kept in three copies, takes far more machines than the 50 or so collaboration servers. Compacting old operations after 30 days keeps its storage in check.

Part 5 · Going further

Rich text: edits on a tree

Optional: the fest plan has headings, bold times and lists, and supporting them changes the transform function but not the architecture. Store the document as a tree: blocks such as paragraphs, headings, list items and tables, each holding runs of text with marks such as bold or a link.

OperationExampleThe hard concurrent case
Insert or delete texttype ' pm' in block 3 at offset 9as for plain text, but a position is now a path: block, then offset
Split or join blockspress Enter in the middle of a paragraphtext typed after the split point must move into the new block
Add or remove a markmake characters 5 to 15 bolda split at 10 at the same time: both halves must end up bold
Change a block's typeturn a paragraph into a headingtwo people pick different heading levels: the edit ordered last wins
Insert, delete or move a blockdrag a list item; delete a paragraphsomeone types into the deleted paragraph: drop the text or keep the paragraph, and decide once

The owner still orders every edit, so nothing in the design above changes; only the transform function grows. Five operation types make 25 ordered pairs, each with its own cases. Marks add a question plain text never had: is a letter typed right after a bold word bold too? Many editors say yes for bold and no for links.

CRDT versions use the same trick as before: a mark is anchored to the ids of its first and last characters, so a bold range keeps covering the right words while others type inside it.

Build plain text first

Get plain text right, then treat rich text as the same idea on a tree: positions become paths and the rule table grows. Teams usually build on a well-tested editor library's operation model rather than writing these rules from scratch.

Part 6 · Interview kit

The 45-minute answer

A 45-minute round leaves about 40 minutes of design after introductions. Here is this product on that clock.

MinutesWhat you say and drawKey number or decision
0–5Requirements: live co-editing, cursors, history, offline. Non-functional: own typing instant, others' within about 200 ms, no acknowledged edit lost1 to 5 editors per document, up to about 100
5–8Estimates that decide something: 1 M live connections, about 400,000 edits a secondabout 4 edits a second per document, so one owner each
8–13API: open over HTTP, edit over a WebSocket, each edit an operation with a base version. Data: the (doc_id, version) log, snapshots in a blob storeoperations, never whole documents
13–22Draw the gateway, owner by consistent hashing, edit log, snapshot store, coordination service for leaseslog, then ack, then forward
22–37Deep dive: two inserts at one spot with a tie-break, a delete shifting an insert left, then CRDTs and their cost9 + 3 = 12; about 1 MB of ids for 30 KB of text, naively
37–40Wrap-up: owner crash and takeover record, zone and region loss, monitoring, rich text nexta claimed version slot fences a stale owner

Pick the deep dive from the question. Two people in one spot: OT and the tie-break. Editing on a train with no signal: offline merging, and CRDTs if offline is the main use. Last Tuesday's version, or what survives a crash: the edit log and snapshots.

Say
"Each document has one owner that orders its edits. At about 4 edits a second per document, one process is plenty; scale comes from spreading 100,000 busy documents."
Say
"I write each edit to a log replicated across zones before I ack it. That costs a few milliseconds, small next to the 100 ms batching window."
Say
"OT with an owner suits an always-online editor; a CRDT suits offline-first sync, at the cost of an id per character and tombstones."
Avoid
"Last write wins on the whole document." Two people typing at once would overwrite each other's work.
Avoid
"Lock the paragraph while someone types." Others are blocked, and a crashed editor's lock blocks everyone until it expires.

Interview drill

Interviewers often change one thing and watch how you adapt. Try each before opening the answer.

Usage grows 10×: 100 million daily users and 10 million live connections at peak. What changes?
  • Per document, nothing: a busy document still sees about 4 edits a second, so one owner each still works.
  • Collaboration servers grow to roughly 500 at 20,000 connections each.
  • The log takes about 4 M writes a second: about ten times the partitions, with group commit (many appends per disk flush).
  • Deploys and ring changes move more documents at once, so restart in smaller steps.
Product adds comments anchored to text that others keep changing. What changes in the data model?
  • A comments table keyed by (doc_id, comment_id) holds each thread and an anchor: start, end and the version they refer to.
  • Anchors move like cursors: the owner transforms them with every edit, or a CRDT ties them to character ids.
  • If the anchored text is deleted, keep the comment and mark it detached.
Finance asks you to halve the cost. Where do you look first?
  • The edit log, since it dominates the bill: compact after 7 days instead of 30, accepting coarser history beyond a week.
  • Move old snapshots to an archive tier, 20 to 50 times cheaper.
  • Serve viewers from a cheaper fan-out tier and unload idle documents sooner.
Common miss
Splitting one document's edits across servers for scale. Four edits a second needs no splitting, and two servers ordering one document would need consensus.
Common miss
Writing cursor moves to the edit log. They change many times a second, are useless a minute later, and would multiply the log's writes.

Wrap-up

The whole chapter on one page, for revision.

Next, the notifications chapter, Design a Notification System, moves from connections you hold yourself to messages you hand to outside providers: push, SMS and email.

Part 7 · Check yourself

Quick check

Q1The fest team's first prototype saved the whole plan on every change, and the last save won. Sentences vanished when two people typed at once. What is wrong, and what would it cost at full scale?

Think it through, then check.

Q2A partner college in London co-organises the fest, and half the editors of d_91, owned in Mumbai, are now there. What do they notice, and what would you change?

Think it through, then check.

Q3Inspectors edit shared reports for days in villages with no signal, then sync. OT with an owner, or a CRDT, and what does the choice cost?

Think it through, then check.

Q4Server 1 freezes for 20 seconds in a garbage-collection pause; its lease lapses and server 2 takes over d_91 at version 1840. Server 1 wakes and appends version 1841. What stops two histories?

Think it through, then check.

Q5A newsroom document is edited by 3 reporters and watched live by 3 lakh readers. Which building block carries updates to readers, and why not the owner?

Think it through, then check.

Next chapter · 35 of 39Design a Notification System~39 min · 3 animated scenes