Skip to content

FRONTEND SYSTEM DESIGN

Design a Collaborative text editor

Design a Google-Docs-style editor: multiple cursors, low-latency sync, and conflict-free merging.

Collaborative text editor architecture diagram

THE INTERVIEW SEQUENCE

How to drive this discussion

01

Clarify scope

02

Set requirements

03

Draw data flow

04

Defend trade-offs

Start with the smallest coherent design. After the main path works, introduce failure, scale, accessibility, observability, and release strategy one constraint at a time.

01Concrete scale assumptions

Rich-text document up to 1 MB with 20 typical and 200 peak simultaneous editors.

Local keystrokes render within one frame; remote edits target sub-200 ms regional delivery.

Offline edits for several hours must converge after reconnect.

Durable document operations and ephemeral presence have different reliability requirements.

02Architecture decisions and rationale

Local-first editor

The editor applies a local operation immediately and never waits for the network to show the user's keystroke.

Conflict-resolution core

A CRDT or OT engine owns positions, causality, merge, undo semantics, and serialization; UI code never hand-merges strings.

Separate presence

Cursor and typing indicators are lossy, high-frequency data and must not share the durable operation log's guarantees.

Snapshot plus operation tail

A compact snapshot bounds load time while the subsequent log preserves causality and auditability.

03Client contracts and data boundaries

Operation envelope

Every operation has stable actor and sequence identity so replay is idempotent.

TypeScript
type OpEnvelope = { documentId: string; actorId: string; seq: number;  deps: Record<string, number>; payload: Uint8Array; };type SyncMessage =  | { type: 'ops'; ops: OpEnvelope[] }  | { type: 'ack'; actorId: string; throughSeq: number }  | { type: 'snapshot'; version: string; bytes: Uint8Array };

04Failure-mode analysis

Socket disconnects

Impact: Remote edits pause while local edits continue.

Mitigation: Persist unsent ops locally, reconnect with backoff, exchange version vectors, and replay idempotently.

Duplicate or reordered operation

Impact: A naive client corrupts or repeats content.

Mitigation: Deduplicate actor/sequence ids and let the merge engine apply causal rules.

Presence flood

Impact: Cursor traffic delays document operations.

Mitigation: Throttle presence, drop stale updates, and isolate its channel or priority.

Snapshot incompatible

Impact: A new client cannot decode persisted state.

Mitigation: Version formats, migrate deliberately, and retain a compatible operation replay path.

05Observability and operations

  • Measure local apply latency, remote propagation, reconnect duration, pending-op depth, merge failures, and snapshot load time.
  • Trace document session and actor ids while avoiding document content in logs.
  • Alert on causal gaps, repeated reconnect loops, and operation rejection rates.
  • Run deterministic convergence simulations with reordered, duplicated, delayed, and offline operation sequences.

06Security and accessibility boundaries

  • Authorize document membership on connection and every mutation; an opaque document id is not permission.
  • Encrypt transport, constrain pasted rich content, and sanitize exports and embeds.
  • Expose semantic document structure, keyboard editing, collaborator names, and non-color cursor distinctions.
  • Announce collaborator joins and edits sparingly so live updates do not overwhelm screen-reader users.

ANSOne full answer, start to finish

Drive the discussion yourself first. This is one strong answer delivered the way you would say it in the room — a model, not the only correct design.

The trap here is trying to invent your own text-merging on the spot. Do not. Name a proven family of algorithm, explain the local-first idea, and spend your time on the two things that actually matter: how edits merge without a lock, and why cursor positions travel on a completely separate channel from the text itself.

  1. Step 1 · Clarify the scope

    Ask: 'How many people edit one document at once — a handful, or hundreds? Plain text or rich text with formatting? Does it need to work offline and sync later? And do we need per-user undo?' State assumptions: 'I will assume up to about 20 active editors per document, rich text, offline support needed, and undo that only undoes my own changes.' The offline and undo answers change the algorithm choice, so it is worth asking.

  2. Step 2 · Requirements, user-facing and hidden

    User-facing: type and see it instantly, see other people's cursors and selections, and never lose an edit to a conflict. Hidden: edits from different people must combine to the same final document on every screen without anyone taking a turn, the server must be the durable record, and presence (cursors) can be lossy but the document cannot.

  3. Step 3 · Name the merge approach

    'I would use a conflict-free replicated data type, a CRDT, or operational transformation — both are well-studied ways to turn each person's edit into an operation that combines correctly with other people's concurrent operations. I would not hand-roll text diffing; that is where these systems break. CRDTs tend to suit offline-first; OT needs a central server but carries less metadata. Given the offline requirement I lean CRDT.'

  4. Step 4 · Local-first, then draw the boxes

    'Every keystroke is applied to the local copy immediately — that is what makes typing feel instant even on a slow connection — and also turned into an operation sent to the server. The server puts operations into a consistent order (or transforms them) and broadcasts them to everyone else, and also stores the authoritative document. Each client applies incoming operations through the same merge logic and converges. So: a thin editor UI, a merge engine, a WebSocket transport, a server that orders and persists.'

  5. Step 5 · Presence is a separate channel

    'Cursor and selection updates go on a different channel from document operations. They can be sent more often, they can be dropped, and they expire on their own — if a cursor update is lost, nothing breaks. Document operations are the opposite: they need ordering, deduplication, persistence, and guaranteed eventual delivery. Mixing them means either the cursors are too heavy or the edits are too fragile.'

  6. Step 6 · Reconnect and undo

    'On reconnect, the client and server compare what versions each has seen, the client pulls the operations it missed, replays any local operations it made offline (idempotently, so replaying twice is safe), and the merge engine settles. Undo is defined as creating the inverse of my own last intention in the current merged state — not restoring an old snapshot of the whole document, which would wipe out other people's work.'

  7. Step 7 · The frontend bottleneck

    'The performance risk is not the merging, it is rendering a large document and re-mapping every cursor and selection on each change. I would render only the nodes an operation actually touched and keep the merge work off the expensive layout path.'

