Home / Software y Cloud / What happens when two people edit the same line at once: CRDT, the math that keeps your document from tearing apart

What happens when two people edit the same line at once: CRDT, the math that keeps your document from tearing apart

dos editores colaborando sobre un documento en tiempo real

If you have ever watched two people type into the same Google Doc, Notion board or Figma file at once without the text tearing apart, you were witnessing a distributed-computing problem that took decades to get right. When two editors modify the same line simultaneously, the document splits into two divergent versions. The hard question is not how to detect the split, but how to make the two branches converge on their own, without anyone losing their work.

The problem underneath: divergence

Think of the document state as a position where each person applies operations (insert a character, delete it). If changes arrive in different orders at each end, the final result should still be the same for everyone to see the same thing. That property is convergence: all replicas end up in the same state even though they applied the operations in different orders. A system that achieves this without a central arbiter is called conflict-free — and that is where CRDTs come in.

The naive answer: last writer wins

The simplest response is LWW (last-writer-wins): when two edits collide, keep the one with the most recent timestamp. That is what many simple note-syncing systems do. The problem is that it silently discards work — if you were writing sentence A and your teammate sentence B, one of them simply disappears. It is robust, but it loses data, and in collaborative editing that is unacceptable.

OT, Google’s answer: call in an arbiter

Google Docs does not use CRDTs; it uses operational transformation (OT). The idea is that a central server maintains the canonical ordering of operations and, when two concurrent operations arrive out of order, it transforms them so they behave as if applied in sequence. The server rewrites operation B so that B’, applied after A, yields the same result as if B had arrived first. OT works, but it requires a server to act as arbiter and a large amount of bespoke complexity: each editor type (text, spreadsheet, drawing) needs its own transformation logic, which is error-prone and hard to debug.

CRDT: convergence by design

A CRDT (Conflict-free Replicated Data Type) is a data structure engineered so that its concurrent operations commute: applying them in any order gives the same result. Because convergence is guaranteed mathematically by the structure itself, there is no need for a central server or hand-crafted transformations. Each replica keeps a local state, replicates its changes to the others, and even if operations arrive in different orders, all replicas end up identical.

There are two families. In state-based (convergent) CRDTs, each replica stores whatever is needed and updates are merged with an operation that is commutative (order does not matter) and idempotent (applying it twice changes nothing). In operation-based CRDTs, what gets replicated is the list of operations, and each node applies them to its own state.

Counters and sets that never forget

The clearest example is the lossless counter. A typical LWW counter loses increments: if A adds 1 and B adds 1 concurrently, the last writer “eats” one of them. The CRDT version, called a PN-Counter, keeps a per-replica subtotal (increments and decrements separately), and the global value is the sum of all subtotals. Since addition commutes, no increment is ever lost.

Sets behave similarly. A G-Set only allows adding: because adding commutes, two concurrent additions never collide. To support removal, the OR-Set (observed-remove set) attaches a unique identifier to each element; when deleting, it removes the identifiers it has seen and uses the causal precedence relation (who saw what first) to decide whether a reappearing element is a re-insertion or a deletion. This avoids the classic bug where a delete could resurrect an element added later.

The heart: text editing with Lamport clocks

The hardest and most familiar case is real-time text. A text CRDT keeps each character as a node with a unique identifier and a pointer to its predecessor. The identifier is a Lamport clock: a pair (counter number, replica identifier). The counter advances with each edit and the replica identifier breaks ties, so all identifiers become totally ordered: even if two edits are born at the same instant, there is always an unambiguous way to decide which comes first. Each insertion records the position it hangs from, forming a linked list of nodes — effectively a directed acyclic graph (DAG) of insertions.

That is the design of YATA, the algorithm behind the Yjs library, and a similar idea underpins Automerge (based on RGA, Replicated Growable Array). Rather than storing one node per character, Yjs packs contiguous characters into blocks and uses a linked list of blocks, cutting memory overhead: instead of a node of dozens of bytes per character, one block covers an entire sentence. When two people insert at the same position at once, both insertions find a gap in the total order and land side by side; when they converge, everyone sees both, with the insertion order decided by the clocks.

The price: metadata and tombstones

Nothing is free. Every character (or block) must carry its identifier and its reference to its predecessor, so metadata overhead can exceed several times the size of the text itself. Worse, CRDTs with deletion keep tombstones: when you delete a character, its node is not removed from the store — it is only marked dead — because other nodes still reference it as a predecessor and the graph must stay connected. Documents with heavy editing therefore retain garbage until a compaction (garbage collection) rewrites the structure and drops the dead nodes.

OT or CRDT?

OT and CRDT solve the same problem from opposite philosophies. OT needs a central server to order and transform, and it powers products such as Google Docs and Etherpad. CRDTs are decentralized by nature: each replica is independent, can work offline (offline-first) and sync when the network returns; that is why they underpin local-first apps, cross-device sync and peer-to-peer collaborative editors. The costs are memory overhead and slightly less intuitive semantics at edges like merging deletions and re-insertions.

Figma, Yjs (used by Traduora, Manyverse and many Notion-style clients), Automerge in local-first apps, and native CRDTs in databases such as Redis or MongoDB for multi-site replicas all show that, when the problem is two people touching the same line at once, the sharpest tool is not an arbiter imposing order but a data structure whose very mathematics guarantees everyone ends up seeing the same thing.