9. Google Docs (Collaborative Editing) Advanced

A. Realistic Interview Prompt

"Design a collaborative document editor like Google Docs: multiple users open the same document at the same time, type simultaneously, and see each other's changes appear live."

This problem's difficulty is almost entirely conceptual, not volumetric. A single document is small (kilobytes to low megabytes of text) and has, realistically, a handful to a few dozen concurrent editors — nothing like the billion-user scale of earlier problems. The hard part is that two people can type in the same place at the same time, and every replica of the document — every open browser tab — must converge on the exact same final text, in the same order, without a human ever noticing a conflict dialog. That single requirement is what pulls in operational transformation or CRDTs, the central technical decision of this entire design.

B. Difficulty

Advanced — not because any individual component is exotic, but because the core conflict-resolution algorithm (N) is genuinely subtle to get right, and a design that "sounds reasonable" (last-write-wins, or naive full-document overwrites) silently loses user keystrokes rather than failing loudly, which is a uniquely dangerous failure mode for a writing tool.

C. What This Problem Tests

D. Requirements

Functional

  1. Multiple users can open and edit the same document concurrently
  2. Edits made by one user propagate to all other active viewers in near real time
  3. Users can see other active editors' presence and live cursor/selection position
  4. A user who edits while offline (or disconnected) has their edits merged in correctly once they reconnect

Non-Functional

Out of scope: rich formatting/embedded objects (treated as an extension of the same text-operation model), commenting/suggestion mode, access control and sharing permissions, full version-history UI.

E. Clarifying Questions

#QuestionWhy It MattersDesign Decision Affected
1Plain text content only, or rich formatting/embedded images too?Rich content is a strictly larger version of the same core problem — the conflict-resolution model must extend to structured, not just linear, contentCore Entities (H)
2Roughly how many concurrent editors per document, realistically?This problem's true scale is small (tens, not millions) per document — it reframes the challenge as algorithmic correctness under concurrency, not raw throughputCapacity Estimation (G)
3Must offline edits be supported, or is a persistent connection assumed?Offline support means the same reconciliation machinery must work with an arbitrarily large gap between "edit made" and "edit synced," not just a few hundred milliseconds of network jitterDeep Dive 5 (Q)
4Is perfect real-time cursor/selection sharing required, or is basic "who's viewing" presence enough?Live cursor positions are high-frequency, ephemeral, and loss-tolerant — a materially different data path than the document content itselfDeep Dive 4 (P)
5Is full edit history / version restore in scope?History is a natural extension of an append-only operation log this design already needs, but is a distinct feature surface from live collaboration itselfDeep Dive 6 (R)

F. Assumptions

G. Capacity Estimation

