Figma wrote in 2019 that it evaluated operational transforms and CRDTs for multiplayer editing and shipped neither in full. A central server receives and broadcasts changes, and two simultaneous changes conflict only when they touch the same property of the same object, in which case the later one wins. What constraint made that enough, and what did the choice cost?
Show the full answer Hide the answer
The situation they were in
A multi-user design tool has to merge concurrent edits to one document. The published algorithms came from text editing, where the hard case is genuinely hard: two people insert characters into one sequence and every index after the insertion point shifts, so a naive apply corrupts the document.
Figma's documents are not a sequence. They are a tree of objects with named properties — x, y, fill, parent. Figma's own framing in the 2019 post is that since Figma is not a text editor, it did not need the power of operational transforms and could get away with something less complicated, and that a simpler system is easier to reason about, implement, debug, test and maintain.
What they chose
A central server is always in the request path during an editing session. Clients send changes, the server orders them and broadcasts them. Two changes are independent unless they affect the same property of the same object, and that case is settled by taking the latest change. The post also records a deliberate split: comments, users, teams and projects live in Postgres rather than in the multiplayer system, because those have different requirements around performance, offline availability and security.
Why it fit their constraints
The property is the unit of conflict, and in a design tool concurrent edits almost never collide at that granularity. Two people dragging two rectangles write disjoint properties, so there is nothing to merge. Convergence without a coordinator — the property a CRDT buys — is only valuable when there is no coordinator, and a live collaborative session already has one.
The transferable lesson is the Postgres split, not the merge rule: the consistency model is chosen per data class, not per product. The same application can run last-writer-wins on document geometry and serialisable transactions on billing.
What it cost them
- Correctness depends on a live connection. Offline editing is not a free property of this design; it has to be built as a separate capability rather than falling out of the algorithm.
- Last-writer-wins discards intent silently. When two people change the same fill, one change vanishes with no error and no audit trail. The symptom is "the tool undid my work", unfalsifiable from logs.
- Multi-property operations are not atomic unless modelled that way. Moving a group touches many properties, and interleaving with another user's edit can leave a half-applied result.
- A stateful server per open document means capacity planning per document rather than per request, and a reconnect that resynchronises state rather than replaying a log.
Figma also described a test harness that simulated three clients and a server and visualised the whole system state, so offline and bandwidth-limited scenarios could be set up deliberately. A deterministic multi-client simulator is the only practical way these bugs are found, and it is the part teams skip.
Where copying this would be a mistake
Copy the per-property last-writer-wins rule into a text editor, a spreadsheet with formula dependencies, or a genuinely offline-first application, and it loses characters and produces results no user can explain. If clients must make durable progress while partitioned from the server, you need convergence without a coordinator and you should pay for a CRDT. If you cannot afford a stateful process per open document, the server-ordered model is not available to you either.
Common weak answers
- "They should have used a CRDT for correctness." A CRDT is not more correct here; it solves coordination-free convergence, pays metadata growth per object and makes debugging substantially harder, in exchange for a property a live session does not need.
- "Last-writer-wins is always wrong." It is wrong when concurrent writes to one field are common and both carry intent. It is the right default when collisions at field granularity are rare.
- "Operational transforms are legacy." They remain the correct tool for sequences.