Problem and Applicable Scenarios
Design a real-time collaborative editor that supports paragraphs, headings, lists, bold text, and comment anchors. The system has 20 million daily active users and maintains 2 million connections at peak, with 200,000 users actively typing. An active editor produces two updates per second on average, and one hot document may have 100 concurrent editors. Online collaborators should see a remote edit within 200 milliseconds at p95. A user may edit offline for up to 24 hours and merge after reconnecting. An edit acknowledged by the server must not be lost. Cursors, selections, and online status may be briefly lost.
The system also supports viewer, commenter, and editor permissions, permission changes, version history, per-user undo, and multi-region access. The interview does not require proving a CRDT or OT algorithm from scratch or reproducing any company's private architecture. The candidate should choose a conflict-resolution model, connect it to the editor data structure, transport, persistence, and authorization boundaries, and state what the design cannot guarantee.
Current English and Chinese interview material from 2026 presents collaborative editing as a system design problem spanning OT versus CRDT, WebSocket connections, document rooms, cursor presence, offline editing, durable updates, and snapshot recovery. The Yjs documentation supplies primary evidence for commutative, associative, and idempotent updates, state-vector delta sync, and non-persistent awareness. That combination makes the prompt representative and technically verifiable.
What the Interviewer Is Evaluating
First, does the candidate solve concurrent convergence instead of stopping at “use WebSockets”? WebSocket provides a bidirectional channel. It does not decide the result when two users insert at the same position. A strong answer compares a server-mediated OT model with a CRDT model and chooses one for the stated constraints.
Second, does the answer separate local experience from the server's durability promise? Typing must apply locally before a network round trip. “Acknowledged means not lost” requires the server to replicate an update durably across availability zones before returning an ack. Until then, the client keeps the update in a pending queue and retries with the same operation identity.
Third, does the design separate durable content from ephemeral presence? Document content, comment anchors, and version history must recover. Cursor movement is high-churn and expires after disconnect. Persisting every cursor movement in the document log increases cost and pollutes recovery with stale state.
Fourth, can the candidate reason about rich-text semantics? A convergent character sequence does not automatically make a document tree valid. Lists, tables, comment anchors, schema upgrades, and undo scope need explicit models, versions, and invariants. Plain integer offsets also move as soon as a remote user inserts before them.
Finally, do capacity, hotspots, multi-region ownership, permissions, and verification form a closed loop? A strong answer calculates write load and per-room fan-out, bounds slow clients, explains why revoked offline edits cannot simply merge, and uses reordered updates, duplicates, partitions, and failover to prove convergence and durability.
Clarifying Questions Before Answering
- What is being edited? The primary design uses a structured rich-text tree. Image bytes live in object storage; the document contains references and attributes.
- What is the product meaning of a conflict? Concurrent updates converge deterministically without overwriting each other. The system does not infer the business intent of two contradictory sentences.
- What does acknowledged mean? The server acks only after the update enters a durable log replicated across three availability zones in the home region.
- Is a global linear order required? No. Content converges through the CRDT. A log offset exists for audit, recovery, and acknowledgement watermarks, not merge correctness.
- How long may a client remain offline? Up to 24 hours. It retains local CRDT state and unacknowledged updates, then reauthorizes before exchanging deltas.
- What are the permission levels? A
viewerreads, acommenterchanges only the comment domain, and aneditorchanges content. Join, reconnect, and every write are authorized. - Is cursor state durable? No. Presence uses heartbeats and a TTL. A lost cursor update is replaced by the next state update.
- May several regions accept writes for the same document? The primary design assigns each document a home region and forwards writes there, simplifying authorization, audit, and failover.
- How long is version history retained? Assume user-visible versions are retained for 30 days. They are distinct from compacted snapshots used for online sync.
- Is end-to-end encryption in scope? The main design lets the server validate permissions and content schema. End-to-end encryption is a follow-up trade-off.
30-Second Answer Framework
“I would use a server-relayed CRDT: apply edits locally, reauthorize each WebSocket update, ack only after cross-zone durable persistence, then broadcast by document room. Reconnects use state vectors for missing differences or load a snapshot; durable content and TTL-based presence stay separate. Peak load is 400,000 updates or about 120 MB/s. A 100-editor hot room creates about 19,800 remote deliveries per second, so gateways batch updates and bound slow-client buffers. I would test reordered and duplicate updates, 24-hour offline reconnects, permission revocation, and home-region failover for convergence and durability.”
Step-by-Step Deep Dive
Start with seven invariants:
- Local input never waits for the network, and replicas that receive all updates converge to the same valid document.
- The server acknowledges a content update only after cross-zone durable persistence succeeds.
- Retries, duplicate broadcasts, and reordered delivery cannot apply an edit twice or change the final result.
- Current identity and permissions come from the server session, never from a role inside the update payload.
- Content, comments, and version history recover; cursors and online status may expire.
- A schema-incompatible client cannot continue writing unknown structures.
- Every drop, rejection, degradation, and recovery outcome is observable.
Step one: choose between OT and CRDT.
OT commonly uses an ordered server version as context and transforms each incoming operation against concurrent operations. It fits a server-authoritative system with an established transformation engine, but transformation functions, retained history, and offline rebasing must all be correct. A CRDT encodes concurrency in the data structure. Updates may arrive in different orders and more than once, and replicas converge after receiving all of them. The costs are causal metadata, deletion markers, complex rich-text bindings, and separate handling for authorization and semantic conflicts.
Because the prompt requires 24-hour offline editing and multi-region access, the primary design chooses a mature sequence/tree CRDT with a server relay and durable persistence. CRDT does not require a decentralized deployment and does not remove the server. The server still owns identity, schema validation, size limits, durable acknowledgement, history, compliance, and room fan-out.
Step two: define the structured document and message contract.
The document root contains blocks with stable IDs. Paragraphs, headings, and list items own CRDT text and formatting attributes. Comment threads live in a separate domain and anchor to ranges with relative positions. A schema version defines allowed nodes, attributes, and migration rules. An integer offset is unstable: an insertion before it changes what it points to. A relative position attaches to a CRDT element and resolves consistently after replicas converge.
The WebSocket subprotocol can use these application messages:
join {
documentId, sessionId, schemaVersion, stateVector
}
update {
documentId, clientId, clientSeq, schemaVersion, payload
}
ack {
clientId, clientSeq, durableOffset
}
sync {
payload, durableOffset, schemaVersion
}
presence {
sessionId, relativeCursor, relativeSelection, statusSeq
}(documentId, clientId, clientSeq) is the retry idempotency key. durableOffset supports acknowledgements and auditing but does not participate in CRDT merge correctness. Messages have compressed and uncompressed size limits plus a schema allowlist. Unknown nodes or unauthorized domain changes are rejected explicitly instead of being broadcast.
Step three: separate loading, live rooms, and storage paths.
Client
-> HTTPS snapshot service -> metadata + snapshot store
-> WebSocket gateway -> document room router -> collaboration service
-> durable update log
-> room pub/sub -> gateways
Client
-> presence channel -> regional ephemeral store -> room fan-outThe client first loads document metadata, the current schema, and a recent compacted snapshot over HTTPS, then opens a WebSocket. The gateway owns connections and room subscriptions but does not invent merge rules. The collaboration service routes by document to its home region, authorizes and validates each update, persists it, acks it, and publishes it. The room bus publishes once to every gateway with subscribers, and each gateway fans out locally instead of sending one cross-node message per recipient.
Step four: make local editing, acknowledgement, and retry an explicit state machine.
A local edit updates the view immediately and enters a locally persistent pending queue. While online, the client may batch adjacent keystrokes over a 20-to-50-millisecond window to reduce message count. That timing is an input assumption and must be tuned with interaction tests. For each update, the server authenticates the session, reads current permission, validates schema and resource limits, appends to the replicated log, returns an ack, and broadcasts. Only the ack removes the update from the pending queue.
If the connection fails after commit but before the ack, the client retries the same clientSeq. A server uniqueness constraint can return the original acknowledgement. CRDT idempotence also makes a repeated apply safe, but audit and billing records still need deduplication. A slow client receives a bounded send buffer. After the high-water mark, the gateway drops presence first and then asks the client to resync from a state vector rather than letting one connection consume unbounded room memory.
Step five: use state vectors for online, offline, and reconnect synchronization.
Commutative, associative, and idempotent CRDT updates let replicas receive updates in different orders and retry safely. On reconnect, a client sends a state vector describing the causal state it already has, and the server computes the missing difference. If the delta is too large, the schema changed, or the online history window expired, the server sends the current full CRDT snapshot and then applies still-authorized local updates.
The order is authenticate, authorize, negotiate schema, then exchange content. If edit permission was revoked while the user was offline, convergence is not permission to accept the changes. The server returns a stable permission_revoked error and keeps a local recovery path for export or copy, but it does not write the draft to the shared document. The product sacrifices “every offline edit merges” to preserve current access control.
Step six: design snapshots, version history, and compaction.
The durable log is partitioned by documentId and records update ID, author, schema, payload, receive time, and durable offset. A background compactor loads the CRDT state, combines increments into a directly loadable snapshot, and records the covered offset. It removes old increments only after the new snapshot passes validation, is durable in object storage, and retains a rollback point for the recovery and audit window.
Merging binary updates removes duplicate information but does not itself garbage-collect deleted content. Deletion markers, offline synchronization, comment anchors, per-user undo, and historical versions interact. Compaction therefore needs real document tests. User-visible history stores independent named snapshots or a change index; online sync compaction is not a substitute for product version history.
Step seven: separate presence and control hotspot fan-out.
Presence contains a session, display name, color, relative cursor, selection, and monotonic statusSeq, refreshed by heartbeat and TTL. It never enters the document CRDT or durable log. Receivers discard older sequence numbers, and disconnect state expires. Losing one cursor update has only a transient effect because the next update replaces it.
A 100-editor room at two updates per second produces 200 content updates per second. Sending each update to the other 99 users means about 19,800 remote deliveries per second before presence. Gateways batch updates from a short time slice, publish by document room, cap presence frequency, and enforce byte and message watermarks per connection. An extremely hot document may receive a dedicated room actor and pub/sub partition, but the system never samples durable content edits to reduce load.
Step eight: recalculate global capacity and connection resources.
Two hundred thousand editors at two updates per second produce 400,000 updates per second. At an average 300-byte binary payload, raw writes are about 120 MB/s or 10.37 TB/day. Replication, indexes, headers, snapshots, and version history are extra. If a gateway connection occupies 50 KiB, 2 million connections need about 100 GiB of gateway state. At 20,000 connections per gateway, the baseline is 100 gateways before failure and deployment headroom.
These estimates are starting points. Hot-room egress, TLS and encoding CPU, slow-client buffers, and durable-log partitions may become bottlenecks before raw content storage. Monitor update and fan-out rate by document and tenant, ack latency, state-vector delta size, reconnects, buffer watermarks, snapshot age, and convergence-check failures.
Step nine: constrain multi-region behavior, failover, and security.
Document metadata records a home region and increasing epoch. A client enters through a nearby gateway, while content writes route to the home region. Immediate local apply hides the cross-region RTT from typing. If the home region fails, the control plane first advances the epoch, then the new region restores from the replicated log and latest snapshot. The persistence layer rejects the old actor's epoch so two room owners cannot both issue durable acknowledgements.
CRDT convergence does not replace one authorization decision, durable acknowledgement, or failover fencing. Security also requires document, update, rate, and decompression-ratio limits; Origin, session, and document authorization checks; encryption in transit and at rest; auditing for sharing, permission changes, and exports; and logs that omit access tokens, document bodies, and precise cursor details.
Step ten: prove the design with property tests and fault injection.
Generate updates from several clients over the same initial document. Apply them in random order, with duplicates, delays, and batches. Every replica must finish with the same serialized result and a valid schema. Add same-position inserts, overlapping deletes, deleting a parent while editing its child, concurrent formatting and text, comment anchors, per-user undo, and schema upgrades.
Kill the collaboration service after durable append but before ack; the retry must produce one audited update. Kill it after ack but before broadcast; recovery must deliver the update. Reconnect after 24 offline hours and exercise both state-vector delta and snapshot paths. Revoke permission before submitting offline updates; shared persistence must reject them while preserving local recovery. Finally, load-test a 100-editor hot room with content and presence, verifying p95 latency, memory watermarks, and the intended degradation order.
High-Quality Sample Answer
“I would begin with three contracts: local input applies immediately; replicas that receive all updates converge; and only an update durably logged across three availability zones is acknowledged. The 24-hour offline requirement favors a mature structured CRDT, while the server remains authoritative for authentication, schema, persistence, and fan-out.
After loading a CRDT snapshot, the client joins a document room. A local edit applies first and enters a pending queue under clientId + clientSeq, then travels over WebSocket as a binary update. The collaboration service rechecks current permission, schema, and resource limits, durably appends the update, acks it, and publishes it to the room. A retry keeps the same sequence. Updates tolerate reordering and duplication. On reconnect, the client sends a state vector and receives missing changes or a full snapshot. If permission was revoked offline, the shared write is rejected and only a local export remains available.
Content and presence use separate paths. Cursors use relative positions, while TTL-based presence is not durably logged. Each document has a home region and epoch. Failover advances the epoch before restoring from the replicated log and snapshot, so the old room owner cannot continue acknowledging updates.
At peak, 200,000 editors times two updates per second equals 400,000 updates per second. At 300 bytes, that is 120 MB/s and 10.37 TB/day of raw updates. A 100-editor hot document creates about 200 updates and 19,800 remote deliveries per second, so the room bus publishes once, gateways batch and fan out locally, and slow-client buffers are bounded. I would prove the system by permuting and duplicating updates until every replica converges, then inject crashes around the ack boundary, 24-hour offline reconnects, permission revocation, schema upgrades, and home-region failure.”
Common Mistakes
- Saying only “use WebSockets” → Transport does not resolve concurrent edits → Choose OT or CRDT and explain convergence and cost.
- Saving the entire document for every edit → Concurrent users overwrite each other and bandwidth grows with document size → Send mergeable incremental updates.
- Acking after an in-memory receive → A process crash loses acknowledged work → Ack after cross-zone durable append.
- Treating CRDT as authorization → Mathematical convergence does not stop a revoked user → Authorize join, reconnect, and every write.
- Saving cursors as integer offsets → Concurrent inserts move the intended position → Use positions relative to CRDT elements.
- Persisting cursor motion in the content log → High-churn ephemeral state inflates storage and recovery → Use a TTL presence channel.
- Claiming exactly-once delivery → Reconnects and broadcasts can duplicate → Use idempotent updates, unique identities, and replayable state.
- Deleting all history immediately after a snapshot → Offline sync, rollback, undo, or schema migration may lose required context → Collect only beyond validated recovery watermarks.
- Calculating only content ingress → Hot-room fan-out and slow clients often exhaust resources first → Calculate delivery, egress, and buffers too.
- Accepting active writes everywhere without a control plane → Authorization, ack, and failover ownership become ambiguous → Use a home region and epoch.
Follow-Up Questions and Responses
Follow-up one: Why choose CRDT instead of OT?
The 24-hour offline requirement and reordered, duplicate multi-region transport align with the CRDT update model, and a mature implementation can exchange differences with state vectors. OT is still valid. With a proven server transform engine, one ordered edit service, and limited offline behavior, it may provide more controlled metadata and server semantics. The decision comes from product constraints and team capability, not from declaring one acronym universally newer.
Follow-up two: Does CRDT remove the need for a durable update log?
No. CRDT solves merging and convergence. Acknowledged durability, audit, history, recovery, and new-device loading still need durable state. The system can compact increments into snapshots and collect old log entries after the recovery window, but it must preserve a verifiable durability boundary and rollback point.
Follow-up three: What if an offline user's permission was revoked?
Reauthenticate and authorize before exchanging content. Reject the shared write with a stable error and keep a local copy for export or copy; do not silently discard it. A business may let an administrator review the draft in an isolated approval area, but that path cannot bypass the current document ACL.
Follow-up four: How does undo work with several users?
By default, undo the current user's most recent local operations and merge the inverse operations through the CRDT. Restoring a whole old snapshot would erase other users' later work. Record the user and transaction boundary for each operation, retain deleted content for the target undo window, and define reversible semantics in the editor binding for structural changes.
Follow-up five: How do cursors avoid jumping after concurrent edits?
Store cursor and comment anchors relative to CRDT elements rather than absolute character offsets. Resolve the relative position to the current index after applying remote updates. If both the anchor and parent structure were deleted, hide the cursor, mark the comment as detached, or fall back to the nearest valid block according to an explicit product rule.
Follow-up six: What if ten thousand people open one document?
Separate editors from read-only viewers. Publish each content update once on the room bus and fan out from gateways. Read-only clients may receive merged updates at a lower frequency or poll short-lived snapshots without changing editor correctness. Presence shows only visible or sampled participants, and every connection has a bounded send buffer. Showing all ten thousand cursors would need its own bandwidth and UI budget.
Follow-up seven: Can the editor support end-to-end encryption?
Clients can encrypt CRDT updates while the server relays and stores ciphertext. The trade-off is that the server can no longer validate rich-text schema, search content, moderate, perform fine-grained exports, or recover lost data easily. Key rotation, member removal, and old members' offline updates also become harder. Define the threat model and document, device, and membership key protocol first; TLS around WebSocket alone is transport encryption, not end-to-end encryption.
Follow-up eight: How do you detect a silent replica fork?
After a quiet period or reconnect, a client reports its state vector and a non-sensitive state digest. Compare only replicas at the same durable watermark; a mismatch triggers full resync and retains a diagnostic sample. Continuous canary documents issue known concurrent updates from several regions and verify final digest, schema, ack count, and log watermark. Comparing digests at different in-flight watermarks would create false alarms.