System design interview: design a collaborative editor

A collaborative editor lets every client apply its own edits immediately and sends them as small operations to a session server that owns the document, orders the operations, stores them in a log and broadcasts them over WebSockets. Concurrent edits are reconciled either with operational transformation (OT), where the server rewrites an operation against the ones it has not seen, or with a CRDT, where every character has a unique identity so replicas converge in any order.

By Published 9 min read

1. Clarify the requirements

Ask what kind of document it is and how people use it. Rich text, spreadsheets and drawings need different operation types, and "must work offline" changes the core algorithm.

  • Functional: several people edit the same rich-text document at once; everyone sees others' edits, cursors and selections; edits made offline merge when the device reconnects; per-user undo; version history; owner, editor, commenter and viewer roles.
  • Non-functional: every replica ends up with the same content (convergence); an acknowledged edit is never lost; your own typing feels instant; other people's edits appear within a few hundred milliseconds.
  • Out of scope unless asked: comments and suggestions, search across documents, export formats.

2. Estimate the scale

These figures are assumptions picked for easy arithmetic, not measurements of any real product.

Back-of-envelope estimate for a large document editor (assumed figures)
QuantityAssumptionResult
Concurrent editors1 million people with a document open at peak1 million WebSocket connections
Open documents500,000 at peak2 editors per open document on average; most documents have one
Operations1 batched operation a second per connected editor (typists send more, readers none)about 1 million operations a second in, about 1 million broadcasts out
Daily log volume300,000 operations a second averaged over the day, 100 bytes eachabout 26 billion operations, about 2.6 TB a day before compaction
Gateways50,000 connections per gateway serverabout 20 gateway servers at peak
Session servers20,000 operations a second each; 100 KB in memory per open documentabout 50 servers, holding about 50 GB of documents in total
Back-of-envelope estimate for a large document editor (assumed figures). Scroll sideways on a small screen.

The conclusions: storage is not the problem; ordering many small writes and fanning them out quickly is. One document's traffic is tiny, so a single server per document is enough, which makes ordering easy.

3. Define the API

GET /api/v1/docs/{docId}
  returns: 200 { "version": 1203, "snapshot": { ... }, "role": "editor" }

WS  /api/v1/docs/{docId}/live?since=1203
  client -> server  { "type": "op", "clientId": "c42", "seq": 17, "baseVersion": 1203,
                      "ops": [{ "retain": 120 }, { "insert": "hello" }] }
  server -> client  { "type": "ack", "seq": 17, "version": 1204 }
  server -> others  { "type": "op", "version": 1204, "clientId": "c42", "ops": [ ... ] }
  both ways         { "type": "presence", "userId": "u9", "cursor": { "anchor": 125, "head": 125 } }

GET  /api/v1/docs/{docId}/history?from=1000&to=1204
POST /api/v1/docs/{docId}/permissions   { "email": "ana@example.com", "role": "commenter" }
A REST call to load a document, then one WebSocket per open document. Operations shown in OT style.

Each client sends at most one unacknowledged batch at a time and buffers later edits until the acknowledgement arrives. That keeps the transformation logic on both sides small. clientId plus seq lets the server drop a batch it has already applied when a client resends after a reconnect.

4. Sketch the data model

What is stored per document
TableKey fieldsWhy it is there
documentsdoc_id, owner_id, title, latest_snapshot_versionThe document's identity and where loading starts
doc_ops(doc_id, version) primary key, client_id, client_seq, user_id, ops, created_atThe append-only log; unique (doc_id, client_id, client_seq) makes resends harmless
snapshots(doc_id, version), pointer to the serialized documentFull state every N operations, so loading never replays the whole history
permissionsdoc_id, principal_id, roleChecked when a session opens and on every write
Routing (coordination service)doc_id to session server, epochWhich server owns the document right now
What is stored per document. Scroll sideways on a small screen.

Presence (who is here, where their cursor is) lives only in the session server's memory. Persisting every cursor move would multiply writes for data nobody needs tomorrow.

5. The core decision: OT or a CRDT

Both approaches make concurrent edits converge. They differ in how they describe a position in the text and in whether they need a central ordering point.

Operational transformation. Operations use integer positions relative to a specific version. When an operation arrives based on an older version, the server transforms it against each operation applied since, so it still means what the author intended. Take the text cat. Ana inserts s at position 3; at the same moment Ben deletes position 0. The server applies Ana's first, giving cats at version 1. Ben's delete was based on version 0; Ana's insert came after position 0, so the delete stays at 0, and the result is ats. Ben's client, which already shows at, receives Ana's insert transformed against his delete: position 3 becomes 2, giving ats. Both converge.

With a central server that fixes a single order, the transform function only has to handle two concurrent operations correctly. Without a central server, OT needs extra properties that are hard to get right, and several published peer-to-peer OT algorithms were later shown to diverge in some cases. That is why practical OT systems put a server in the middle.

CRDTs (conflict-free replicated data types). Each inserted character gets a permanent unique id, such as (replica id, counter), and an insert says "after the character with id X" rather than "at position 3". Deleted characters usually stay as tombstones so later operations can still refer to them. The merge rules are commutative and idempotent, so any two replicas that have received the same set of operations hold the same text, whatever the order. Operation-based CRDTs rely on the sync layer to deliver each operation in causal order (and to ignore repeats); state-based CRDTs merge whole states and tolerate any delivery order.

OT and CRDTs side by side
OT with a central serverCRDT
Needs a central orderYesNo; a server is still useful for storage, access control and relay
Metadata costSmall: integer positionsAn id per character plus tombstones; modern libraries compress runs of characters
Long offline editsWorks, but a long offline branch must be transformed against a long server historyMerges naturally; this is the main reason to choose one
Where the complexity sitsTransform functions for every pair of operation typesData structure design, memory use, garbage-collecting tombstones
Open-source examplesShareDBYjs, Automerge
OT and CRDTs side by side. Scroll sideways on a small screen.

A reasonable default for an online-first product is OT with one session server per document, because the server is already the source of truth. If the interviewer stresses offline-first or peer-to-peer use, switch to a CRDT; the rest of the architecture barely changes, except that the server merges and relays instead of transforming. Neither approach understands meaning: if two people retype the same sentence differently, both edits survive and a human tidies up.

6. The architecture

  1. Clients
    • Editor with local document copy
    • Pending-operation queue in local storage
  2. Edge
    • Load balancer
    • WebSocket gateways (auth, connection state)
  3. Routing
    • Document-to-server routing with leases and epochs
  4. Session tier
    • Session server per document (order, transform, presence)
    • Permission check
  5. Storage
    • Operation log, keyed by document and version
    • Snapshot store (object storage)
    • Document and permission metadata
  6. Async
    • Snapshot worker
    • Version history service
    • Search and export indexers
Path of one edit from a keystroke to every collaborator. Browser and app clients apply edits locally and keep a pending queue in local storage. They connect through a load balancer to WebSocket gateways that terminate connections and check authentication. Gateways look up the document's owner in a routing service and forward messages to that session server. The session server holds the document in memory, checks permissions, transforms or merges the operation, appends it to the operation log, acknowledges the author and broadcasts to every other gateway with a client on that document. A snapshot worker writes full document state to object storage every N operations, and a history service serves version history from the log and snapshots.
  1. Apply locallyThe client applies the edit to its own copy at once, then sends it with its base version.
  2. OrderThe session server transforms the operation against anything newer than its base version and assigns the next version number.
  3. PersistIt appends the operation to the log with a conditional write on (document, version) and only then acknowledges the author.
  4. Fan outIt broadcasts the operation to the other clients, which transform it against their own unacknowledged edits and apply it.

7. Scale it and handle the hard features

  • Shard documents to session servers. Use consistent hashing on doc_id or an explicit routing table with leases. One writer per document keeps ordering trivial; capacity grows by adding servers. See sharding strategies.
  • Snapshots. Write a snapshot every 1,000 operations or 5 minutes (assumed tuning). Load is snapshot plus tail, and old operations can be compacted into named versions for history.
  • Presence. Send cursor updates at most every 50 to 100 ms and transform cursor positions like operations (with a CRDT, anchor them to character ids). Drop a user's presence when their connection closes or a short timeout passes.
  • Offline edits. The client keeps unsent operations in local storage with their base version. On reconnect it sends them; the server transforms them against everything since (OT) or merges them (CRDT). If the needed history has been compacted away, fall back to a three-way merge against a snapshot, and never drop the user's work silently.
  • Undo. Undo is per user and never rewrites history. The client takes the inverse of the user's last operation, transforms it against everything applied since, and sends it as a new operation. With a CRDT, undo is a new operation that deletes what you inserted or restores what you deleted.
  • Permissions. Check the role when a session opens and on every write. When a role is downgraded, the session server pushes the change and starts rejecting operations from that user at once.
  • Very busy documents. For thousands of viewers, broadcast through a separate fan-out tier, cap simultaneous editors and batch operations.
  • Regions. One session server per document means collaborators on another continent see edits a cross-region round trip later, often 150 ms or more. Place each document's server near most of its editors and move it when that changes. Consistency comes first here; see consistency and CAP.

