Understanding sync engines: How Figma, Linear, and Google Docs work

Understanding sync engines: How Figma, Linear, and Google Docs work

Two users edit a rectangle in your design tool while offline. Alice moves it to position [100, 200]. Bob changes its color to red. When they reconnect, what should happen? This is easy to answer—show the rectangle at [100, 200] in red. Both changes survive.

Now the hard questions:

  • What if they both moved it to different positions?
  • What if Alice deleted the rectangle while Bob moved it?
  • What if they're both typing at the same position in a text field?

Your answers to these questions determine your entire sync architecture. Choose wrong, and you're looking at 3-6 months of refactoring when you realize users are losing data.

Every modern app is adding “realtime collaboration” to their feature list, and you've probably heard CRDT thrown around like it's a silver bullet. The reality is far more nuanced: Figma’s collaborative canvas works fundamentally differently than Google Docs’ text editing, which works differently than Linear's issue tracking. They’re all “realtime” and “collaborative”, but the underlying sync mechanisms solve different problems.

As someone who’s built realtime systems at scale—from social media platforms with offline-first sync to event-driven microservice architectures—I've seen how critical this architectural decision becomes. This guide synthesizes what production systems actually do and why.

Every realtime collaborative system must be able to handle multiple users editing the same data simultaneously, with network delays and offline periods. Changes can arrive in any order, and everyone needs to end up with the same result.

Let’s think back to Alice and Bob’s rectangle, and some conflicts they’ll face in a multiplayer app:

  • Alice and Bob change different properties? Easy. Both changes survive.
  • They edit the same property differently? Tricky. A conflict resolution strategy is needed.
  • Alice deletes, Bob modifies? Tricky. An intention conflict needs to be resolved.
  • Both typing in the same text position? Difficult. This is where most naive implementations break.

There are two solution families that handle these scenarios differently.

Operational Transformation (OT) transforms operations based on what others did concurrently. Requires a central server to order operations, and is used by Google Docs, Microsoft Office Online. Complex to implement correctly, but gives predictable results for text editing.

Conflict-free Replicated Data Types (CRDTs) operations are mathematically commutative—apply them in any order, always converge to the same state. Works peer-to-peer or client-server. True CRDTs underpin frameworks like Yjs and Automerge, powering many collaborative text editors and document tools.

CRDT-inspired architectures power Figma, Linear, and products built with Liveblocks like Vercel, Hashnode, and Resend.

Both enable “realtime collaboration”, but they’re designed for fundamentally different problems. Figma’s rectangle positioning needs different conflict resolution than Google Docs’ text editing. The key insight is that it’s not about OT vs CRDT—it’s about property-level vs character-level conflict resolution.

Property-level conflict resolution treats each attribute of an object as an independent unit. When two users edit different properties of the same object, both changes succeed. When they edit the same property, Last Write Wins (LWW) based on a logical clock.

Character-level conflict resolution treats text as a sequence of characters with positional metadata. When two users edit different parts of the same text, both edits are preserved in the final result through sophisticated merging algorithms.

Two users are editing a task item while offline:

This is property-level conflict resolution, used by Figma and Liveblocks Storage. Each property is independent, and changes to different properties never conflict.


Now consider the same scenario with text editing:

Buy apples
\`and oranges\`
\`and bananas\`
\`and oranges and bananas\`

This is character-level conflict resolution, used by Google Docs and Yjs. The CRDT tracks the position and context of each character insertion, allowing both edits to coexist in the final document. This difference determines your entire sync architecture.

The theory is interesting, but what do real companies with millions of users actually do? Let’s examine three battle-tested systems.

Figma’s multiplayer system is one of the most well-documented collaborative architectures. How it works:

Figma’s servers track the latest value for each property on each object, as described on Figma’s engineering blog. When two clients change different properties (one moves the rectangle, another changes its color), both changes succeed. When two clients change the same property, the last change received by the server wins.

This is explicitly not a true CRDT because it relies on a central server for ordering, but it’s inspired by CRDT concepts—specifically the Last-Writer-Wins Register pattern. Liveblocks Storage uses this same proven architecture with centralized CRDT-like structures. I’ve implemented similar patterns in production social platforms—the simplicity is what makes it reliable at scale. Figma chose this approach because:

Reason Impact
Each shape, layer, or component is an independent entity
Color changes don’t conflict with position changes
Natural work patterns reduce conflicts
When conflicts occur, newest wins matches user expectations
Faster performance, easier to debug

Figma’s approach doesn’t work for text editing. If the text “B” becomes “AB” on one client and “BC” on another, the result is either “AB” or “BC”—never “ABC”. This is fine for design tools but unacceptable for collaborative text editors.

Linear’s sync engine is designed for a more varied data model than Figma (as documented in their blog and reverse-engineered here) and uses a similar property-level approach for most data:

Linear tracks changes as discrete events, each modifying a single attribute. The server assigns each change a monotonically increasing sync ID, and conflicts resolve to the highest ID (effectively last-write-wins, but server-ordered).

RationaleEvery change is a separate event with metadataStatus changes don't conflict with title edits

Reliable ordering through centralized transaction processing

Conflicts are rare; simple resolution worksAdded later for issue descriptions only

According to their engineering team's discussions, conflicts are actually quite rare in their domain—most of the time users are working on different issues or different properties of the same issue. They only recently added CRDTs for one specific use case; rich-text editing in issue descriptions.

Both Figma and Linear use property-level approaches because they work with discrete objects where conflicts are rare. Text editing is fundamentally different—users constantly insert characters at nearby or identical positions. This is where character-level resolution becomes essential.

Google Docs and Yjs both handle character-level text editing, but use different approaches. Google Docs uses Operational Transformation (server-based), while Yjs uses CRDTs (peer-to-peer). Both recognize that text editing has fundamentally different conflict patterns than object properties.

Here’s how Yjs structures text for CRDT-based collaboration:

Every character gets a unique identifier tracking its position and creation context. When two users type at the same spot simultaneously, Yjs uses a deterministic algorithm to decide which text comes first based on these identifiers:

\`Hello world\`
\`Hi there\`
\`Hello world\`
\`Hi Hello world\`

This is fundamentally different from property-level sync. Yjs operates on ordered sequences—treating text as individual characters with positional relationships. You can’t just “swap in Yjs” for a Figma-like app because it’s solving a different problem: preserving the order and intent of concurrent insertions in sequences, not resolving conflicts between discrete object properties.

You’ve seen what Figma, Linear, and Google Docs do, now let's determine which approach fits your application.

Property-level resolution is fully supported by Liveblocks Storage.

Examples Why property-level works
Figma, Miro, Whimsical, Lucidchart Objects have discrete properties (position, size, color)
Draw.io, flowchart builders Nodes and edges are independent objects
Kanban boards, form builders, spreadsheet cells Each item has distinct properties that rarely conflict
Dashboard builders, workflow designers Changes typically affect different objects/properties

Conflict characteristics that favor property-level:

  • Users typically work on different objects or different properties.
  • When same-property conflicts occur, last write wins is acceptable.
  • You need to track “who changed what” at the property level.
  • Objects have clear identity and structured properties.

LiveObject is a CRDT-like data structure that allows these changes to be merged automatically.

It’s also possible to use LiveMap and LiveList to manage complex data structures, nesting them inside each other.

Character-level resolution is fully supported by Liveblocks Yjs and our text editor integrations.

Examples Why Character-level Is required
Google Docs, Notion, Medium Multiple users typing in same paragraph
VSCode Live Share, Replit Character-level precision for code
HackMD, CodiMD Text content is primary data
Slack-like apps with rich text Insertion order and position matter

Conflict characteristics that require character-level:

  • Multiple users editing the same text simultaneously.
  • Insertion position and order are critical.
  • Character-level granularity is required.
  • Delete operations need to be preserved.
  • Undo/redo must work correctly in collaborative context.

Liveblocks Yjs is a CRDT-like data structure that allows these changes to be merged automatically.

Use this tree to determine which approach to use for your application.

After studying production systems:

Why It Matters When to Use
Matches user mental models for object editing 90% of design tools, structured data apps
Required for concurrent text editing Any collaborative text/code editor
System timestamps are unreliable in distributed systems Distributed systems without central ordering authority
Users expect instant feedback Every realtime collaborative app
Seeing others' cursors is table stakes Apps with multiple simultaneous editors

What doesn't matter as much:

  • OT vs CRDT debate: Yjs and CRDTs won. Use CRDTs.
  • P2P vs client-server: Client-server is simpler and works fine for most use cases.
  • Custom CRDT implementations: The research is solved. Use existing solutions.

At this point you might be thinking: “This seems straightforward—can’t I just build it myself?” Here’s what that actually entails.

The decision framework seems straightforward, but you might be thinking: “Can’t I just build this myself?”. It’s harder than it looks, which is why platforms exist to solve this. When I talk to clients who built their own sync engine, they consistently underestimated the following components.

You can’t just use Date.now() for timestamps. System clocks drift, users change their time zones, and servers can have clock skew. One approach is Hybrid Logical Clocks.

Note that systems with centralized servers (like Linear and Figma) can sidestep this complexity with server-assigned ordering, but truly distributed systems need HLCs or similar mechanisms. The original HLC paper details why this matters for distributed systems.

You need a database schema that handles millions of events efficiently. Here’s the difference:

The production schema tracks both client-side causality (client_id + client_sequence) and server-side ordering (server_timestamp). When conflicts occur, server timestamp wins. The UNIQUE constraint prevents duplicate operations from the same client.

This approach, used by Linear and Figma, is simpler than full vector clocks but reliable because the server provides total ordering.

Before you know it, an array of tasks will be on your hands.

Complexity Why it’s hard
High HLCs require careful implementation; bugs cause silent data corruption
Medium Must handle millions of events, efficient queries, garbage collection
Very High Queuing, reconnection, merge conflicts, UI updates during merge
Medium Separate broadcast system for cursors, selections, typing indicators
High Row-level security, encryption, audit logs, graceful access revocation
Very High Connection pooling, load balancing, heartbeats, mobile sleep/wake handling

6-12 months of engineering time from experienced distributed systems engineers, plus ongoing maintenance, monitoring, and debugging infrastructure.

The theory is clear. Now let’s build it. You have two options:

Option 1: Build it yourself (6-12 months, 2-3 engineers)

  • Implement logical clocks, conflict resolution, WebSocket infrastructure.
  • See Why not roll your own? for the full task list.

Option 2: Use Liveblocks (hours to days)

  • Handles sync infrastructure, storage, and conflict resolution.
  • Two products matching the patterns above.

This complexity is exactly why Liveblocks exists—to handle the difficult sync infrastructure so you focus on your application. Liveblocks is the only hosted solution that offers both sync types, providing:

  • Liveblocks Storage for property-level sync (the Figma/Linear pattern)
  • Liveblocks Yjs for character-level sync (the Google Docs pattern)

Let’s see how to implement each:

Architecture pattern:

Key capabilities:

  • LiveObject, LiveMap, LiveList CRDT-like data structures.
  • Automatic conflict resolution per property.
  • Optimistic updates with automatic sync.
  • Connection handling with offline detection.
  • Built-in presence (cursors, selections).
  • Packages for React, Zustand, Redux.

Architecture pattern:

Key capabilities:

Many applications need both:

The collaborative sync landscape has matured significantly. The fundamental patterns, property-level LWW for objects, character-level CRDTs for text, are well understood and battle-tested in production (Figma, Linear, Yjs).

These are different problems requiring different solutions. Figma doesn't use Yjs because property-level sync is the right model for design tools. Google Docs doesn't use property-level sync because character-level CRDTs are required for text editing.

Choose based on your primary data model:

Use...Liveblocks StorageLiveblocks YjsUse both thoughtfully

The techniques presented here represent battle-tested approaches from production systems serving millions of users. While there are other sync engines and approaches out there, the decision framework remains the same: match your data model to the right conflict resolution strategy.

Now go build something collaborative. Reach out if you get stuck.


Главная - Вики-сайт
Copyright © 2011-2026 iteam. Current version is 2.155.1. UTC+08:00, 2026-04-13 20:12
浙ICP备14020137号-1 $Гость$