How to close

'So: up to ~20 editors, rich text, offline-capable. A proven CRDT turns each edit into a commuting operation; edits apply locally first for instant feel, then sync through a server that orders and persists them, and every client converges with no lock. Cursors ride a separate lossy channel. Reconnect reconciles by version and replays local ops idempotently. Undo inverts my own intention in the merged state. After launch I would watch convergence correctness with fuzz-tested operation histories and the render cost on large documents.'

QAInterview questions and model answers

Choose from offline needs, server authority, metadata cost, ecosystem maturity, and required undo semantics; both require a proven algorithm, not ad hoc text diffs.

Primary references

DESIGN CHECKLIST

Build your answer from first principles

Use this checklist to rehearse the case without reading the worked answer above.

CLARIFYING QUESTIONS

Do not design yet. Use these questions to make hidden assumptions explicit and prevent solving the wrong problem.

  1. 01

    How many concurrent editors per document, realistically — a handful or hundreds?

  2. 02

    Is offline editing with later sync required?

  3. 03

    Do we need full version history/undo across collaborators, or just current-state convergence?

  4. 04

    Rich text (formatting, embeds) or plain text?

FUNCTIONAL REQUIREMENTS

These are the user-visible capabilities the design must support. They define the first version's scope.

  1. 01

    Multiple users edit the same document concurrently with changes visible to each other in near real time.

  2. 02

    Each collaborator sees the others' live cursor position and selection.

  3. 03

    Edits eventually converge to the same document state for every client, regardless of arrival order.

  4. 04

    Support undo/redo per user without corrupting others' concurrent edits.

NON-FUNCTIONAL REQUIREMENTS

These qualities shape the architecture even though users do not click them directly: latency, resilience, accessibility, consistency, and scale.

  1. 01

    Low perceived-input latency for the local user — local edits render instantly, never waiting on a round trip.

  2. 02

    Correct convergence under concurrent, out-of-order, and even temporarily offline edits.

  3. 03

    Reasonable payload size per edit so the sync channel scales with many small edits, not full-document diffs.

ARCHITECTURE

Now assign responsibilities to clear boundaries. Each part should have one reason to change and an explicit contract with the next part.

  1. 01

    A conflict-resolution core, typically CRDT-based or Operational-Transform-based, that turns local edits into ops that commute correctly with concurrent remote ops.

  2. 02

    A thin editor UI (contenteditable or a structured rich-text framework) that applies local ops optimistically and remote ops as they arrive.

  3. 03

    A real-time transport (WebSocket) broadcasting ops between connected clients through a central server that also persists the authoritative document state/op log.

  4. 04

    A presence channel, separate from document ops, carrying lightweight cursor/selection updates at a different cadence and durability requirement.

DATA FLOW

Trace one important user action from input to rendered result. This exposes ownership, race conditions, retries, and stale-data paths.

  1. 01

    A local keystroke is applied to the local document immediately and turned into an op sent to the server.

  2. 02

    The server assigns the op a position in the causal order (or transforms it against concurrent ops) and rebroadcasts it to other connected clients.

  3. 03

    Each client applies incoming remote ops against its local state using the same commutative merge logic, converging without a central lock.

  4. 04

    Cursor/presence updates flow on a lighter, higher-frequency, lossy channel that doesn't need the same durability as document ops.

TRADE-OFFS

There is no perfect architecture. State what each choice optimizes, what it costs, and the signal that would make you revisit it.

  1. 01

    CRDTs simplify offline/merge correctness but can carry more metadata overhead per character/op than OT; the choice depends on expected edit volume and offline requirements.

  2. 02

    Optimistic local rendering gives the best perceived latency but requires a robust reconciliation path for the rare case a local op is rejected or needs transforming against a conflicting remote op.

  3. 03

    Broadcasting every keystroke as its own op maximizes responsiveness but increases network chatter; batching small, rapid edits trades a little latency for materially less traffic.

SCALING

Scale the measured bottleneck rather than every box. Frontend scaling includes payload size, main-thread time, rendering work, cacheability, and release safety.

  1. 01

    Shard document sessions by document id so a single hot document doesn't bottleneck the whole real-time fleet.

  2. 02

    Persist a periodic snapshot plus the op log since that snapshot, rather than replaying the entire history to reconstruct state on load.

  3. 03

    Separate the presence/cursor channel's scaling concerns from the document-op channel — presence can tolerate loss and staleness that ops cannot.

COMMON PITFALLS

Interviewers often probe these failure modes. Name them before being prompted and explain the guardrail you would add.

  1. 01

    Treating this as a simple 'last write wins' sync problem, which silently drops concurrent edits instead of merging them.

  2. 02

    Re-broadcasting a client's own ops back to itself and double-applying them.

  3. 03

    Coupling cursor/presence updates to the same reliability guarantees as document content, adding unnecessary latency and load.

Your closing summary

Restate the critical user flow, the most important quality target, the architecture boundary that protects it, and the trade-off you accepted. Then name the first metric you would watch after launch.

Back to all cases →