THE MIGRATION LINE
INCIDENT // FIGMA-2019 SHIPPED

Figma studied operational transforms and CRDTs, then built a simpler centralized model that syncs one property at a time instead of shipping document state around.

FIGMA · 2019 · COLLABORATION / CRDT / WEBSOCKETS / SYNC
System stress over time Breach at T+4
1 per document Server processes
3 simulated Prototype clients
Last write wins Conflict rule
Fractional indexing Child ordering
BASELINE

A feature people actively did not want

When Figma started building multiplayer, nobody was clamoring for a collaborative design tool. Designers worried out loud about hovering art directors and design-by-committee catastrophes. Figma built it anyway, because it felt wrong not to offer multiplayer on the web: no exporting, no syncing, no emailing copies of files.

The setup was a client/server one. Figma clients are web pages talking to a cluster of servers over WebSockets, and the servers spin up a separate process for each multiplayer document. Opening a document downloads a copy of the file; from then on, updates flow both ways over that socket. Reconnecting after an arbitrary stretch offline works the same way — download a fresh copy, replay your offline edits on top, resume syncing. That made connecting easy and pushed all the real difficulty into one place: what to send for every change to a document that is already open.

REQS

What the system actually had to guarantee

The primary design goal was stated flatly: the multiplayer system should be no more complex than necessary to get the job done. A simpler system is easier to reason about, which makes it easier to implement, debug, test, and maintain.

Underneath that sat the hard constraints. Two clients editing the same document can never be allowed to diverge and never converge again — eventual consistency is a correctness requirement, not a nicety. Editing must work offline for an arbitrary amount of time, which means new objects have to get IDs without asking the server. And local changes have to apply immediately rather than waiting for a server acknowledgement, because Figma has to feel responsive.

OPTIONS

What they read, and what they threw out

Before touching the real codebase, Figma built a prototype playground: a web page simulating three clients and a server, visualizing the whole state of the system, so they could set up offline clients and bandwidth-limited connections at will. That environment is where the alternatives got eliminated.

OT was ruled out as overkill. Conflict-free replicated data types — CRDTs, data structures whose math guarantees that replicas eventually converge — were the inspiration, but Figma is not using true CRDTs either. CRDTs are designed for decentralized systems with no single authority to decide the final state, and that decentralization carries unavoidable performance and memory overhead. Figma’s server is the central authority. Because of that, they could strip the overhead out and keep a faster, leaner implementation, borrowing from several CRDTs at once rather than adopting any one of them.

THE CALL

Sync the property, not the document

The decision: Figma’s servers track the latest value any client has sent for a given property on a given object. A document is a tree of objects, like the HTML DOM — you can picture it as a two-level map from object ID to a map of property to value, or as rows of object-property-value tuples. So two clients changing unrelated properties on the same object never conflict, and two clients changing the same property on unrelated objects never conflict. A real conflict is only ever the same property on the same object, and the document simply keeps the last value the server received. That is a last-writer-wins register, minus the timestamp, because the server gets to define the order of events.

The tree was the hardest part, and reparenting was why. Many systems model a move as delete-and-recreate-with-a-new-ID, which Figma rejected: concurrent edits get dropped the moment an object’s identity changes. Instead the parent link is stored as a property on the child, so identity survives the move — and the link and the child’s fractional index (its position expressed as a fraction between 0 and 1, so you can insert between two siblings by averaging theirs) are stored as one property, so they update atomically. It makes no sense to keep a position from a parent you no longer have.

COSTS

The bad parts they signed for

They took the losses on purpose. No collaborative text merging, as above — acceptable, because Figma is a design tool. Cycles are worse: a client can send an unacknowledged change parenting A under B while the server sends back a change parenting B under A. The server rejects cycles, so that client’s change is doomed, but the client does not know it yet. Figma’s answer is to temporarily parent the objects to each other and pull them out of the tree until the rejection lands. Their own verdict is that this is not great, because the object briefly disappears — just a simple solution to a very rare temporary problem.

Deletes push cost onto the client too: the server keeps no properties of deleted objects, so the client that performed the delete holds them in its undo buffer and is responsible for restoring them on undo. That is what keeps long-lived documents from growing forever. And undo itself needed a governing principle, found only after a lot of trouble: if you undo a lot, copy something, then redo back to the present, the document should not change. The simplification that made this system tractable was not a cleverer algorithm — it was admitting they had a central server and refusing to pay for the guarantees that only matter when you don’t.

Source — read the original

https://www.figma.com/blog/how-figmas-multiplayer-technology-works/

A plain-language, AI-drafted and human-edited retelling of the article published on figma.com, reorganized and explained in our own structure and words, with original analysis in the editor's note above. The facts, numbers, and decisions belong to the original author and are not altered. For the full depth, read the source.

← All systems