8. What breaks, and what you do about it

Failure modes worth naming
FailureEffectMitigation
Session server crashesOpen documents on it pause; unacknowledged operations are lost on the serverRouting moves the document; the new server loads snapshot plus log; clients resend unacknowledged operations, deduplicated by client id and sequence
Two servers own one documentTwo different operations claim the same versionOwnership epochs; the conditional append on (document, version) lets only one succeed
Gateway diesThousands of clients reconnect at onceReconnect with exponential backoff and jitter; clients rejoin with their last acknowledged version
Slow clientIts outbound buffer grows on the serverBound the buffer; when it overflows, close the socket and let the client reload from a snapshot
Bad snapshotEvery load starts from a wrong stateKeep earlier snapshots and the log; periodically rebuild a snapshot by replay and compare checksums
Failure modes worth naming. Scroll sideways on a small screen.

9. Trade-offs to discuss

  • OT or CRDT. OT is lighter on metadata and well proven with a central server; CRDTs remove the ordering point and handle offline merges, at the price of per-character metadata.
  • Persist before broadcast. Persisting first adds a log write (a few milliseconds) to the collaborator's latency but means nobody sees an edit that could still be lost. Broadcasting first is faster and occasionally shows text that later disappears.
  • Batch size. Sending every keystroke gives the smoothest remote view but the most messages; batching for 50 to 100 ms cuts message volume sharply at a delay people barely notice.
  • Snapshot frequency. Frequent snapshots make loads and recovery fast but cost storage and write bandwidth.
  • Rich text. Formatting spans (bold that two people extend at once) need their own merge rules in either model; name this as the next hard problem.

10. How to talk through it in the interview

Fictional example

Explaining why a central server makes OT easier
Interviewer
Why does every editor of a document go to the same server?
Candidate
Because it gives every operation a single version number. With one order, the server only transforms an incoming edit against the edits it has applied since the client's base version, and each client does the mirror image for its own pending edit. Without that order I would need a CRDT.
Interviewer
And when that server dies?
Candidate
Nothing acknowledged is lost, because I append to the log before I acknowledge. The routing service gives the document to another server with a higher epoch, it loads the latest snapshot and the log tail, and clients resend whatever was not acknowledged.
  • Walk through one tiny concurrent edit, like the cat example, on the whiteboard. It proves you understand the merge model.
  • Say clearly which guarantee each piece gives: convergence from the merge model, durability from the log, ordering from the single session server.
  • Keep presence separate and cheap, and say why.

Practicing this design with Natively

Run the walk-through as a mock interview: ask a friend to play the interviewer on a call, or read the prompt aloud yourself. Natively transcribes the conversation as it happens and can suggest talking points for each question as it comes in; comparing them with what you said is a quick way to spot gaps such as offline reconciliation or undo. If you sketch the OT example or the architecture, you can capture a screenshot of it and have Natively explain it. After the session, the transcript and notes show where you ran long and what you skipped.

Questions people ask

What is the difference between OT and CRDTs?

Operational transformation uses integer positions and rewrites an incoming edit against concurrent edits the sender had not seen, which is straightforward when a central server fixes one order. A CRDT gives every character a unique id and uses merge rules that converge in any delivery order, so it needs no central order and handles offline edits well, at the cost of extra metadata per character.

Why route all editors of a document to one server?

A single session server gives every operation one version number, which makes ordering and OT transformation simple. One document's traffic is small, so one server can handle it, and capacity grows by spreading documents across many servers.

How does undo work in a collaborative editor?

Undo is per user and is sent as a new operation, never as a rewrite of history. The client takes the inverse of the user's last edit, transforms it against everything applied since, and submits it. In a CRDT, undo is likewise a new operation that removes what you inserted or restores what you deleted.

Where to go next

Practice with the same assistant you would use on a call: it listens, transcribes and suggests answers from your own resume and the job description.

Facts on this page were last checked on . Product details come from the Natively app itself; if something here is out of date, tell us at hello@heynatively.com.

Keep reading

Put it on your own machine.

A live-conversation assistant that runs locally. Your conversations stay on your machine.

macOS 12+ · Windows 10/11

© 2026 Natively AI Private Limited

Source-available under the Natively Personal Use Source License v1.0 · IP geolocation by DB-IP