Guide

Collaborative text is the hardest merge problem in this section. Two people type into the same paragraph. If each applies the other's edit by position, the two copies end up different. Two approaches fix this: operational transformation (OT) and sequence CRDTs.

Concept

Before you start. An operation is one edit, such as "insert ! at position 5". A position is an index into the text. Positions shift whenever someone types before them.

The big idea

Both users start with Hello. Alice inserts ! at position 5. At the same time, Bob inserts world at position 5. If each side applies the other's edit literally, the copies diverge.

Alice: "Hello" + "!" at 5        -> "Hello!"
Then Bob's " world" at 5 applied -> "Hello world!"

Bob:   "Hello" + " world" at 5  -> "Hello world"
Then Alice's "!" at 5 applied    -> "Hello! world"

The two copies disagree. Each side must adjust the other's position, or anchor the insert to something that does not move.

Walking through it

Operational transformation rewrites each incoming operation against the operations already applied. Bob's insert moves past Alice's ! on one side, and Alice's ! moves before Bob's text on the other. A central server picks one order for all edits. Google Docs works this way, in the lineage of the Jupiter system and Google Wave. It works well, but every edit must pass through the server, and the transform rules are hard to get right.

Sequence CRDTs avoid positions entirely. Each character gets a unique, permanent ID. An insert says "put this character after the character with ID X". Concurrent inserts after the same character are ordered by their IDs, so every replica gets the same result. Deletes leave tombstones.

OTSequence CRDT
Needs a central serverIn practice, yesNo, works peer to peer and offline
Metadata per characterNoneAn ID per character, compressed in modern libraries
CorrectnessHard, and many published algorithms had bugsConvergence is proven
ExamplesGoogle DocsYjs and Automerge users

Some sequence algorithms suffer from interleaving. Two people typing words at the same spot get their letters mixed, as in Hweolrllod instead of Hello world. Fugue was designed to avoid this.

Figma's shortcut

Figma looked at both and chose something simpler, because a design file is a tree of objects, not one string. Its system:

  • Uses a central server as the authority.
  • Resolves conflicts per property, with last-writer-wins in the server's order. If Alice changes a color while Bob moves the same shape, both changes apply.
  • Orders children with fractional indexes, so an insert between two items never renumbers the others.

Use the simplest model whose conflicts users accept.

Common mistakes

  • Applying remote edits by position alone. The copies diverge as soon as two people type at one spot.
  • Thinking OT is a quick fix. Many published OT algorithms had correctness bugs.
  • Assuming CRDTs cost nothing. Per-character IDs add metadata, and tombstones grow over time.

Check yourself

  1. Alice and Bob both insert at position 5. What must change so the copies match? Each side transforms the other's position, or the insert is anchored to a character ID.
  2. Why does Figma avoid a general CRDT? Its conflicts are per property, and a central server already orders them, so a simpler rule is enough.

Key takeaways

  • Position-based edits diverge when two people type at the same spot.
  • OT transforms positions against a server-ordered history.
  • Sequence CRDTs give each character an ID, so they work without a server.
  • Figma uses a server with per-property last-writer-wins and fractional indexes.

Further learning

Martin Kleppmann's talk on the hard parts of CRDTs covers interleaving and moving list items.

CRDTs: The Hard PartsMartin Kleppmann

His Cambridge lecture compares OT and CRDTs for text with worked examples.

Distributed Systems 8.1: Collaboration softwareMartin Kleppmann
How Figma's multiplayer technology worksA peek into the homegrown solution we built as the first design tool with live collaborative editing.Figma