CRDT vs OT: How Real-Time Collaboration Works
CRDTs and OT both enable real-time co-editing. CRDTs merge conflict-free without a central server. OT transforms operations around a central server. Here's the full comparison.
CRDTs and Operational Transformation are the two approaches to building real-time collaborative editing. I built LetX on CRDTs after evaluating both. Here's an honest technical comparison — what each one actually does, where each breaks down, and when to choose which.
The core problem
When two users edit the same document simultaneously, you get conflicts:
Initial: "Hello world"
User A: delete "world" → "Hello "
User B: insert "there" → "Hello there world"
Naive merge: "Hello there " (wrong) or conflict error
Both OT and CRDTs solve this, but differently.
Operational Transformation (OT)
OT transforms an operation based on concurrent operations that happened before it:
User A op: DELETE(6, 5) // delete 5 chars at position 6 ("world")
User B op: INSERT(6, "there ") // insert at position 6
Server transforms A's op given B's op:
DELETE(6+6, 5) = DELETE(12, 5) // position shifted by B's insertion
Final: "Hello there world" → delete "world" → "Hello there "
OT requires a central server to linearize all operations. Every client sends ops to the server; the server transforms and broadcasts. No central server = no guaranteed convergence.
This is how Google Docs works. It's battle-tested, highly optimized, and the academic literature on OT is mature.
OT failure modes
- Server as SPOF: if the server is down, collaboration stops
- Transform functions are hard: for rich text with complex formatting, transform functions are notoriously difficult to get right (CKEditor's OT implementation has had years of edge-case bugs)
- Latency sensitivity: every op must round-trip through the server before being applied
Conflict-free Replicated Data Types (CRDTs)
CRDTs define data structures where any two concurrent operations can be merged in any order and produce the same result. No server arbitration needed.
For text, the dominant CRDT is RGA (Replicated Growable Array). Every character gets a globally unique ID (timestamp + site ID). Insert position is described relative to neighbors, not index:
type Element struct {
ID ElementID // (lamport_clock, site_id)
Char rune
Deleted bool // tombstone, not physical removal
Left ElementID // left neighbor at insertion time
}
When two users insert at the "same" position, IDs break the tie deterministically. No server needed — clients can sync peer-to-peer or through a dumb relay.
CRDT failure modes
- Tombstones accumulate: deleted characters stay in the structure (just marked deleted). Long-lived documents need periodic compaction.
- Memory overhead: each character carries metadata (~40 bytes vs ~2 bytes for plain text). A 100k-character document has ~4MB of CRDT metadata.
- Op log size: for history/undo, the op log is large. LetX uses snapshots + delta logs to bound this.
- Complex sequences: some CRDT variants (like YATA, used in Yjs) have known edge cases in highly concurrent interleaved insertions.
Side-by-side comparison
| | OT | CRDT | |---|---|---| | Central server required | ✅ Yes | ❌ No | | Peer-to-peer capable | ❌ No | ✅ Yes | | Offline editing | Limited | ✅ Strong | | Memory overhead | Low | Higher (tombstones) | | Transform complexity | High (rich text) | Lower | | Battle-tested examples | Google Docs, Notion | Figma, Linear, LetX | | Max document size | ~No limit | Bounded by tombstone accumulation |
When to use which
Choose OT if:
- You already have centralized infrastructure and don't need P2P
- Your data model is simple (plain text, not rich structured docs)
- You're building on top of an OT library (ShareDB, Yjs-OT) with existing transform functions
Choose CRDTs if:
- Offline-first is a hard requirement
- You want to avoid single-server coordination
- You're building a product where P2P sync matters (local-network collaboration, edge cases with intermittent connectivity)
- Your team can manage tombstone compaction
I chose CRDTs for LetX because LaTeX documents are worked on in environments with intermittent connectivity (research labs, libraries, conferences) and I wanted offline editing to be first-class.
Libraries worth knowing
| Library | Type | Language | Notes | |---------|------|----------|-------| | Yjs | CRDT (YATA) | TypeScript | Most popular, rich ecosystem | | Automerge | CRDT | TypeScript/Rust | Strong JSON CRDT, Rust core | | ShareDB | OT | Node.js | Battle-tested, used at many companies | | diamond-types | CRDT | Rust | Extremely fast, newer |
FAQ
Which is better: CRDT or OT? Neither is universally better. CRDTs excel at offline-first and P2P. OT is simpler for server-centric architectures and has more mature tooling. Choose based on your offline and P2P requirements.
Does Google Docs use OT or CRDT? Google Docs uses OT. It requires Google's servers to coordinate all edits, which is why it doesn't work well offline.
Does Figma use CRDTs? Figma uses a CRDT-inspired approach for their multiplayer. Linear uses CRDTs. Notion uses OT.
What CRDT does LetX use? LetX uses a variant of RGA (Replicated Growable Array) implemented in Go for the server and TypeScript for the client.
Can you mix OT and CRDT? Not at the algorithm level — they're fundamentally different approaches. But you can use each for different parts of a system (e.g., CRDT for document content, OT for cursor positions).
Written by Shihab Shahriar Antor — AI Engineer & Founder of Shahriar Labs. See also: How I Built LetX · Real-Time Apps With WebSockets in Go.