In this article
Before you start
This page belongs to the system design interview guide.
Why interviewers ask this
Candidates report this prompt in several forms: a Google Docs design, a Notion page with many editors, a block editor for 100 people with a tight sync target, a sync engine for an issue tracker, and a conflict-resolution redesign for a cloud drive.
The consequence of a vague answer is plain: "use WebSockets" describes a pipe, not a design. Interviewers use the question to see whether you separate transport (how bytes move) from consistency (what happens when two edits collide), and whether you can size the load. A weak answer names a technology. A strong answer picks one place where order is decided, says what the merge rule is, and states what breaks when a connection drops.
Collaborative editing from first principles
- Operational transformation (OT)
- Operational transformation (OT) is a technique that adjusts an incoming edit, such as "insert a letter at position 5", to account for edits that happened since the sender last saw the document, so every copy ends the same. It is usually run with a server that fixes the order.
- Conflict-free replicated data type (CRDT)
- A conflict-free replicated data type (CRDT) is a data structure designed so that copies which receive the same edits, in any order, always converge to the same state (operation-based CRDTs also assume causal delivery), because each edit carries enough identity to be merged without a central referee.
- Optimistic update
- An optimistic update is a change a client shows on screen straight away, before the server has confirmed it, on the bet that the server will accept it.
In plain words
Picture four people editing one printed page with pens. Trouble starts when two of them write on the same line at once. A referee who decides whose mark goes first fixes it. The analogy breaks here: real editors hold their own copy and keep typing while messages are in flight. So the referee must also rewrite positions, not just pick a winner.
In one precise sentence, every editor's copy must converge to the same text, and each editor sees their own typing at once.
Step 1: One server per document gives every edit a single order
A collaborative editor needs one place that decides the order of edits to a document. The simplest place is one server process per document. Figma's engineers describe clients that talk to a cluster of servers over WebSockets, with a separate server process for each multiplayer document that everyone editing it connects to (Figma, 2019).
Input: edits from many editors. Output: one ordered list. That list answers the tied-position question: whichever edit reaches the server first goes first, and later edits are adjusted to fit. A router in front maps a document identifier to its server. Interviewers sometimes add that each document is handled by one server only, with round-robin load balancing; round-robin alone sends editors of one document to different servers, so keep round-robin only for a stateless gateway tier and route from the gateway to the document server by document identifier, through a registry or consistent hashing (see consistent hashing for a distributed cache). You now know where order is decided; next, how editors stay fast.
Step 2: Editors apply changes locally and send them over a WebSocket
An editor that waited for the server before showing each letter would feel broken. So the client uses optimistic updates. Figma's engineers apply a client's own property changes at once rather than wait for the server to acknowledge them, to keep the product responsive (Figma, 2019).
Input: a keystroke. Output: the letter on screen at once, plus an edit message sent over the WebSocket to the document server. A WebSocket, defined in the Request for Comments (RFC) number 6455 (Internet Engineering Task Force (IETF), 2011), suits this because the server can also push other people's edits without being asked, which plain request-and-response cannot do. The WebSocket is only transport. It moves edits; it does not decide what to do when two of them collide. That is the next step. For a related real-time design, see designing a chat system with live comments.
Step 3: A merge rule settles concurrent edits
Concurrent edits are two edits made before either editor saw the other. The merge rule decides the result. Two families are common, described in Ellis and Gibbs (1989) and Shapiro et al. (2011). With operational transformation, the server rewrites an arriving edit against the edits it has already accepted. To know which accepted edits to rewrite against, each client edit carries the revision number of the server state it was based on. With a conflict-free replicated data type, every character or block has a unique identity, so edits merge in any order without a referee.
Input: two edits made from the same starting text. Output: one shared text. Example in words: both editors insert at the start of "cat"; the server accepts A's edit first, then shifts B's position so both letters appear and every copy matches. The two are not strict opposites: Figma's engineers describe drawing on CRDT ideas while keeping the central server as the final authority on order (Figma, 2019). Either choice is defensible if you state its cost.
Step 4: The server broadcasts ordered changes and saves them
After ordering and merging, the server sends the accepted change to every other editor and appends it to a saved log. Sending one change to every other editor is called fan-out. The log lets a late joiner or a reconnecting editor catch up by replaying changes from the last one it saw. A log that only grows would slow every join, so the server also saves periodic snapshots and truncates the log before the latest one. Cursors and presence (who is online, where each caret sits) are small, frequent and disposable, so they travel on the same socket but skip the log.
Input: one accepted change. Output: a message to each other editor plus a durable record. The client then has to reconcile: when a server message arrives while local changes are still waiting for acknowledgement, the client must not let an older acknowledged value overwrite a newer local one, or the screen flickers. Keeping a list of unacknowledged local changes and layering them over the server state solves this. The saved log needs one agreed order of changes; see linearizable consistency.
Step-by-step description
- A router maps each document to one server.
- Editors apply changes locally, then send them.
- The server settles concurrent changes.
- Ordered changes go to everyone and to the log.
- A router sends all editors of one document to the same document server.
- Editors apply changes locally and send them to the server over WebSockets.
- A merge rule settles concurrent changes.
- The server broadcasts the ordered changes and saves them in a log.
Knowing it is half the job. Saying it is the other half.
Interview Clozo is an AI interview copilot for real interview rounds: it hears the question and puts a strong answer on your screen, and you say it in your own words. See the features or how it works.
Start Free Trial →Worked example
Take the prompt of a block editor with 100 concurrent editors per document and sub-200 millisecond sync. Every input below is an assumption for practice, not a measurement: 5 edits per second per editor, 120 bytes per edit, and a server tick of 50 milliseconds.1
The server receives 500 edits per second. Each edit goes to the 99 other editors, so naive fan-out is 49,500 messages per second, about 5.94 megabytes per second of edit payload.1 Each editor would receive 495 messages per second, which is wasteful. Batching edits into one message per tick caps each editor at 20 messages per second, so the server sends 2,000 messages per second in total, about 25 times fewer.1
| Quantity | Value (assumption-based, computed) |
|---|---|
| Edits arriving at one document server | 500 per second |
| Naive outbound messages | 49,500 per second |
| Outbound payload | about 5.94 megabytes per second |
| Messages per editor, unbatched | 495 per second |
| Messages per editor, 50-millisecond tick | 20 per second |
| Total outbound messages, batched | 2,000 per second |
| Latency budget used | 160 of 200 milliseconds |
The latency budget is an interview target, not a sourced fact. Assume 50 milliseconds from client to server, up to 50 waiting for the batch tick, 10 of server work and 50 back: 160 milliseconds, leaving 40.1 The batch wait is a trade: fewer messages for a bounded delay. The editor who typed sees their own letter immediately because of the optimistic update, so the budget applies to what others see.
Text description of the diagram
- The assumed budget is 50 milliseconds from client to server and 50 from server to client.
- Batch wait is 50 milliseconds and server work is 10, so 40 milliseconds of the 200 target remain.
Trade-offs and edge cases
The two merge families trade where complexity lives. Operational transformation keeps stored state small but needs a server to order edits and careful rules for each pair of edit types. A conflict-free replicated data type removes the referee but stores identity metadata for each item, so documents can grow. These are design reasoning, not measured results.
Text description of the diagram
- Operational transformation relies on a server to order edits.
- A CRDT removes the referee but stores identity metadata for each item.
Edge cases to name in a round:
- A server dies. One server per document is a single point of failure for that document. Recover by reloading the saved log on another server and letting clients reconnect and replay.
- Hot documents. One server per document caps the editors one process can serve; the worked example shows why fan-out, not edits, is the cost. Relieve it with a separate broadcast tier that serves read-only viewers, or send presence and cursor updates at a lower rate than edits.
- Editing without a connection. The client keeps its local changes, then sends them on reconnect; the server merges them with what happened meanwhile. A local-first design makes the merge rule do real work here, and long gaps make logs and metadata grow.
- Structured data. A tracker like an issue system has relations such as parents and blockers; the merge rule must preserve invariants, such as no cycles, which plain text merging does not.
For why the slowest few percent of messages, not the average, decide whether a 200-millisecond target holds, see tail latency.
How to answer it in an interview
Design a collaborative editor like Google Docs.
I route each document to one server, so there is one place that orders edits. Clients apply their typing immediately and send edits over a WebSocket. The server merges concurrent edits with operational transformation, or a conflict-free data type if I want no central order, then broadcasts the result and appends it to a log. I batch broadcasts on a short tick to cut fan-out. If a client drops, it keeps editing locally and replays on reconnect. The cost is that one server owns each document, so I would plan recovery from the log.
The spoken structure: say where order is decided, then the transport, then the merge rule, then fan-out and the failure case.
Common mistakes
- "WebSockets handle the conflicts." (wrong) A WebSocket is a transport. (right) "WebSockets move edits; a merge rule such as operational transformation settles conflicts."
- "Wait for the server before showing the edit." (wrong) The editor feels slow. (right) "Apply locally at once and reconcile when the acknowledgement returns."
- "Round-robin balancing across servers." (wrong) Editors of one document land on different servers. (right) "Round-robin only a stateless gateway tier, then route to the document server by document identifier."
- "Last write wins for the whole document." (wrong) It silently discards someone's typing. (right) "Merge at the level of single edits or blocks."
- "I will skip the numbers." (wrong) You cannot tell if the design holds. (right) "I estimate edits per second, fan-out and a latency budget, and label them as assumptions."
Follow-up questions
How do you handle conflicts and WebSockets in a collaborative editor?
Use the WebSocket only to carry edits both ways, and put conflict handling in a separate merge layer on the document server. The server orders edits, adjusts or merges the concurrent ones, and broadcasts the result.
How do you support 100 concurrent editors with sub-200 millisecond sync?
Keep one server per document, apply edits locally, and batch outbound messages on a short tick. In the worked example, an assumed 50-millisecond tick cuts messages about 25 times and keeps the budget at 160 of 200 milliseconds.
How do you keep editing working without a connection?
Let the client store local changes and keep editing, then replay them on reconnect for the server to merge. The merge rule must cope with long gaps, and the client needs a stable identity for each pending change.
What if each document must be handled by one server only, with round-robin load balancing?
Round-robin alone breaks the rule, so add a routing layer that maps document identifier to server, for example by hashing. Keep round-robin for a stateless gateway tier, and route from the gateway to the document server through a registry or consistent hash. Use the system design guide to place this layer in a full design.
Should you pick OT or a CRDT?
Pick operational transformation when a central server per document is acceptable and you want small stored state. Pick a CRDT when editors must merge without a central order, accepting more metadata.
- Route each document to one server so edits get a single order.
- Apply edits locally at once; reconcile when the server replies.
- A WebSocket is transport; conflicts need a merge rule.
- Batch broadcasts to cut fan-out, and state your numbers as assumptions.
- Plan for server loss and for editing without a connection.
Sources
- How Figma's multiplayer technology works — Figma, 16 Oct 2019
- RFC 6455: The WebSocket Protocol — IETF, 1 Dec 2011
- Concurrency Control in Groupware Systems (Ellis and Gibbs) — ACM SIGMOD 1989, 1 Jun 1989
- Conflict-free Replicated Data Types (Shapiro et al.) — Inria HAL, 1 Jul 2011