Back to blog
Multiple users editing a shared document in real time with cursors converging to a central server
System Design

Operational Transformation: How Google Docs Syncs Edits

Sep 15, 2026 10 min read Avinash Tyagi
operational transformation google docs system design crdt collaborative editing real time collaboration conflict resolution distributed systems websockets system design

Open a Google Doc, share it with a few colleagues, and start typing at the same time. Every keystroke shows up on every screen in a fraction of a second, nobody overwrites anybody, and the document never ends up as garbage. Now imagine that happening across 25 million people editing at once. That is the problem Google Docs solves every day, and the core idea that makes it work is called operational transformation.

This post walks through how real time collaborative editing actually works under the hood. We will start with why the obvious approaches fail, then build up the operational transformation algorithm, look at the architecture that supports it at scale, and compare it to the newer approach that tools like Figma chose instead. If you have ever wondered how does Google Docs work when everyone is typing into the same paragraph, this is the answer.

Why naive concurrent editing breaks

The first instinct most engineers have is to treat a shared document like a shared variable: whoever saves last wins. This is fine for a settings page. It is a disaster for a document.

Say the document contains the text "Hello". Alice inserts "!" at the end to make "Hello!". At almost the same moment, Bob inserts "there " after "Hello " to make "Hello there". If the server simply takes the last write it receives, one of those edits vanishes. Multiply that by dozens of active cursors and you get lost work, mangled sentences, and users who never trust the tool again.

The second instinct is locking. Only one person can edit a region at a time; everyone else waits. This preserves correctness but destroys the entire point of collaborative editing. Nobody wants to request a lock on a paragraph and stare at a spinner while a coworker finishes a sentence. Locking also scales terribly, because contention rises with every additional editor.

The real requirement is subtle. Every client must be able to edit freely and immediately, with no waiting, and yet all clients must converge on exactly the same final document. That combination of low latency and guaranteed convergence is what a collaborative editing system design has to deliver, and it is why a special algorithm is needed rather than a database trick.

What operational transformation actually is

Operational transformation is a technique for merging concurrent edits so that every participant ends up with an identical document, regardless of the order in which edits arrive. Instead of syncing the whole document or locking regions, each client sends small operations that describe what changed.

An operation is a compact instruction. For text, the common operations are insert a string at a position, delete a range at a position, and retain (skip) a number of characters. When Alice types "!" at position 5, her client does not send the new document. It sends something like insert("!", 5). This keeps messages tiny and fast, which matters enormously when millions of them fly around every second.

The magic is in the word "transform". Operations are defined against a specific version of the document. When two operations are created against the same version but arrive in different orders, applying them blindly produces divergence. Operational transformation fixes this by rewriting one operation so that it still makes sense after the other has been applied.

The transform function

The heart of the operational transformation algorithm is a function usually written as transform(a, b). It takes two operations that were both created against the same document state and returns adjusted versions that can be applied one after the other while preserving both authors' intent.

Return to Alice and Bob. Both edits were written against "Hello" (length 5).

  • Alice: insert("!", 5)
  • Bob: insert("there ", 5), inserted after "Hello " but positions are kept simple for the example

If the server applies Alice first and then Bob's original operation, Bob's index no longer points where he meant, because Alice's insert shifted everything after position 5. The transform function catches this. It notices Alice inserted one character at position 5, so it bumps Bob's insertion position by one. Bob's operation becomes insert("there ", 6). Now the two operations compose cleanly, and every client that applies them in any order lands on the same string.

This positional adjustment is the essence of the technique. Every operation carries enough context to be rewritten against any concurrent operation, so the system never has to lock, never has to reject an edit, and never loses intent.

Operational transformation merging concurrent edits from two clients through a central server into a converged document
How operational transformation rewrites concurrent operations so all clients converge.

The architecture behind Google Docs scale

Operational transformation is an algorithm, not an architecture. To run it for real, a google docs system design needs a specific set of moving parts around it.

A central server that assigns order

Google Docs uses a client-server model with a single authoritative server per document. Clients do not talk to each other directly. Every operation flows to the server, which assigns it a global sequence number and transforms it against any operations that arrived first. This central ordering is what makes convergence tractable. Peer-to-peer operational transformation is possible in theory but the number of transform cases explodes, so production systems almost always keep a server in the middle.

Each client keeps three things: the last server revision it has acknowledged, a buffer of operations it has sent but not yet had confirmed, and a buffer of local edits not yet sent. When the server confirms an operation, the client transforms its pending buffers against whatever else the server applied in the meantime. This is why your cursor never jumps and your text never flickers even when a coworker is editing the same line.

The connection and revision layer

Underneath sits a persistent connection, historically long-polling and now typically WebSockets, so the server can push transformed operations to every client the instant they are ordered. The server also writes every operation to an append-only revision log. That log is the source of truth. It lets the system rebuild the document from scratch, support unlimited undo, show revision history, and recover a client that briefly went offline by replaying the operations it missed.

Persistence usually combines periodic document snapshots with the operation log. Rebuilding from the beginning of time would be slow, so the server stores a snapshot every few thousand operations and replays only the tail. This snapshot-plus-log pattern is a recurring idea in system design interviews and shows up in databases, event sourcing, and message brokers alike.

Operational transformation vs CRDTs

Operational transformation is not the only way to solve real time collaborative editing. The main alternative is the conflict-free replicated data type, or CRDT.

A CRDT attaches a unique, globally sortable identifier to every character or element rather than relying on integer positions. Because identifiers never shift, concurrent edits merge by simple, commutative rules with no transform function and, crucially, no central server required. Two replicas that have seen the same set of operations always converge, in any order, which makes CRDTs attractive for offline-first and peer-to-peer apps.

The trade-off is cost. A conflict-free replicated data type carries metadata for every element, so memory and payload sizes grow, and deleted characters often linger as invisible tombstones. Operational transformation keeps the document representation lean but pushes complexity into the transform logic and the requirement for a server that orders operations.

Neither is universally better. Google Docs, Etherpad, and many mature editors stayed with operational transformation because they already had a reliable central server and decades of hardened transform code. Figma chose a CRDT-style model because its multiplayer design canvas benefits from server-authoritative merging of objects rather than character-level text. The right choice depends on your data model, your latency budget, and whether you need to work without a server at all. When you evaluate the two, weigh them the same way you would any consistency decision, alongside the synchronous versus asynchronous trade-offs in the rest of your system.

Conflict resolution in practice

Here is a stripped-down version of what a transform step looks like for two insert operations. Real implementations handle inserts, deletes, retains, and formatting, but the shape is the same.

transform.pypython
def transform_insert_insert(op_a, op_b):
    """Transform op_a against a concurrent op_b (both insert ops).
    Returns op_a adjusted so it can apply after op_b."""
    if op_a.pos < op_b.pos:
        return op_a  # a is before b, unaffected
    if op_a.pos > op_b.pos:
        # b inserted text before a, shift a right by b's length
        return Insert(op_a.text, op_a.pos + len(op_b.text))
    # same position: break the tie deterministically by author id
    if op_a.author < op_b.author:
        return op_a
    return Insert(op_a.text, op_a.pos + len(op_b.text))

The tie-break on the last branch matters more than it looks. When two people insert at the exact same position, the algorithm must make the same choice on every client, or documents diverge. Using a stable, deterministic rule such as comparing author identifiers guarantees that every replica resolves the tie identically. Getting these edge cases wrong is the classic way collaborative editors corrupt documents, which is why the transform matrix is tested exhaustively.

Deletes add another layer. If Alice deletes a range that Bob is simultaneously editing, the transform must shrink or drop Bob's operation so it does not point into text that no longer exists. Handling delete-versus-insert, insert-versus-delete, and delete-versus-delete correctly is most of the real work in a production operational transformation algorithm.

Scaling to millions of concurrent editors

A single document rarely has 25 million people in it. The 25 million is spread across millions of documents, and that distinction is what makes the scale manageable.

Because each document has its own authoritative server process and its own revision log, the system shards naturally by document id. One document's traffic never touches another's, so the platform scales horizontally by spreading documents across a fleet of servers. A routing layer maps each document to the server currently hosting it, and consistent hashing keeps that mapping stable as machines are added or removed.

Scaling collaborative editing by sharding documents across servers via a routing layer with consistent hashing
Sharding by document id lets the platform scale horizontally across a fleet of servers.

Within a hot document, the expensive parts are broadcasting operations to every connected client and holding all those live connections open. Presence data, meaning the colored cursors and names you see, is high volume but low value, so it is often sent over a separate, lossy channel rather than the durable operation log. Dropping a cursor update is invisible; dropping an edit is catastrophic, so the two are treated very differently.

Failure handling matters too. If a document server crashes, clients reconnect, the routing layer assigns a new server, and that server rebuilds state from the latest snapshot plus the operation log. Designing these paths so one overloaded document cannot take down its neighbors is the same discipline covered in cascading failures in distributed systems. Isolation by document, backpressure on the broadcast fan-out, and fast recovery from the log are what let the system stay up while millions of edits pour in.

Bringing it together

Real time collaborative editing looks like magic from the outside, but it rests on a small number of durable ideas. Represent edits as tiny operations. Use operational transformation to rewrite concurrent operations so intent is preserved and every client converges. Put a central server in charge of ordering and persist an append-only revision log. Shard by document to scale out, and treat presence and edits with different reliability guarantees. Whether a system reaches for operational transformation or a conflict-free replicated data type, those principles carry across.

If you are preparing for interviews or designing your own collaborative feature, this topic is one of the highest-leverage system design case studies you can study, because it forces you to reason about consistency, latency, and failure all at once. For more breakdowns like this, browse the Levelop blog or start at levelop.dev.

Frequently asked questions

What is operational transformation in simple terms?

Operational transformation is a way to merge edits from many people editing the same document at the same time. Each edit is sent as a small operation, and the algorithm rewrites concurrent operations so that no matter what order they arrive in, every user ends up with the exact same document without any edits being lost.

How does Google Docs work when many people edit at once?

Google Docs sends each keystroke to a central server as an operation. The server assigns it an order, transforms it against any concurrent operations using operational transformation, appends it to a revision log, and pushes the adjusted operation to every connected client. Each client applies operations locally for instant feedback while the server guarantees everyone converges.

What is the difference between operational transformation and a CRDT?

Operational transformation uses integer positions and a transform function, usually with a central server that orders operations. A conflict-free replicated data type gives every element a unique identifier so edits merge commutatively without a server, at the cost of extra metadata and lingering tombstones for deleted content. OT keeps the data small; CRDTs remove the need for central ordering.

Why not just use last-write-wins or locking for collaborative editing?

Last-write-wins silently discards concurrent edits, so people lose work. Locking forces users to wait for each other, which defeats the purpose of collaboration and scales poorly under contention. Operational transformation avoids both problems by letting everyone edit freely while still guaranteeing that all copies of the document converge.

Is operational transformation still used in 2026?

Yes. Mature editors such as Google Docs and Etherpad continue to rely on operational transformation because they have battle-tested transform logic and reliable central servers. Newer tools, especially offline-first and peer-to-peer apps, increasingly choose CRDTs. Both approaches are in active production use, and the right one depends on the product's data model and connectivity needs.

Keep reading

System Design

Circuit Breaker Pattern in Distributed Systems: When and How to Use It

The circuit breaker pattern explained: its three states, when to use it, and how to implement it with Resilience4j to stop cascading failures in microservices.

Read article
System Design

Synchronous vs Asynchronous Communication Explained

The choice between synchronous and asynchronous communication shapes latency, failure modes, and coupling. Here is how request-response, fire-and-forget, and event-driven differ, and when to use each.

Read article
System Design

API Gateway Best Practices: Taming One Failure Domain

An API gateway merges auth, throttling, routing and transformation into one process, making it a correlated failure domain for every service behind it. The failure modes, and what contains them.

Read article