Last modified: March 23, 2026

This article is written in: 🇺🇸

Operational Transform (OT)

Operational Transform is a foundational technique in distributed systems that enables real-time collaborative editing of shared documents. Originally proposed by Ellis and Gibbs in 1989, OT allows multiple users to concurrently modify the same document while preserving consistency across all participants. The core idea is to transform incoming operations against previously executed operations so that every replica converges to the same final state regardless of the order in which edits arrive.

[User A]              [User B]              [User C]
      |                     |                     |
      | op_a: Insert("X",3) | op_b: Delete(5)    | op_c: Insert("Z",1)
      |                     |                     |
      v                     v                     v
 +-----------+        +-----------+         +-----------+
 | OT Engine |<------>| OT Engine |<------->| OT Engine |
 | (Client)  |        | (Client)  |         | (Client)  |
 +-----------+        +-----------+         +-----------+
       \                    |                    /
        \                   |                   /
         \                  v                  /
          \        +-----------------+        /
           +------>|   OT Server     |<------+
                   | (Central Relay) |
                   +--------+--------+
                            |
                            v
                   +-------------------+
                   |  Shared Document  |
                   | "Hello, World!"   |
                   +-------------------+

Collaborative Editing Challenges

Collaborative editing introduces several fundamental problems in distributed systems:

User A (Local State)               User B (Local State)
  "ABCD"                             "ABCD"
    |                                   |
    | Insert('X', pos=2)                | Delete(pos=3)
    v                                   v
  "ABXCD"                            "ABD"
    |                                   |
    |--- op_a sent over network ------->|  (arrives late)
    |<------ op_b sent over network ----|  (arrives late)
    |                                   |
    | Naive apply Delete(pos=3)         | Naive apply Insert('X', pos=2)
    v                                   v
  "ABCD"  (WRONG! Lost 'X')          "ABXD"  (DIFFERENT! Inconsistent)

How OT Works

OT resolves conflicts by transforming each incoming remote operation against local operations that have already been applied. This transformation adjusts positions and parameters so the intent of every edit is preserved.

The transformation process follows these steps:

User A: Insert('X', pos=2)         User B: Delete(pos=3)
  on document "ABCD"                 on document "ABCD"

         Transform Function: T(op_a, op_b)
        +-------------------------------+
        | Input:                        |
        |   op_a = Insert('X', pos=2)   |
        |   op_b = Delete(pos=3)        |
        |                               |
        | Logic:                        |
        |   op_a.pos <= op_b.pos        |
        |   so shift op_b.pos by +1     |
        |                               |
        | Output:                       |
        |   op_a' = Insert('X', pos=2)  |
        |   op_b' = Delete(pos=4)       |
        +-------------------------------+

  User A applies op_b': Delete(pos=4)    User B applies op_a': Insert('X', pos=2)
  "ABXCD" -> "ABXD"                      "ABD" -> "ABXD"
              ^                                     ^
              Both converge to "ABXD"

Transform Function Example

The transform function T(op_a, op_b) takes two concurrent operations and returns adjusted versions that can be safely applied in either order. Below is a simplified example for insert-vs-insert and insert-vs-delete cases:

T(Insert(pos_a), Insert(pos_b)):
  +-----------------------------------------+
  |  if pos_a < pos_b:                      |
  |      return Insert(pos_a), Insert(pos_b + 1) |
  |  else if pos_a > pos_b:                 |
  |      return Insert(pos_a + 1), Insert(pos_b) |
  |  else:  (tie-break by user ID)          |
  |      if user_a < user_b:                |
  |          return Insert(pos_a), Insert(pos_b + 1) |
  |      else:                              |
  |          return Insert(pos_a + 1), Insert(pos_b) |
  +-----------------------------------------+

Types of Operations

OT systems define a set of primitive operations that cover all possible document mutations:

Convergence Properties

For an OT system to guarantee correctness, the transform function must satisfy key mathematical properties:

TP1 Convergence Guarantee:
  +---------------------------------------------------+
  |                                                   |
  |  State S                                          |
  |    |            \                                 |
  |    | apply(a)    \ apply(b)                       |
  |    v              v                               |
  |  State S_a      State S_b                         |
  |    |                \                             |
  |    | apply(T(b,a))   \ apply(T(a,b))              |
  |    v                  v                           |
  |  State S_ab  ===  State S_ba   (must be equal)    |
  |                                                   |
  +---------------------------------------------------+

Server-Based vs Peer-to-Peer OT

OT architectures fall into two main categories depending on how operations are coordinated across participants:

Server-based OT uses a central server as the single source of truth:

Peer-to-peer OT has no central authority:

OT vs CRDT Comparison

Conflict-free Replicated Data Types (CRDTs) offer an alternative approach to collaborative editing. While OT transforms operations, CRDTs design data structures that merge automatically without conflicts.

Aspect OT CRDT
Conflict Resolution Transforms operations at runtime Conflicts are impossible by design
Server Requirement Often relies on a central server Works fully peer-to-peer
Complexity Complex transform functions Complex data structures
Memory Overhead Low (stores operations) Higher (stores metadata per character)
Proven at Scale Google Docs, Apache Wave Yjs, Automerge, Apple Notes
Correctness Must satisfy TP1/TP2 (error-prone) Mathematically guaranteed
Undo Support Straightforward with inverse operations More difficult to implement
Network Requirement Typically needs ordered delivery Works with any delivery order

Key considerations when choosing between them:

Implementing OT

Building an OT system requires careful attention to several engineering challenges:

Popular OT implementations include: