System designby Learnastra

System-design interview · Extended interviews

Design a collaborative text editor

By Anup Rai

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 practice

Useful 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.

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

  1. Create, open and share. Create a document, open its current accepted content, and invite another client with read or edit permission.
  2. 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.
  3. 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.
  4. Resume after reconnect. Replay missing accepted operations after a known version such as v21. Missing history older than the retained boundary cannot be inferred.
  5. Share document access. A grant to client B permits subsequent authorized opens and edits. It cannot recall content that was already downloaded.
  6. View presence. Show recent cursors that expire when disconnected. Cursor delivery is not durable editing history.
  7. 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

  1. 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.
  2. 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.
  3. Durability. Acknowledged edits survive one zone failure. Acceptance follows durable persistence; a locally pending edit may still need retry after a crash.
  4. 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.
  5. 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.
  6. Authorization. Check each accepted edit, not only socket establishment. A revocation committed before that edit's authorization transaction causes rejection.
  7. 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.

architecture · baselineOne owner appends before acknowledging

Local rendering is optimistic; the database commit defines saved.

One owner appends before acknowledgingLocal rendering is optimistic; the database commit defines saved. clients to owner: Submit A17 at base 20; owner to db: Authorize and append version 21; owner to clients: Durable acceptance + remote editsSubmit A17 at base 20Authorize and append version21Durable acceptance + remoteeditsACTOREditor clientsSERVICEEditor / documentownerSTOREDocument log andgrantssync
Read each connection in order
  1. syncSubmit A17 at base 20Editor clients → Editor / document owner
  2. syncAuthorize and append version 21Editor / document owner → Document log and grants
  3. 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.

architecture · finalDocument order behind scalable gateways

Connections and delivery scale independently; the document shard remains the accepted-order authority.

Document order behind scalable gatewaysConnections and delivery scale independently; the document shard remains the accepted-order authority. clients to gate: 1. Open / edit / replay; gate to directory: 2. Resolve document owner; gate to owner: 3. Forward identified edit; owner to db: 4. Authorize + conditional append; db to replica: Durable accepted log; db to fan: 5. Resume publication cursor; fan to gate: 6. Accepted versions; gate to clients: Deliver / repair gaps; gate to presence: Refresh expiring cursors; snap to db: Read exact version boundary; snap to objects: Upload verified snapshot; snap to db: Publish snapshot manifest; db to history: Retain replay / transform metadata; owner to objects: Restore verified snapshot; owner to history: Replay / transform supported base1. Open / edit / replay2. Resolve document owner3. Forward identified edit4. Authorize + conditionalappendDurable accepted log5. Resume publication cursor6. Accepted versionsDeliver / repair gapsRefresh expiring cursorsRead exact version boundaryUpload verified snapshotPublish snapshot manifestRetain replay / transformmetadataRestore verified snapshotReplay / transform supportedbaseACTOREditor clients +pending bufferG1SERVICEAuthenticated socketgatewaysG1SERVICEDocument routingdirectoryG2SERVICEDocumentcoordinatorsG2STORELog / grants / epochauthorityG2STORESynchronous logreplicasG2SERVICEVersioned fanoutserviceG3CACHEExpiring presencestateG1WORKERSnapshot workersG3STOREVerified snapshotobjectsG3STORERetained operationhistoryG3syncreplicationasyncG1 Clients and connectionsG2 Document ownershipG3 Delivery and recovery
Read each connection in order
  1. sync1. Open / edit / replayEditor clients + pending buffer → Authenticated socket gateways
  2. sync2. Resolve document ownerAuthenticated socket gateways → Document routing directory
  3. sync3. Forward identified editAuthenticated socket gateways → Document coordinators
  4. sync4. Authorize + conditional appendDocument coordinators → Log / grants / epoch authority
  5. replicationDurable accepted logLog / grants / epoch authority → Synchronous log replicas
  6. async5. Resume publication cursorLog / grants / epoch authority → Versioned fanout service
  7. async6. Accepted versionsVersioned fanout service → Authenticated socket gateways
  8. syncDeliver / repair gapsAuthenticated socket gateways → Editor clients + pending buffer
  9. syncRefresh expiring cursorsAuthenticated socket gateways → Expiring presence state
  10. syncRead exact version boundarySnapshot workers → Log / grants / epoch authority
  11. syncUpload verified snapshotSnapshot workers → Verified snapshot objects
  12. syncPublish snapshot manifestSnapshot workers → Log / grants / epoch authority
  13. asyncRetain replay / transform metadataLog / grants / epoch authority → Retained operation history
  14. syncRestore verified snapshotDocument coordinators → Verified snapshot objects
  15. 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.

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  5. 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.

  6. 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.

  7. 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.

  8. 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.

