System-design interview · Extended interviews
Design a collaborative text editor
Design an editor that displays local typing immediately, merges concurrent edits, acknowledges durably saved operations and reconnects without losing edits or bypassing permissions.
You will learn to
- Show why arrival-order text replacement loses edits and transform two concrete operations.
- Separate local responsiveness, convergence, durable acceptance, and user intent.
- Recover document sessions from snapshots and operation history without replaying duplicate edits.
Practice in this chapter
8 interview questions with model answers and follow-ups.
Go to interview practiceUseful foundations: Real-time communication: polling, long polling, SSE, and WebSocket · Quorums, consensus, leases, and fencing · Replication and durability
Workload and timing examples are interview assumptions.
Dotted concept links open the relevant explanation in a new tab.
01Problem and scope
A collaborative editor lets people type immediately, then combines their concurrent edits so all clients reach the same document state. Whole-document last-writer-wins replacement loses independent edits, so clients submit operations against a known revision. In an example, two clients insert X and Y at position 1 in cat; both edits must survive under one deterministic transformation rule. Pending local edits, durable saved state and cursor presence are separate concepts.
For example, send operations describing edits, such as insert X at position 1 based on document version 20. The system needs a rule for combining concurrent operations so every participant reaches the same content. Fast local typing, eventual agreement, and preserving a person's intent are related but distinct promises. Showing another user's cursor is presence; it does not solve conflicting edits.
In the interview I ask, “Must people edit for months while disconnected, or primarily collaborate online?” We choose online collaboration with temporary offline drafts and a stated limit on how old an edit's base version may be for automatic resynchronization. I also ask whether saved means visible on this laptop or durably accepted by the service. We choose a visible distinction between pending and saved. Fast local display must not make an unsaved edit look saved.
A document is the ordering unit. Different documents need not share one global edit order. We support plain text first and defer rich formatting, embedded spreadsheets, and multi-document transactions. Each needs rules for what edits mean and who can make them; adding socket servers does not supply those rules.
02Functional requirements
- Create, open and share. Create a document, open its current accepted content, and invite another client with read or edit permission.
- Edit locally. A typed character such as X appears immediately with pending state. Local display does not mean the server accepted the edit before its reply.
- Track durable save acceptance. An acceptance such as A17/v21 clears the pending marker exactly once. It does not mean every peer has already rendered the edit.
- Resume after reconnect. Replay missing accepted operations after a known version such as v21. Missing history older than the retained boundary cannot be inferred.
- Share document access. A grant to client B permits subsequent authorized opens and edits. It cannot recall content that was already downloaded.
- View presence. Show recent cursors that expire when disconnected. Cursor delivery is not durable editing history.
- Undo an edit. Apply the library's defined inverse semantics. Do not revert the whole document over other users' edits.
Scope and acceptance boundaries
Client A can create a document, invite client B with read or edit permission, open its current accepted content, edit while connected, see remote changes, and resume after a short disconnection. A successful save acknowledgment identifies a durable accepted operation. Closing a tab with pending edits shows an explicit warning unless the browser has persisted the pending buffer under the supported recovery policy.
Undo uses the accepted and pending edit history; it does not upload an old copy of the whole document. If client A undoes X after client B adds Y, the intended result should preserve client B's work under the chosen algorithm. We explicitly test that behavior before promising the feature.
03Non-functional requirements
- Interaction latency. Assume local rendering under 16 ms, same-region edit acceptance p95 below 150 ms, and remote delivery p95 below 300 ms under admitted load.
- Availability. Target 99.9% monthly service availability. Loss of document authority pauses save acceptance; clients may retain local pending drafts but cannot label them saved.
- Durability. Acknowledged edits survive one zone failure. Acceptance follows durable persistence; a locally pending edit may still need retry after a crash.
- Document bounds. For this exercise, choose a 1 MB maximum document and 100 active participants. Rich formatting, tables, comments and months of offline editing need additional semantics.
- History and reconnect. Retain accepted operation history for 30 days. Automatic reconnect is guaranteed only within the retained transformation boundary, returned as a version rather than inferred from the calendar.
- Authorization. Check each accepted edit, not only socket establishment. A revocation committed before that edit's authorization transaction causes rejection.
- Agreement during partitions. Prefer agreement and durable authorization over accepting writes on both sides. Local text remains editable with honest pending status.
Baseline and convergence model
A snapshot stores the complete accepted document at a chosen version. The operation log stores accepted edits in order; recovery loads a snapshot and replays later edits instead of rebuilding the document from its first keystroke.
Support plain text, simultaneous online edits, temporary disconnections, persisted history and access-controlled sharing. Begin with one owner per active document, a database operation log and snapshots. Clients apply edits immediately as pending and reconcile with accepted operations. WebSocket provides quick bidirectional updates; versioned history provides recovery.
Choose server-ordered operational transformation (OT) for the baseline: adjust the position/meaning of an edit for concurrent edits already accepted. Use a proven full-operation algorithm/library; an insert-only demonstration does not solve the richer features. A conflict-free replicated data type (CRDT) is an alternative whose operations or merge rules make replicas converge after receiving the same updates under its delivery assumptions. Text CRDTs can use stable element identities to combine edits; the later comparison explains their metadata and cleanup costs.
Compaction boundary
Snapshots may compact document content without immediately deleting metadata required to transform supported pending operations. The stated latency and availability figures are exercise targets, not properties supplied by the transport or merge algorithm.
04Capacity estimates
All figures below are assumed workloads; measure operation size, document skew, connection memory and daily duty cycle.
| Estimate | Arithmetic | Consequence |
|---|---|---|
| Active edit ingress | 1,000,000 connected × 0.05 typing fraction (5%) × 2 operations/s = 100,000 operations/s | Partition documents across coordinators |
| Operation payload | 100,000/s × 200 B = 20 MB/s, or 1.728 TB/day if sustained | Before framing and indexes |
| Peer deliveries | Ten other participants per edit = one million deliveries/s | Fanout exceeds ingress |
| Hot document | 100 typists × 2/s = 200 ordered edits/s and about 19,800 peer deliveries/s | Per-document order is a hotspot boundary |
| Snapshot generation | One million documents × 100 KB = 100 GB | Frequency trades write cost against replay |
| Gateway state | One million connections × 32 KB = roughly 32 GB | Before process and encryption overhead |
| Unbounded-client risk | 10,000 slow clients × 10 MB buffer = 100 GB | Cap queued bytes, not only connection count |
| Three log replicas | 20 MB/s × 3 = 60 MB/s payload | Before protocol and index overhead |
| Thirty-day operation history | 1.728 TB/day × 30 = 51.84 TB logical | Sustained rate, not necessarily the daily average |
| Hot-document snapshots | Every 1,000 operations at 200/s = every five seconds; 1 MB / five seconds = 200 KB/s | A count-only trigger can become expensive |
Partitioning and slow clients
To split one document, first define how its parts can be edited independently. Randomly hashing its operations loses the required order. A lagging client should reconnect from a version instead of holding an unlimited stream in server memory.
Adaptive snapshot policy
Snapshot every 1,000 accepted operations or when replay exceeds a byte threshold, with a minimum interval or adaptive replay budget. The average duty cycle may be much lower than the peak; benchmark both rather than treating every connected user as continuously typing.
05APIs and contracts
An operation ID names one edit across retransmissions. Its base version identifies the document state the edit was made against; its accepted version identifies its position in saved history. The protocol needs all three to distinguish retries from new edits and to transform an edit against intervening changes.
| API / event | Example | Meaning |
|---|---|---|
| Open | GET /documents/d7?afterVersion=20 |
Snapshot or missing accepted operations |
| Edit | {"documentId":"d7","actorId":"client-a-phone","operationId":"A17","baseVersion":20,"insert":{"position":1,"text":"X"}} |
Submit a pending edit |
| Acceptance | {"operationId":"A17","version":21} |
Durable order and deduplication identity |
| Presence | cursor and selection update with expiry | Ephemeral collaboration hints |
Open returns {documentId:"d7", snapshotVersion:20, headVersion:20, minTransformVersion:10, coordinatorEpoch:4} plus a connection route. Edit acknowledgments include the operation's canonical transformed representation, accepted version, and operation identity so the sender can reconcile its pending queue.
In the open response, headVersion is the latest accepted version, while minTransformVersion is the oldest base for which the service retains the required transformation history. coordinatorEpoch identifies the current ownership generation; storage checks it so an obsolete coordinator cannot append edits.
The same actor/operation ID and payload return the existing acceptance. Reuse with different content returns a conflict. An out-of-range position or invalid encoding returns a validation error; revoked edit access returns forbidden; a base older than minTransformVersion returns resync_required with a recovery snapshot while preserving the local draft. Backpressure responses tell the client to slow transmission without discarding pending edits.
History pagination requests afterVersion and a bounded throughVersion. The latter freezes the requested upper boundary while edits continue. A socket reconnect is allowed to land on a different gateway: correctness comes from these identifiers and replay, not from a sticky network connection. Presence events carry a session sequence and expiry but do not advance the document's durable version.
Position units are part of the protocol. Choose one documented text operation type and encoding for all clients; this exercise uses Unicode scalar-value offsets, while the ASCII cat example has the same offsets in common encodings. Clients using UTF-16 strings must convert offsets consistently and reject malformed text rather than mixing code units, bytes and displayed grapheme clusters. Bind actor IDs to authenticated sessions and scope operation uniqueness to the document; possession of another actor’s ID is not permission to replay its operations.
06Data model and access patterns
| Stored data | Key and fields | Query |
|---|---|---|
| Document | documentId, owner, headVersion, coordinatorEpoch | Route and enforce append ownership |
| Operation | (documentId,version); unique actor/operation ID |
Ordered replay after a known version |
| Snapshot | (documentId,version), immutable bytes, checksum |
Restore a verified accepted boundary |
| Grant | (documentId,userId), role, policyVersion |
Authorize read or append |
| Client pending buffer | actor ID, operation ID, base, edit | Retry without inventing a new edit |
Document metadata, grants, operation uniqueness, and log append live in the same document-owned transactional shard. Snapshot bytes may live in object storage. After verifying the upload, commit a manifest that names the immutable object and its exact document version. A snapshot cannot claim version 22 while containing only version 21.
The in-memory coordinator state is derived from the snapshot and accepted log. The database is authoritative about what was saved. Presence and gateway connection registries are disposable; losing them may hide a cursor but must not delete text.
Operation records retain transformed operations and the original identity and payload hash. The algorithm may need additional original-operation metadata for reconnect transformations. We retain that explicitly rather than assuming the final text alone encodes the history of every position shift. Garbage collection advances minTransformVersion only when the retention policy allows older clients to use the explicit merge workflow instead of automatic transformation.
Snapshot cleanup must coordinate with upload, publication and reads; age alone cannot determine whether deletion is safe. Before uploading each candidate snapshot, register a staging grant at the document authority: a record that protects the object from deletion while upload and publication are in progress. Publishing verifies the immutable object and atomically transfers its staging grant to a manifest reference. Garbage collection atomically marks an object deleting only when it has no live staging grant, retained manifest or reader pin; publication rejects deleting objects. For a replay or download, record a reader pin that prevents deletion of the chosen snapshot generation until the bounded read finishes; release or safely expire that pin before reclaiming the object. Thus a collector cannot delete an uploaded snapshot between verification and publication, or during a supported read.
07Basic working design
For a small deployment, one application process owns all documents, serves WebSockets, and stores accepted operations in one database. Client A opens d7 at version 20 and sends A17. The process checks permission and operation identity, computes the appropriate transformation against later accepted history, and appends version 21 transactionally. It acknowledges only after the stated durable commit.
The process then updates its in-memory document and broadcasts version 21. The acknowledgment and broadcast can arrive in either order at client A, so the client reconciles by operation identity and version rather than inserting X whenever it receives a packet. A restart reconstructs d7 from its last verified snapshot and the remaining accepted log.
This baseline already needs a proven OT implementation for clients and server. A database transaction gives an order; it does not define how an insert's position changes around another insert or delete. We start with server order to keep the storage and reconnect contract understandable, while leaving the transformation algorithm to a tested implementation.
At low traffic, the same process can also handle presence. Presence remains a separate message type with a short expiry and no durable save acknowledgment. That prevents frequent cursor movement from competing with edit durability unnecessarily.
Local rendering is optimistic; the database commit defines saved.
Read each connection in order
- syncSubmit A17 at base 20Editor clients → Editor / document owner
- syncAuthorize and append version 21Editor / document owner → Document log and grants
- syncDurable acceptance + remote editsEditor / document owner → Editor clients
08Find the baseline flaws
First, one million sockets and one million outbound edit deliveries/s can saturate a single application's network and event loop before the storage write rate becomes the limit. A stalled receiver can accumulate an unbounded outbound queue unless the baseline disconnects it at a byte threshold. Adding RAM delays the failure but does not change that growth rate.
Second, whole-document replacement would lose edits: both users start with cat, client A saves cXat, and client B later saves cYat. Neither a row lock nor last-write-wins recovers X. Database concurrency control orders writes. Edit operations also describe what each person changed, so the transformation algorithm can combine those changes.
Third, a failover creates a hidden split brain. Coordinator A pauses at epoch 4. Coordinator B becomes owner at epoch 5 and accepts version 22. If the storage layer trusts A's stale lease, A can later append its own version 22 or overwrite the head. Routing all new clients to B is insufficient because A still has open connections and buffered writes. The database must check ownership in the same transaction that appends the edit.
A hot single document remains serial even after spreading other documents. Two hundred edits/s may be manageable, but 19,800 peer deliveries/s belongs on gateways, not inside the critical append transaction.
09Improve the design, step by step
First, move sockets to gateway processes. The trigger is connection and fanout load. Gateways authenticate sessions, enforce bounded buffers, and forward edits to the document owner; owners publish accepted operations to the relevant gateways. This spreads network work without creating multiple edit authorities. The cost is an extra hop and reconnect coordination; a gateway can lose notifications, so replay remains mandatory. Direct owner sockets remain preferable for a small service with little fanout.
Second, shard ownership by document. The trigger is aggregate edit CPU or log throughput. A directory maps each document to a coordinator and transactional storage shard. Coordinators handle different documents independently; d7’s edits still follow one accepted order. This improves aggregate throughput but adds ownership transfer and hot-document imbalance. Randomly assigning edits to workers would still require those workers to agree on one order and transform edits against it. Subdocument partitioning is appropriate only after the editing model defines independently mergeable regions.
Third, add replicated durability and checked epochs. The trigger is the requirement to preserve saved edits through process or zone failure. The document store durably replicates append transactions, and its metadata rejects obsolete coordinator epochs. New owners reconstruct committed state before accepting work. This improves recovery safety at the cost of quorum latency and temporary refusal during a partition. An asynchronous replica would reduce acknowledgment latency but cannot support the same acknowledged-edit loss promise.
Fourth, introduce verified snapshots and bounded replay. The trigger is growing restore and reconnect time. A worker captures the document at one accepted version, uploads immutable snapshot bytes, verifies them, and publishes a manifest. History is retained according to the supported reconnect window, not erased just because a snapshot exists. This cuts restore work but adds snapshot storage, version bookkeeping, and orphan cleanup. Replaying the full log remains the simplest choice for short documents with tiny histories.
A fifth component is not automatically necessary. If long offline editing becomes a primary requirement, we evaluate a proven CRDT as a change to the editing model. We do not bolt CRDT metadata onto an OT stream and assume the two protocols become interchangeable.
10Detailed architecture
Connections and document authority
Clients hold accepted content plus a pending-operation buffer. Gateways own connections and ephemeral presence. A routing directory resolves document owners; it does not authorize edits by itself. Each document coordinator reconstructs its state, transforms operations, and submits atomic append transactions to its authoritative shard.
That shard owns document head, coordinator epoch, grants, and operation identity uniqueness. Its synchronous replicas provide the acknowledged durability policy. A committed change stream or replayable publication cursor feeds a fanout service, which sends accepted versions to subscribed gateways. Lost notifications are repaired with versioned replay rather than pretending the publish call shared the database transaction.
Snapshots and retained history
Snapshot workers read the document at one committed version and upload its immutable bytes to object storage. The authority publishes the corresponding manifest only after verification. An operation archive preserves the declared history window; the current coordinator's cache is disposable.
Acknowledgment versus delivery
Synchronous work includes authorization, transformation, append, and acceptance. Remote delivery, presence, snapshots, and history cleanup are asynchronous. The same version may reach client A twice through replay and fanout; identity-based reconciliation is expected behavior. No gateway can declare an edit saved based only on receipt. Gateways and document coordinators can scale separately, while each document keeps one verifiable edit order.
Algorithm and adapter choice
For implementation, evaluate a maintained OT stack such as ShareDB with an appropriate text operation type and persistent adapter, rather than implementing insert/delete transformations from this sketch. Its document synchronization capabilities do not by themselves prove the custom epoch, permission, durability or snapshot-reclamation guarantees above; verify and implement those at the chosen adapter boundary. Yjs is a concrete CRDT alternative, not the OT library used by this selected algorithm.
Connections and delivery scale independently; the document shard remains the accepted-order authority.
Read each connection in order
- sync1. Open / edit / replayEditor clients + pending buffer → Authenticated socket gateways
- sync2. Resolve document ownerAuthenticated socket gateways → Document routing directory
- sync3. Forward identified editAuthenticated socket gateways → Document coordinators
- sync4. Authorize + conditional appendDocument coordinators → Log / grants / epoch authority
- replicationDurable accepted logLog / grants / epoch authority → Synchronous log replicas
- async5. Resume publication cursorLog / grants / epoch authority → Versioned fanout service
- async6. Accepted versionsVersioned fanout service → Authenticated socket gateways
- syncDeliver / repair gapsAuthenticated socket gateways → Editor clients + pending buffer
- syncRefresh expiring cursorsAuthenticated socket gateways → Expiring presence state
- syncRead exact version boundarySnapshot workers → Log / grants / epoch authority
- syncUpload verified snapshotSnapshot workers → Verified snapshot objects
- syncPublish snapshot manifestSnapshot workers → Log / grants / epoch authority
- asyncRetain replay / transform metadataLog / grants / epoch authority → Retained operation history
- syncRestore verified snapshotDocument coordinators → Verified snapshot objects
- syncReplay / transform supported baseDocument coordinators → Retained operation history
11Write path and acknowledgement
Acknowledged edits belong to the durable ordered operation history. Repeating an operation identity returns the same accepted result.
Client A opens
d7, receives snapshot version 20 and edit permission, and connects to the coordinator identified by epoch 4. The client’s cursor updates are separate from document edits.The client inserts X locally and sends A17. The coordinator authenticates the client, checks that A17 was not already accepted, transforms against operations after base 20 if needed, and appends it as version 21.
Only after the log is durable under the stated replica policy does it acknowledge A17 and broadcast the accepted operation. Client A clears the pending marker; client B reconciles the remote operation with the local pending B9.
Client B's B9 becomes accepted version 22. A background task may later create a snapshot exactly at version 22, including
cXYat; newer operations remain in the log.Client A disconnects after receiving 21 but before 22. On reconnect the client requests operations after 21 and receives B9. If the client retries A17 because its acknowledgment was lost, the unique actor/operation identity returns version 21 instead of inserting another X.
If the database commits A17 but the coordinator dies before publishing, the replacement reads A17 from the log and a publication worker resumes from its cursor. Client A's retry returns the existing version. The system must look up duplicates before treating their old base version as an unsupported new edit.
If an edit is refused because permission was revoked, the client preserves its local pending text as a private draft and shows the reason. It does not automatically resubmit through a different user or document identity.
A snapshot at version 22 is verified against the accepted log boundary before its manifest becomes visible. Subsequent operations start replay after 22; no operation is skipped because a snapshot was produced concurrently.
The acknowledgment boundary is the durable log commit, not fanout completion. Requiring every participant to respond would let one disconnected browser stop everyone else's saving.
12Read and delivery path
On reconnect, the client loads an authorized snapshot and replays retained operations accepted after that snapshot. Its local pending buffer does not determine which edits the server has committed.
Read permission is checked again before snapshot download and replay. Short-lived object URLs reduce the lifetime of a granted download, but they cannot retract bytes already stored on a device. The UI makes this practical limit clear when sharing is revoked.
13Correctness deep dive
Both operations start from version 20, text cat, with zero-based character positions. Client A sends A17 = insert(1,"X"); client B sends B9 = insert(1,"Y"). Suppose the server accepts client A first and a defined tie-break rule places A17 before B9 for equal-position concurrent inserts.
The cells show the text after each accepted operation. Positions are zero-based; the agreed tie-break puts A before B.
Remember: After X takes position 1, move Y to position 2.
Read the diagram
- Track the text from cat to cXat to cXYat.
- A and B both submit insertions at position 1 of base text cat.
- Accept A’s X, then transform B’s Y to position 2.
Try from memoryWhat goes wrong if B inserts at position 1 after X without transformation?
The result would be cYXat, contrary to the agreed A-before-B tie-break. Transforming B to position 2 produces cXYat.
| Step | Accepted operation | Result |
|---|---|---|
| Version 20 | Initial content | cat |
| Version 21 | A17 inserts X at position 1 | cXat |
| Transform B9 | client A inserted before client B's target; shift B9 to position 2 | Pending operation becomes insert(2,Y) |
| Version 22 | Apply transformed B9 | cXYat |
The transformation calculation and its log position must be protected from a concurrent append or ownership change. The coordinator may calculate outside a database transaction, but the transaction checks the exact head and epoch it used:
append(doc=d7, ownerEpoch=5, expectedHead=21, edit=B9):
begin transaction; lock document d7
require current grant permits this authenticated actor to edit
if operation identity already exists:
require identical original payload fingerprint
return saved acceptance
require coordinatorEpoch == 5 and headVersion == 21
insert operation B9 at version 22 with transformed payload
update headVersion = 22
commit; return accepted version 22
A failed expected-head check causes the coordinator to reload intervening operations and recompute, not retry the same transformed position blindly. Permission changes use the same document transaction lock. Once a revocation commits, a later append cannot reuse the socket's old permission cache to pass the transaction.
Suppose old owner A prepared B9 under epoch 4 while new owner B advances the epoch to 5. If A's transaction commits first, its operation is part of the committed history B must reconstruct. If the epoch change commits first, A's append fails. The storage lock and conditional append select one order; no two owners independently install version 22. That storage check is what makes the fencing token effective.
Client A’s edit is durable before takeover; the old epoch cannot append client B’s operation afterward.
Read each connection in order
- syncA17: insert X at base 20Client A → Old coordinator
- syncAppend A17; epoch 4, head 20Old coordinator → Document authority
- returnCommitted version 21Document authority → Old coordinator
- syncAdvance ownership to epoch 5New coordinator → Document authority
- syncB9: insert Y at base 20Client B → Old coordinator
- syncAttempt append using epoch 4Old coordinator → Document authority
- blockedReject stale epochDocument authority → Old coordinator
- syncRetry identical B9Client B → New coordinator
- syncTransform; append at head 21 / epoch 5New coordinator → Document authority
- returnCommitted version 22: cXYatDocument authority → New coordinator
- syncAccept B9 / version 22New coordinator → Client B
14Failure and recovery
Different failures threaten different state: a client may lose its connection while its edits remain saved, and a coordinator may lose authority while its process keeps running. Recovery must establish which history and owner are current before it resumes acceptance or replay.
| Failure or race | Required response and boundary |
|---|---|
| Stale coordinator resumes | If coordinator A pauses and B takes ownership with epoch 5, A must not resume appending epoch-4 operations. A fencing token is that increasing epoch checked by the protected log; the log rejects stale owners even if A believes its lease still exists. B reconstructs from a snapshot plus committed operations before serving edits. Routing clients to B alone does not stop A's late writes. |
| Client older than retained history | A long-offline client may reference a base version older than retained transformation history. Return an explicit resynchronization requirement, preserve the user's pending text locally, and use a defined rebase/merge or conflict workflow. Never pretend missing history can be reconstructed from position numbers alone. Permission revocation is checked again at accepted edit boundaries; presence and already-downloaded content have separate revocation limits. |
| Network overload | Under network overload, gateways cap queued bytes per client. A lagging client receives a reconnect requirement and later replays from its last accepted version. The server does not throw away durable edits to make a buffer appear healthy. Presence updates can be dropped or coalesced immediately because only their recent state matters. |
| Document authority unavailable | If the authority loses quorum, typing can continue locally but acceptance pauses. The UI's pending count grows and eventually enforces a local storage limit. Recovery replays saved operations before resubmitting pending ones with their original IDs. A region-wide restore may have a different loss boundary if backups are asynchronous; the claimed one-zone durability guarantee does not silently become zero-loss disaster recovery. |
Alternative merge model: CRDT
A conflict-free replicated data type (CRDT) is a replicated data type whose operations or state-merge rules let replicas converge after receiving the same updates, under the algorithm’s stated delivery assumptions. CRDTs include counters and sets as well as collaborative text structures. A sequence CRDT for text can assign stable identities to content elements: inserts name neighboring element IDs rather than only a shifting numeric position. That can support offline merging, but adds metadata, deletion markers, and garbage-collection constraints for old replicas. Yjs provides a concrete implementation. OT and CRDT are alternatives with full algorithmic contracts, not two labels that automatically make arbitrary edits safe.
Each replica alone increments its own slot. Merge uses the maximum of corresponding slots.
Remember: Maximum per slot, then sum; do not add whole replica totals.
Read the diagram
- Merge [2, 0] and [0, 3] into [2, 3].
- The visible merged total is 5.
- Repeating the same merge still gives [2, 3], so duplicated state does not double-count.
Try from memoryWhat happens if the merged state is received twice?
The component-wise maximum stays [2,3], so the visible total stays 5. Repeated state merges are idempotent.
15Operations, security, and cost
Document content is private data. The gateway validates identity, the owner enforces current grants, and storage credentials are scoped to the required document partitions. Limit document size, operation size, per-user edit rate, and concurrent participants. Avoid putting body text in tracing labels or application logs; operation IDs and versions are sufficient for most diagnostics.
Measure accepted-edit latency separately from local render latency and remote delivery latency. Pending age reveals a saving problem hidden by fast local rendering. Track transform failures, replay bytes, snapshot age, stale-epoch rejections, and fanout buffer evictions. A convergence canary—a small automated correctness test—applies the same generated insert/delete history through different client delivery schedules and compares final accepted content.
Before upgrading an editing library, replay a corpus of concurrent insert, delete, undo, and reconnect histories through old and new versions. Do not mix protocol versions unless their wire semantics are explicitly compatible. During coordinator migration, advance the epoch, reconstruct committed state, and resume; preserve the actor-operation uniqueness records for the supported retry window.
At one million deliveries/s, reducing a 200-byte envelope by 50 bytes saves 50 MB/s before framing, but aggressive batching adds latency. A 20 ms fanout batch can reduce write calls while remaining inside a 300 ms remote-delivery objective. Benchmark that tradeoff on hot documents and slow clients rather than optimizing log storage while network fanout dominates.
16Decision ledger and limitations
OT and CRDT define how concurrent edits combine; replicated storage determines which accepted edits survive a failure. The comparison keeps those responsibilities separate when weighing the chosen online editing model against alternatives.
| Decision | Benefit | Cost |
|---|---|---|
| Server-ordered OT | Explicit accepted order and compact positional edits | Correct transforms and retained history |
| CRDT | Mergeable identified operations | Metadata and cleanup complexity |
| Local pending edits | Responsive typing during latency | Reconciliation and visible pending state |
| Snapshot plus log | Bounded recovery time | Safe snapshot and retention boundaries |
Our chosen OT design favors a compact online accepted order and a bounded supported reconnect window. It pays for transformation history and a coordinator per active document. A CRDT is worth evaluating when offline multi-device editing becomes central, but stable element identifiers, deletion metadata, and garbage collection still need a product contract.
Replicated storage protects saved edits but adds acceptance latency. Local pending rendering masks that latency without removing it. Ephemeral presence saves writes at the acceptable cost of temporarily missing or stale cursors. Snapshots bound replay but cannot erase history still needed by supported pending edits.
The remaining scale limit is a single hot document. More shards help different documents, not the inherently ordered transformations of one document. Before splitting its model, I would measure transformation CPU, group fanout, and batching. If the interviewer demands a million simultaneous editors of one text, the participant and semantic requirements must change substantially.
17Interview closing
“I designed an online plain-text editor where local typing is immediate but saved means durably accepted. Clients submit identified operations rather than replacing the whole document. A proven operational-transformation implementation reconciles concurrent local and accepted edits, while one document owner assigns the durable accepted order. Clients transform pending operations against that order so concurrent work converges without silently overwriting another edit.
“Gateways scale sockets and fanout independently from document coordinators. The log stores operation identities and versions; snapshots reduce recovery work without deleting transformation history prematurely. A coordinator epoch and expected head are checked atomically with each append, so a resumed old owner cannot fork the accepted history. Lost replies are handled by returning the existing operation acceptance.
“The costs are transformation complexity, retained history, and a hot-document ordering limit. I would watch pending age and replay size as carefully as API latency. My next test combines concurrent insert/delete operations with coordinator failover and an acknowledgment loss, then proves every client reaches the same accepted text without applying its own edit twice.”
If the interviewer adds months of offline editing, I would evaluate a proven CRDT and redefine retained metadata and merge behavior. If rich formatting is added, I would extend the supported edit types, their combination rules and compatibility tests before promising that the plain-text example generalizes.
Practise the interview questions
Say your answer aloud before opening the model answer. Then answer the follow-up and compare the reasoning.
Two clients concurrently insert X and Y at position 1 in cat. How does an ordered OT design preserve both edits?
Reveal a model answer
If client A’s X wins our deterministic tie-break, it becomes cXat. Client B’s concurrent Y shifts from position 1 to 2, producing cXYat. Both clients reconcile pending operations to that same accepted order.
Interviewer follow-up
Does that solve concurrent deletion?
Reveal the follow-up answer
No. Delete/insert overlap and range deletion require additional proven transformation rules. The insertion example teaches the mechanism, not the entire algorithm.
What the answer must demonstrate: Work through positions, not only the acronym OT.
When can the editor say an edit is saved?
Reveal a model answer
After the accepted operation is durably recorded under our failure policy. I show local typing immediately as pending, then clear pending on acknowledgment. A socket send alone is not saved.
Interviewer follow-up
What if the acknowledgment disappears?
Reveal the follow-up answer
The client retries the same actor/operation ID and receives the existing version. It must not create a second insertion.
What the answer must demonstrate: Separate local responsiveness from durability.
The old document coordinator resumes after a new one takes over. Why is that dangerous?
Reveal a model answer
Both could append conflicting operations unless the storage layer enforces ownership. Each append carries an increasing epoch, and the log rejects stale epochs after takeover.
Interviewer follow-up
Is a load balancer sufficient?
Reveal the follow-up answer
No. It changes new routing but does not stop a paused process from continuing an old request.
What the answer must demonstrate: Fencing must be checked by the protected resource.
A laptop reconnects after you deleted its required operation history. Can you transform its edit normally?
Reveal a model answer
Not safely from an old position alone. I preserve its pending work, send a current snapshot, and use the product’s explicit merge or conflict path. Retention must match the promised offline window.
Interviewer follow-up
Would a CRDT remove all retention concerns?
Reveal the follow-up answer
No. Stable element IDs help merging, but tombstone cleanup and very old replicas still require a policy.
What the answer must demonstrate: Do not discard the user’s pending work silently.
Should cursor positions be stored like document edits?
Reveal a model answer
Usually not. Cursor presence is short-lived and can expire when a connection disappears. Document operations need durable replay; presence can be dropped and refreshed.
Interviewer follow-up
How do cursors survive remote inserts?
Reveal the follow-up answer
Represent or transform their positions using the editor’s position model, but avoid putting every cursor movement in the durable content log.
What the answer must demonstrate: Different state has different durability needs.
When would you choose a CRDT instead of server-ordered OT?
Reveal a model answer
When offline and independently mergeable editing are central, and a proven CRDT supports our exact content model. I would compare metadata, cleanup, undo, and rich-text behavior, not just network availability.
Interviewer follow-up
What does a successful evaluation look like?
Reveal the follow-up answer
Representative concurrent editing traces converge, preserve acceptable user intent, recover old sessions, and respect permissions and storage budgets.
What the answer must demonstrate: Avoid universal claims about either algorithm family.
A connected client loses edit permission. Where must the decisive permission check occur?
Reveal a model answer
I serialize the current grant check with the authoritative append transaction. If revocation commits first, the later edit fails even if the gateway cached an old grant. If the edit commits first, it is legitimately part of the accepted history before revocation.
Interviewer follow-up
Can revocation erase a copy client B already downloaded?
Reveal the follow-up answer
No. It blocks future authorized reads and edits, but cannot recall bytes on that device. Short-lived download links reduce future access exposure; they do not provide remote deletion.
What the answer must demonstrate: Checking permission only during WebSocket establishment is insufficient.
An edit arrives between the initial snapshot read and the live subscription. How is it recovered?
Reveal a model answer
The client records a fixed accepted head and subscribes with its last applied version. The owner or gateway replays all later versions around registration, so overlap can create duplicates but cannot create a silent gap. Identity and version checks remove duplicates.
Interviewer follow-up
What if version 24 arrives before 23?
Reveal the follow-up answer
The client pauses application at the gap and requests the missing range. Positional operations cannot safely be applied against the wrong base. After replay it reconciles pending edits with the proven algorithm.
What the answer must demonstrate: A snapshot followed by an unversioned socket is a gap-prone protocol.
Blank-page exercise · 45 minutes
Build the answer yourself
Design a text editor and work through client A inserting X and client B inserting Y at the same position in cat, then lose the coordinator.
- Show both local states and the converged result.
- Define operation identity, base version, and acceptance.
- Trace snapshot plus log recovery.
- Handle stale coordinators and old offline clients.
- Compare OT and CRDT using actual requirements.
Check that each component and design decision follows from your requirements and workload.
Recall the key ideas
Answer from memory before opening each card. Explain why the choice works and what it costs. Revisit missed cards tomorrow.
Design a collaborative text editorWhy not save the whole document after every keystroke?Recall first, then reveal
Concurrent replacements can erase another user’s work; identified operations make concurrent edits explicit.
Send the edit, not just the result.
Return to lessonDesign a collaborative text editorDoes convergence prove the users’ intent was preserved?Recall first, then reveal
No. Deterministic merging can produce the same surprising text everywhere.
Same text is not necessarily intended text.
Return to lessonDesign a collaborative text editorWhat stops an old coordinator after takeover?Recall first, then reveal
The durable log rejects writes carrying an older ownership epoch.
The log checks the ownership epoch before accepting a write.
Return to lessonFinal revision
Summary and interview notes
A collaborative editor separates immediate local rendering from durable accepted operations and remote delivery. Proven transformation rules reconcile concurrent edits, while a document-owned append transaction controls permission, version and coordinator ownership.
Remember these points
- Whole-document replacement loses independent edits; identified operations preserve the information needed to merge.
- OT transformation and fenced log append solve different problems: edit semantics versus one accepted history.
- A lost acknowledgment retries the same document/actor/operation identity without inserting text twice.
- Reconnect loads an authorized snapshot, replays later saved operations and starts live delivery without skipping an edit.
- Snapshot cleanup must check upload grants, retained references and active reader pins before deleting bytes.
Interview tips
- Work through equal-position inserts with actual positions, then explain why deletes and undo require additional rules.
- Trace a stale coordinator, revoked grant and duplicate edit through the append transaction.
- State the encoding and position unit; a browser string offset is not automatically a Unicode character index.
Important qualifications
- The insertion example is not a complete OT algorithm; use a proven operation type for the full feature set.
- CRDTs change merge metadata and offline behavior but do not remove permission or garbage-collection obligations.
Technical references
- Yjs shared typesOfficial examples of collaborative shared data types and transactions.
- Yjs document updatesDocuments update exchange, state vectors, and merge behavior for a concrete CRDT implementation.
- RFC 6455: WebSocketDefines the bidirectional transport used for interactive edit and presence events.
- ShareDB documentationOfficial operational-transformation backend documentation; evaluate the supported text type and persistence adapter rather than inferring custom authority guarantees.
- CRDT definitions and glossaryStandard convergence property for conflict-free replicated data types; sequence text is one application, not the general definition.
Practice marks stay in this browser.