MetricValue / CalculationWhy It Matters
Concurrent editors per documentTens, illustratively (F's assumption) — not millionsConfirms the per-document hot path is small-N; the real scale challenge is the number of documents, not editors-per-document
Total documents platform-wideHundreds of millions, most almost never edited concurrentlyThe system must cheaply support a huge count of mostly-idle documents while giving a small number of "hot" documents fast, correct real-time sync
Edit operation sizeA single character insert/delete: position + character + metadata ≈ tens of bytesIndividual operations are tiny — the challenge is correctness and volume of operations per second on a hot document, not per-operation payload size
Operations per second on a "hot" documentTens of editors × a few keystrokes/sec each ≈ low hundreds of ops/sec, peakComfortably within a single server's capacity to sequence and broadcast for one document — reinforces that per-document load is never the bottleneck
Document sizeTypical document: tens of KB to a few MB of textSmall enough to hold entirely in memory on whichever server is actively coordinating that document (S)

H. Core Entities

EntityPurposeKey Fields
DocumentA single editable text documentdoc_id, latest_snapshot_ref, revision
OperationOne atomic edit (insert/delete at a position), the unit of the conflict-resolution model (N)op_id, doc_id, author_id, type (insert/delete), position, content, base_revision
SnapshotA periodic, materialized full document state (R), avoiding replaying the entire operation historydoc_id, revision, content_blob, created_at
Presence (ephemeral)A user's live cursor position/selection and online status while viewing a document (P)doc_id, user_id, cursor_pos, last_seen_at

I. API Design

WS   /v1/documents/{id}/edit             -> bidirectional stream:
                                             client sends: { op, base_revision }              (M, N)
                                             server sends: { transformed_op, new_revision }    (broadcast to all)
WS   /v1/documents/{id}/presence         -> bidirectional stream: { cursor_pos }               (P, not persisted)
GET  /v1/documents/{id}                  -> { content, revision }                              (initial load, from R)
GET  /v1/documents/{id}/history          -> [{ revision, snapshot_ref, created_at }]            (extension, out of scope per F)

Why WebSocket streams, not request/response REST calls, for the edit path: this is fundamentally a bidirectional, server-push problem — the server must proactively deliver other users' operations to a client the instant they're committed, not wait for that client to poll for them. A persistent connection per active editor is exactly the connection-management problem already solved in the Chat System design, reused directly rather than reinvented (M).

Why presence is a separate stream from edit operations: cursor positions change far more frequently than actual content edits (every mouse movement or arrow key), and are entirely disposable if a message is dropped — mixing them into the same stream as durable, must-not-be-lost content operations would force the wrong reliability guarantee onto both.

J. Basic / Naive High-Level Design (V1)

The simplest thing that satisfies the functional requirement in isolation: whenever a user edits, the client sends the entire updated document content to a server, which overwrites the stored document and pushes the full new content to every other connected client.

flowchart LR A(["User A edits"]) -- "full document content" --> Server["Doc Server"] Server -- "overwrite" --> Store[("Document Store")] Server -- "push full content" --> B(["User B's client"])

V1 — whole-document last-write-wins. Looks fine with one editor; silently destroys work with two.

Why this breaks immediately, not eventually: if User A and User B are both typing at the same moment, each sends their own full copy of the document reflecting only their own edit. Whichever write reaches the server second simply overwrites the first in its entirety — User A's keystrokes vanish completely, with no error, no conflict warning, and no way to recover them. Unlike Ticketmaster's overselling race, which produces a visible, loud failure (two people show up for one seat), this failure mode is silent — the product looks like it's working right up until a user notices their paragraph is gone.

K. Data Model & Store Choice

Two distinct stores for two distinct access patterns: an append-only operation log (every individual insert/delete, ordered by revision number for a given doc_id) is the actual source of truth for how the document reached its current state, while a periodic snapshot store (R) holds materialized full-document content at checkpoints so a newly-joining client (or server restart) doesn't have to replay potentially thousands of tiny operations from the beginning of the document's history.

Why the operation log is the source of truth, not the "current content" field alone: the log is exactly what operational transformation (N) needs to reconcile a client's edit against everything that's happened since that client last synced — a bare "current content" string has already discarded the ordering and positional information required to transform a late-arriving concurrent operation correctly.

Sharding/routing by doc_id: all operations for one document must be sequenced by a single authority to have a well-defined order at all (O) — this is the same principle as Ticketmaster's per-event sharding and Chat System's per-conversation routing, here applied to keep one document's entire operation stream flowing through one coordinating point.

L. End-to-End Flow — Two Users Editing Concurrently

sequenceDiagram participant A as User A (client) participant B as User B (client) participant DS as Doc Server (owns doc_id, O) A->>A: apply own op locally (optimistic echo) A->>DS: op_A (based on revision 5) B->>B: apply own op locally (optimistic echo) B->>DS: op_B (based on revision 5) DS->>DS: transform op_B against op_A (N)
assign revision 6, 7 in arrival order DS-->>A: broadcast transformed op_B (rev 7) DS-->>B: broadcast op_A (rev 6, already applied for A) A->>A: apply transformed op_B Note over A,B: both clients converge to identical
document content, despite differing local histories

M. Deep Dive 1 — Real-Time Sync & Transport

Requirement this addresses: the near-real-time propagation NFR (D) and the instant-local-responsiveness NFR (D) together — these pull in opposite directions and both must be satisfied at once.

Optimistic local echo: a user's own keystrokes are applied to their local in-memory document copy immediately, before any server round-trip completes — the edit is sent to the server asynchronously in the background. This is the only way to satisfy "instant" typing feel in a networked system; waiting for server acknowledgment before rendering a keystroke would make every character feel laggy.

Why a persistent WebSocket connection per active editor, not polling: polling either wastes requests when nothing changed or adds up to a full polling-interval's worth of latency to every edit's propagation — directly reusing the same connection-management reasoning already established in the Chat System design (long-lived, stateful connections held by dedicated connection servers, tracked in a shared session store for routing).

One coordinating server "owns" a given document (K, O): rather than any connected client's edit being validated against a globally-distributed source of truth, all operations for one doc_id flow through a single server instance holding that document's current state in memory — turning conflict resolution into a fast, local, single-machine operation instead of a distributed consensus problem.

N. Deep Dive 2 — Operational Transformation vs. CRDTs

Requirement this addresses: the convergence NFR (D) — the single central technical decision of this entire problem, and the one place a candidate's judgment is most directly tested.

The actual problem being solved: two users each start from the same document state (revision 5) and each submit an operation — say, User A inserts "X" at position 10, and User B (unaware of A's edit) inserts "Y" at position 10 as well. Both operations are individually valid against revision 5, but applying them naively in whatever order they happen to arrive produces two different final documents on two different clients, unless something actively reconciles them.

ApproachHowTrade-off
Operational Transformation, central server (chosen default)A single coordinating server (M) receives each operation along with the revision it was based on. If other operations were committed since that revision, the server mathematically transforms the incoming operation's position to account for them (e.g. shifting B's insert position by however many characters A's already-applied insert added before it), then assigns it the next sequential revision number and broadcasts the transformed result to everyone.Requires a correct transformation function per operation type, and a single authority per document — but that authority is exactly what M already introduces, and matches how Google Docs itself is actually built (the Jupiter protocol)
CRDT (e.g. a sequence CRDT like RGA/Logoot)Every character is assigned a globally unique, immutable, totally-ordered identifier (typically a per-site counter plus a site ID) at creation time. Any two replicas that have seen the same set of characters converge to the same order automatically, purely from comparing IDs — no central arbiter needed at all.Enables genuine peer-to-peer sync with no server in the loop, and merges offline edits from arbitrarily long disconnections trivially — at the cost of real per-character ID metadata overhead and materially harder correctness reasoning to implement from scratch
Peer-to-peer OT (no central server)Every client transforms every operation against every other client's concurrent operations directlyThe transformation logic must handle transforming an operation against an arbitrary set of concurrent operations in any order — the complexity that made early peer-to-peer OT research impractical, and exactly what routing all operations through one central server (chosen default) sidesteps entirely

Why central-server OT is the right default answer here, not peer-to-peer OT or a CRDT: once M already establishes that one server owns a given document's authoritative state, that same server is perfectly positioned to be the single point of transformation — every incoming operation only ever needs to be transformed against operations the server itself already committed, in a known order, which is a far simpler problem than transforming against an unordered set of concurrent peer operations. This is the actual architecture real Google Docs uses. A CRDT becomes the more compelling choice specifically when the product requirement is genuine serverless peer-to-peer sync or truly long-lived offline-first editing with no reliable central authority — worth naming as the right answer to a different, adjacent set of requirements (Q revisits this for the bounded offline case actually required here).

O. Deep Dive 3 — Ordering & Causality

Requirement this addresses: convergence (D) depends on every client applying operations in a way that respects what each operation was actually based on — the same ordering problem named explicitly in Chat System's message-ordering deep dive, here applied to edit operations instead of chat messages.

The fix — a per-document monotonic revision number, assigned at the coordinating server (M), not by the client: exactly the same principle as Chat System's per-conversation seq_num assigned at the point of persistence — each operation carries the revision it was based on, and the server assigns the next revision only once the operation is transformed (N) and committed. Clients never need wall-clock timestamps to agree on order; the server's assigned revision sequence is the single authoritative ordering, immune to clock drift between clients.

Why this only works because one server owns the document (K): a monotonic sequence is trivial to assign correctly from a single authority, and becomes a distributed-consensus problem the moment more than one server could commit revisions for the same document concurrently — another way K and M reinforce each other rather than being independent decisions.

P. Deep Dive 4 — Presence & Live Cursors

Requirement this addresses: the presence/cursor-sharing functional requirement (D) — deliberately built as a separate, lower-guarantee data path from document content itself (I).

Why presence is ephemeral, in-memory, and never persisted: a cursor position five minutes ago is worthless information — there's no analog to the durable operation log (K) here, because there's nothing worth recovering after the fact. Presence state lives only in the coordinating server's memory (or a short-TTL cache) for as long as a user is actively connected, and simply disappears on disconnect with no cleanup job required beyond the TTL itself.

Why dropped presence updates are an acceptable trade-off but dropped edit operations are not: if a cursor-position packet is lost on the wire, the next one (arriving milliseconds later, since cursor updates are frequent) simply supersedes it with no visible impact — this is the same class of loss-tolerance as Ticketmaster's approximate seat-map display (R in that problem), applied here to presence instead of inventory counts. An edit operation, by contrast, must never be silently dropped, which is exactly why it travels over the same reliable, ordered channel that carries revision numbers (O), not the disposable presence channel.

Q. Deep Dive 5 — Offline Editing & Reconciliation

Requirement this addresses: the offline-edit functional requirement (D) — the bounded special case where the gap between "edit made" and "edit reaches the coordinating server" can be minutes or hours instead of milliseconds.

Why this reuses N's machinery rather than needing a separate merge algorithm: a client that goes offline simply keeps applying operations to its local copy and queuing them, each still recording the revision it was based on. On reconnect, the client replays its queued operations to the coordinating server exactly as if they'd each arrived slightly late — the server transforms each one against everything committed in the meantime (N), the same way it already transforms operations that arrive milliseconds apart from two simultaneously-typing users. Offline editing is not a new problem; it's the existing conflict-resolution problem with a longer, more variable delay.

Where this stresses the OT-vs-CRDT decision (N) the hardest: a very long offline period means a very large batch of concurrent history to transform against on reconnect, which is the scenario where a CRDT's "just merge, no central coordination required" property is most attractive — worth explicitly naming as the boundary condition where the trade-off in N could tip toward a CRDT if offline periods were expected to be routinely long (days, not minutes) rather than the occasional dropped-connection case assumed here (F).

R. Deep Dive 6 — Persistence & Snapshotting

Requirement this addresses: practical durability and fast document load times — not explicitly stated in D, but required to make K's operation log usable in practice rather than just in theory.

Why periodic full-document snapshots, not replaying the entire operation log on every load: a long-lived, heavily-edited document could accumulate many thousands of individual insert/delete operations over its lifetime — replaying all of them to reconstruct current content on every load would make opening an old, well-edited document progressively slower over its lifetime, which is exactly backwards. A snapshot taken periodically (e.g. every N operations, or on a time interval) captures full materialized content at that revision; loading a document means fetching the latest snapshot plus only the handful of operations committed since it.

This is the same metadata/blob-store pattern used elsewhere in this document (News Feed media, Chat System attachments, Web Crawler pages, Video Streaming files) — a compact, frequently-accessed pointer (the current revision number) alongside a larger, less-frequently-rewritten blob (the snapshot content), rather than reconstructing expensive state from scratch on every access.

S. Deep Dive 7 — Scaling to Many Documents

Requirement this addresses: G's observation that the real scale challenge is the sheer number of documents, not concurrency within any single one.

Why this is an assignment/routing problem, not a per-document throughput problem: since G established that even a "hot" document only sees low hundreds of operations per second, a single coordinating server instance can easily own many simultaneously-active documents at once — the actual scaling question is how a client's initial connection request for a given doc_id gets routed to whichever server instance currently owns (or should own) that document.

Consistent hashing of doc_id across a fleet of coordinating servers — the same technique used for shard/owner assignment in Rate Limiter and Chat System's connection routing — lets the fleet scale horizontally by adding servers, with each new server picking up ownership of a proportional slice of documents, while any single document's operations always land on the same server for as long as it stays active.

An idle document has no server holding it in memory at all: a coordinating server only needs to load a document's state (from the latest snapshot plus recent operations, R) when the first client connects to it, and can evict it from memory after a period with no active editors — keeping the vast majority of the hundreds of millions of mostly-untouched documents (G) from consuming any active-server resources at all.

T. Full Solution at Scale — Final Architecture

flowchart TB A(["User A"]) -- "WebSocket" --> LB["Connection Router
(consistent hash on doc_id, S)"] B(["User B"]) -- "WebSocket" --> LB LB --> DS["Doc Server owning
this doc_id (K, M)"] DS -- "transform + assign revision (N, O)" --> DS DS -- "broadcast transformed ops" --> A DS -- "broadcast transformed ops" --> B DS -- "append operation" --> OpLog[("Operation Log (K)")] DS -- "periodic snapshot" --> SnapStore[("Snapshot Store (R)")] DS -- "ephemeral cursor state" --> Presence[("Presence Cache (P)
in-memory / short TTL")]

Everything above is the composition of M through S — no new component introduced here.

U. Design Evolution

What stays stable throughout: the operation-based edit model from M — every later refinement changes how operations are transformed, ordered, or routed, never the basic shape of "an edit is a small, positioned operation, not a full-document overwrite."

V. Consistency Model

This design targets what the distributed-systems literature calls strong eventual consistency: any two replicas that have received the same set of operations converge to the identical document state, regardless of the order those operations happened to arrive in — but there is no requirement that all replicas see every operation at the exact same instant. The coordinating server (M) provides a stronger, additional guarantee on top of that baseline: a single authoritative order (via revision numbers, O) for the online, connected case, which is what OT specifically relies on. The offline case (Q) relaxes the timing further — a client's view can diverge from the server's for an arbitrarily long period — but never relaxes the eventual-convergence guarantee itself. This is a different consistency shape than Ticketmaster's hard strong-consistency requirement: there is no single "seat," no operation that must be rejected to preserve correctness — every legitimate concurrent edit is preserved and reconciled, never discarded.

W. Failure Scenarios

X. Bottlenecks & Scaling Boundary

StageActual BottleneckFix Applied
V1 whole-document overwriteConcurrent edits destructively overwrite each otherOperation-based edits + OT on a central coordinating server (M, N)
Single global document serverOne server can't hold every document in memory, nor should it need to for idle documentsConsistent-hash-routed fleet, only active documents loaded (S)
Operation log replay on every loadLoad time grows unboundedly with a document's total edit historyPeriodic snapshots (R)
Presence mixed into the content channelHigh-frequency ephemeral cursor traffic competes with must-not-lose content operationsSeparate, disposable presence channel (P)

Y. Design Trade-Offs

DecisionOption AOption BTrade-off
Conflict resolutionCRDT (peer-to-peer capable)Operational transformation, central server (chosen)A CRDT enables true serverless peer-to-peer sync and trivially long offline periods, at the cost of per-character ID metadata and harder implementation; central-server OT is simpler to reason about and matches how Google Docs is actually built, at the cost of requiring one authority per document
Edit propagationPollingPersistent WebSocket connection (chosen)Polling adds latency proportional to the poll interval and wastes requests when nothing changed; a persistent connection gives near-instant server-push delivery, reusing Chat System's connection-management design
Document ownershipAny server can commit revisions for any documentSingle coordinating server per active document (chosen)Multi-writer ownership would turn revision assignment into a distributed-consensus problem; single ownership makes it a trivial local counter, at the cost of that one server being a temporary single point of coordination for that document
Document load strategyReplay the full operation logPeriodic snapshot + recent ops only (chosen)Full replay is simpler but degrades over a document's lifetime; snapshots keep load time flat at the cost of periodic snapshot-generation work
Presence channelSame channel as content operationsSeparate ephemeral channel (chosen)Sharing a channel is simpler to build but forces the same reliability guarantee onto disposable and durable data alike; separating them lets each get the guarantee it actually needs

Z. Common Bad Designs

AA. Interviewer Follow-Ups

Follow-UpDesign ChangeComponents ChangedComponents Unchanged
"What if we need true peer-to-peer sync with no server at all?"Revisit N's trade-off table — a CRDT becomes the right answer once there's no central authority to transform againstConflict-resolution mechanism (N), removes the coordinating-server dependency (M)Operation-based edit model itself, offline-reconciliation logic (Q)
"Support rich formatting (bold, headers, embedded images), not just plain text."Operations extend to structured content (e.g. a tree or block-based document model) instead of a flat character sequence — the same OT/transformation principle applies to structured operationsOperation/entity model (H), transformation function specifics (N)Central-server ownership, ordering, presence, snapshotting
"Add full version history with named restore points."Directly exercises R's already-existing snapshot mechanism — expose snapshots (plus replay from a chosen point) as a first-class, user-facing featureNew history UI/API surface on top of RLive-editing path entirely
"A document goes viral and gets thousands of simultaneous viewers (mostly read-only)."Separate the vastly larger read-only viewer population from the small set of active editors — serve viewers from a cached/broadcast read replica of current content rather than routing them through the full edit-coordination pathNew read-only viewer pathEditor coordination, OT, presence for actual editors
"What happens if the coordinating server for a hot document becomes a bottleneck?"Directly exercises S's eviction/reassignment logic, plus confirms G's premise that a single document's true edit throughput is low — the fix is operational (rebalance, restart), not architecturalNone new — this is what S already handlesEverything else

AB. Interviewer Evaluation Rubric

AC. Interview Simulation

Interviewer: Design a collaborative document editor like Google Docs — multiple people editing the same doc at once.
Candidate: The core challenge is that two users can edit the same part of the document at nearly the same instant, and every client needs to end up with the identical final content regardless of the order those edits happened to arrive over the network. I wouldn't send whole-document content on every edit — that turns into last-write-wins and silently destroys someone's work. Instead, edits should be small operations — insert this character at this position — sent individually.
Interviewer: Two operations arrive that both insert text at the same position. How do you resolve that?
Candidate: That's exactly what operational transformation solves. I'd route all operations for one document through a single coordinating server that holds the authoritative state. When an operation arrives, the server checks what's already been committed since the revision that operation was based on, and mathematically transforms its position to account for that — for example, shifting an insert position forward if something was already inserted before it. Then it assigns the next revision number and broadcasts the transformed operation to everyone.
Interviewer: Why not a CRDT instead?
Candidate: A CRDT is the right call if you genuinely need peer-to-peer sync with no reliable central server, or very long offline periods, since it converges without any central authority at all — every character gets a unique ordered ID and replicas merge automatically. But it comes with real per-character metadata overhead and is harder to get right. Since I already have one server owning each document to make revision numbering trivial, that same server is naturally positioned to do the transformation directly — which is actually the architecture real Google Docs uses.
Interviewer: What about a user editing while offline for an hour?
Candidate: I wouldn't build a separate merge algorithm for that — their client just keeps queuing operations locally, each still recording what revision it was based on. On reconnect, it replays them to the coordinating server, which transforms each one against everything committed in the meantime, exactly like it would for two people typing milliseconds apart. It's the same mechanism, just with a longer gap. If offline periods were expected to be routinely long — days, not an occasional dropped connection — that's actually where I'd reconsider the CRDT trade-off.
Interviewer: How do you show live cursors without that adding load to the edit path?
Candidate: Cursor positions get their own separate, ephemeral channel — they're high-frequency and completely disposable, since a stale cursor position is immediately replaced by the next update. I wouldn't give that the same durability or ordering guarantees as actual content edits, which do need to never be lost.

AD. Online Solution Comparison

SourceApproachStrengthWeaknessWorth Learning
Hello InterviewOT-first framing with a central per-document server, explicit discussion of CRDT as the peer-to-peer alternative, and offline sync treated as reused reconciliation machineryClearest articulation of why central-server OT matches Google's actual real-world architecture — the core structure adopted for this sectionLighter on presence/cursor-sharing as its own explicit deep diveThe "one server owns a document, transformation becomes a local problem" framing reused directly as the spine of M, N, and O
automerge (GitHub) / yjs (GitHub) — production CRDT librariesReal, production-grade sequence-CRDT implementations used by actual local-first collaborative appsGround-truth confirmation that CRDT-based collaborative editing is a proven, real-world-deployed alternative, not just a theoretical interview talking pointBoth are peer-to-peer/local-first-oriented, so translating their design choices back to this problem's central-server framing requires careConfirms the genuine trade-off named in N is real and actively chosen differently by different real products (Google Docs vs. Figma/Notion-adjacent tooling), not a false dichotomy
Google's original "Jupiter" collaborative editing paper (referenced conceptually — the algorithm underlying real Google Docs)Client-server operational transformation with a central server resolving all concurrency, avoiding the O(n²) complexity of peer-to-peer OTThe actual, real-world precedent for the central-server-OT default chosen in N — not a hypothetical interview constructAcademic paper, not a system-design walkthrough — needs translation into interview-shaped components, which this section providesConfirms that routing all operations through a single authoritative server specifically to simplify transformation (M, N) is deliberate, well-precedented engineering, not a shortcut

AE. Interview Cheat Sheet

ProblemGoogle Docs (Collaborative Editing)
DifficultyAdvanced (conceptually hard, volumetrically small — small-N correctness, not big-N throughput)
Core requirementsConcurrent multi-user editing, near-real-time propagation, live presence/cursors, offline-edit reconciliation, guaranteed convergence
Central decisionOperational transformation on a single coordinating server per document — not peer-to-peer OT, not (by default) a CRDT, though CRDTs are the right call for genuine peer-to-peer/long-offline requirements
OrderingServer-assigned monotonic revision number per document, never client timestamps — same principle as Chat System's per-conversation seq_num
Offline editingReuses the same OT reconciliation machinery with a longer delay — not a separate merge algorithm
PresenceSeparate ephemeral, in-memory, non-persisted channel — deliberately lower guarantees than content operations
PersistenceAppend-only operation log as source of truth + periodic snapshots to bound document-load time
ScalingConsistent-hash-routed fleet of coordinating servers, one authority per active document, idle documents held by no server at all
Common mistakesWhole-document last-write-wins overwrites; ordering by wall-clock timestamp; treating presence with content-grade durability; blocking local echo on the network
What NOT to over-engineerFull version-history UI or rich structured-content formatting — name them as natural extensions of the operation log and operation model, not something to design from scratch