Concept in focusPreserve two inserts at the same position

The cells show the text after each accepted operation. Positions are zero-based; the agreed tie-break puts A before B.

Preserve two inserts at the same positionThe cells show the text after each accepted operation. Positions are zero-based; the agreed tie-break puts A before B. 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.Both clients start with cat and insert at position 1catA inserts X; B inserts YcXatAccept A first: cXatcXYatB shifts to position 2With tie-break A before B, transforming B preserves both insertions.

Remember: After X takes position 1, move Y to position 2.

Read the diagram
  1. Track the text from cat to cXat to cXYat.
  2. A and B both submit insertions at position 1 of base text cat.
  3. 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.

sequence · edit-raceOne accepted order survives owner takeover

Client A’s edit is durable before takeover; the old epoch cannot append client B’s operation afterward.

One accepted order survives owner takeoverClient A’s edit is durable before takeover; the old epoch cannot append client B’s operation afterward. a to old: A17: insert X at base 20; old to db: Append A17; epoch 4, head 20; db to old: Committed version 21; new to db: Advance ownership to epoch 5; b to old: B9: insert Y at base 20; old to db: Attempt append using epoch 4; db to old: Reject stale epoch; b to new: Retry identical B9; new to db: Transform; append at head 21 / epoch 5; db to new: Committed version 22: cXYat; new to b: Accept B9 / version 22PARTICIPANTClient APARTICIPANTOld coordinatorPARTICIPANTDocumentauthorityPARTICIPANTNew coordinatorPARTICIPANTClient B1. A17: insert X at base 202. Append A17; epoch 4,head 203. Committed version 214. Advance ownership toepoch 55. B9: insert Y at base 206. Attempt append usingepoch 47. Reject stale epoch8. Retry identical B99. Transform; append at head21 / epoch 510. Committed version 22:cXYat11. Accept B9 / version 22syncreturnblocked
Read each connection in order
  1. syncA17: insert X at base 20Client A → Old coordinator
  2. syncAppend A17; epoch 4, head 20Old coordinator → Document authority
  3. returnCommitted version 21Document authority → Old coordinator
  4. syncAdvance ownership to epoch 5New coordinator → Document authority
  5. syncB9: insert Y at base 20Client B → Old coordinator
  6. syncAttempt append using epoch 4Old coordinator → Document authority
  7. blockedReject stale epochDocument authority → Old coordinator
  8. syncRetry identical B9Client B → New coordinator
  9. syncTransform; append at head 21 / epoch 5New coordinator → Document authority
  10. returnCommitted version 22: cXYatDocument authority → New coordinator
  11. 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.

Concept in focusMerge slots before adding the total

Each replica alone increments its own slot. Merge uses the maximum of corresponding slots.

Merge slots before adding the totalEach replica alone increments its own slot. Merge uses the maximum of corresponding slots. 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.Grow-only counter: each replica increments its own slotReplica A20Replica B0323component-wise maximumVisible total: 2 + 3 = 5Merge [2,3] again: still [2,3]. Replayed state does not double the count.

Remember: Maximum per slot, then sum; do not add whole replica totals.

Read the diagram
  1. Merge [2, 0] and [0, 3] into [2, 3].
  2. The visible merged total is 5.
  3. 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.

Foundation · Question 1

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.

What the answer must demonstrate: Work through positions, not only the acronym OT.

Applied · Question 2

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.

What the answer must demonstrate: Separate local responsiveness from durability.

Applied · Question 3

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.

What the answer must demonstrate: Fencing must be checked by the protected resource.

Follow-up · Question 4

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.

What the answer must demonstrate: Do not discard the user’s pending work silently.

Foundation · Question 5

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.

What the answer must demonstrate: Different state has different durability needs.

Follow-up · Question 6

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.

What the answer must demonstrate: Avoid universal claims about either algorithm family.

Applied · Question 7

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.

What the answer must demonstrate: Checking permission only during WebSocket establishment is insufficient.

Follow-up · Question 8

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.

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 lesson
Design 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 lesson
Design 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 lesson

Final 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.