Artikel mendalam tentang teknologi yang membentuk masa depan.

Collaborative Editing Lebih Sulit dari yang Kamu Kira

CRDT vs OT untuk real-time collaborative editing: keduanya punya trade-off yang jarang dibahas. Realita praktis di balik multiplayer text.

Dua tangan hantu mengetik di satu keyboard, dengan aliran cahaya yang bertabrakan di antara keduanya

Google Docs menangani real-time collaborative editing untuk jutaan pengguna. Kelihatannya mudah: kamu mengetik, kursormu bergerak, dan edit dari kolaboratormu langsung muncul. Kesederhanaan ini menipu. Di balik layar, collaborative editing adalah salah satu masalah tersulit di distributed systems, dan dua pendekatan utama untuk menyelesaikannya (Operational Transformation dan CRDT) punya trade-off yang tidak kelihatan sampai kamu mencoba membangun sesuatu dengannya.

Pitch untuk CRDT (Conflict-free Replicated Data Types) memang menggoda: struktur data yang otomatis di-merge tanpa konflik, tidak butuh central server, dan eventual consistency dijamin oleh matematika. Tapi kenyataannya, seperti yang dialami beberapa tim setelah memilih CRDT untuk editor kolaboratif mereka, jauh lebih berantakan. Teorinya indah. Engineering-nya brutal.

Masalahnya: Concurrent Edits

Tantangan utamanya: dua pengguna mengedit dokumen yang sama secara bersamaan, dengan delay jaringan di antara mereka. User A menyisipkan 'Hello' di posisi 5. User B, yang belum melihat edit A, menghapus karakter di posisi 5. Seharusnya seperti apa hasil akhir dokumennya?

Ini ambigu. Apakah delete B seharusnya berlaku untuk karakter yang ada di posisi 5 sebelum insert A, atau sesudahnya? Kalau sebelum, delete menghapus karakter asli dan 'Hello' muncul. Kalau sesudah, delete menghapus 'H' dari 'Hello.' Dua interpretasi ini sama-sama masuk akal. Sistem harus memilih salah satu dan memastikan semua client konvergen ke hasil yang sama. Divergence berarti dua pengguna melihat dokumen yang berbeda, dan ini adalah failure mode yang tidak bisa dimaafkan.

The convergence problem:
Initial document: "The quick brown fox"
^ position 10
User A (at time T):  Insert "very " at position 10
User B (at time T):  Delete character at position 10
A sees locally: "The quick very brown fox"
B sees locally: "The quick rown fox"  (deleted 'b')
Now A receives B's operation: delete at position 10
But A already inserted 5 chars at position 10...
Should B's delete now apply at position 10? (deletes 'v' from 'very')
Or at position 15? (deletes 'b' from 'brown' — the original target)
Both A and B must arrive at the same document.
This is the problem OT and CRDTs solve differently.

Operational Transformation (OT)

OT, pendekatan yang lebih tua (1989), menyelesaikan ini dengan men-transform operasi terhadap operasi lain. Saat A menerima 'delete di posisi 10' dari B, sistem A mengecek operasi apa saja yang sudah A terapkan tapi belum dilihat B. Lalu sistem men-transform operasi B untuk memperhitungkan perubahan itu: karena A menyisipkan 5 karakter di posisi 10, delete B sekarang harus berlaku di posisi 15 (karakter target aslinya bergeser ke kanan).

Cara ini berhasil, dan Google Docs memakainya. Masalahnya ada di transformation function. Untuk text editor dengan operasi insert dan delete, kamu harus mendefinisikan bagaimana setiap pasangan operasi saling men-transform. Dengan dua tipe operasi, ada empat kasus transformasi. Tambahkan formatting, tabel, gambar, list, dan komentar, dan jumlah kasusnya meledak secara kombinatorial. Setiap kasus harus benar, dan bug kecil bisa menyebabkan divergence dokumen yang hampir mustahil direproduksi dan di-debug.

OT juga secara tradisional butuh central server untuk menetapkan total ordering dari operasi. Tanpa server, operasi concurrent bisa di-transform dengan urutan berbeda oleh client yang berbeda, dan hasilnya pun ikut berbeda. Google mampu membiayai central server untuk Docs. Aplikasi peer-to-peer tidak bisa dengan mudah memakai OT.

CRDT: Janji di Atas Kertas

CRDT mengambil pendekatan yang fundamentally berbeda. Alih-alih men-transform operasi, mereka memakai struktur data di mana semua kemungkinan urutan merge menghasilkan hasil yang sama. Ini adalah jaminan matematis: kalau struktur datanya adalah CRDT yang valid, konvergensi terjadi secara otomatis. Tidak ada transformation function, tidak ada central server, tidak ada ketergantungan pada urutan.

Untuk text editing, varian CRDT utamanya adalah RGA (Replicated Growable Array) dan sequence CRDT lain yang mirip. Alih-alih melacak posisi (yang bergeser setiap ada edit), mereka memberi setiap karakter identifier unik dan terurut secara global. Insert membuat ID baru di antara ID yang sudah ada. Delete menandai ID sebagai tombstone (tidak benar-benar dihapus, nanti dibahas lebih lanjut).

CRDT sequence representation:
Document: "cat"
Internal representation (simplified):
ID: (A,1) → 'c'   (user A, logical clock 1)
ID: (A,2) → 'a'   (user A, logical clock 2)
ID: (A,3) → 't'   (user A, logical clock 3)
User B inserts 'h' between 'c' and 'a':
ID: (B,1) → 'h'   position: between (A,1) and (A,2)
Document: "chat"
User A (concurrently) inserts 'r' between 'c' and 'a':
ID: (A,4) → 'r'   position: between (A,1) and (A,2)
Both IDs go between the same characters.
The CRDT uses a deterministic tie-breaking rule
(e.g., higher user ID wins) to order them.
Final document: "chart" or "chrat"
(deterministic — all clients get the same result)

CRDT: Realita di Lapangan

Janji CRDT (conflict-free, terdesentralisasi, konvergensi yang dijamin secara matematis) memang nyata. Tapi tantangan engineering-nya signifikan.

Tombstone menumpuk. Saat kamu menghapus karakter di CRDT, karakter itu tidak bisa dihapus dari struktur data, karena replica lain mungkin belum melihat delete tersebut dan masih butuh ID-nya untuk memposisikan edit mereka dengan benar. Jadi karakter yang dihapus ditandai sebagai tombstone: tidak terlihat, tapi masih ada. Dokumen yang sering diedit bisa menumpuk ribuan tombstone. Dokumen 1000 karakter bisa punya 50.000 tombstone dari riwayat editing. Ini membengkakkan memori dan memperlambat operasi.

Overhead metadata-nya sangat besar. Setiap karakter butuh ID unik (identifier user + logical timestamp), pointer ke ID tetangga, dan flag tombstone. Metadata per karakter bisa mencapai 50-100 byte. Untuk dokumen 100KB, representasi CRDT-nya bisa 5-10MB. Ini jadi masalah untuk sync, karena mengirim full state CRDT lewat jaringan itu mahal.

Performa menurun seiring riwayat. Operasi pada sequence CRDT tidak O(1). Menyisipkan karakter butuh mencari posisi yang tepat dalam urutan berbasis ID, dan itu bergantung pada total jumlah ID (termasuk tombstone). Untuk dokumen dengan riwayat editing yang panjang, ini jadi terasa lambat.

Intention preservation itu susah. CRDT menjamin konvergensi, semua replica mencapai state yang sama. Tapi apakah mereka mencapai state yang benar? Saat dua pengguna mengedit kata yang sama secara bersamaan, CRDT menginterleave karakter mereka secara deterministik. Hasilnya konvergen, tapi bisa jadi tidak masuk akal. OT bisa didesain untuk menjaga edit satu pengguna tetap utuh dan menerapkan edit pengguna lain di sekitarnya. CRDT men-merge secara mekanis.

Yjs dan Jalan Tengah yang Praktis

Yjs adalah library CRDT paling populer untuk JavaScript dan menjadi fondasi fitur kolaboratif di banyak aplikasi web. Engineering-nya bagus dan menjawab banyak masalah teoretis CRDT lewat optimasi praktis: binary encoding yang compact mengurangi overhead metadata, dan garbage collection untuk tombstone bekerja saat semua client online.

Tapi Yjs bukan sihir. Tim yang mengadopsinya biasanya sadar bahwa library CRDT menyelesaikan masalah konvergensi, tapi bukan masalah kolaborasi. Kamu tetap butuh: signaling server untuk peer discovery, persistence layer untuk perubahan offline, resolusi konflik untuk operasi level tinggi (apa yang terjadi kalau dua pengguna merestrukturisasi section yang sama?), presence tracking (kursor, seleksi), undo/redo yang menghormati edit pengguna lain, dan manajemen permission.

Beberapa tim, setelah membangun editor kolaboratif di atas Yjs, menyimpulkan bahwa pendekatan CRDT menambah kompleksitas yang sebenarnya tidak mereka butuhkan. Kalau aplikasimu punya central server (dan kebanyakan punya), OT atau pendekatan yang lebih sederhana (last-write-wins dengan deteksi konflik) mungkin lebih praktis. CRDT bersinar untuk skenario peer-to-peer sungguhan: aplikasi offline-first, local-first software, dan sistem di mana tidak ada otoritas pusat yang bisa diasumsikan.

Yang Sebaiknya Dilakukan Kebanyakan Aplikasi

Kalau kamu mau menambahkan collaborative editing ke aplikasimu, ini framework keputusan yang pragmatis.

  • Kalau kamu punya central server dan dokumennya kecil: Pakai OT. Pendekatan Google itu berhasil. Library seperti ShareDB mengimplementasikan OT untuk Node.js. Central server menyederhanakan hampir semuanya: ordering, persistence, resolusi konflik, permission.
  • Kalau kamu butuh offline support atau peer-to-peer: Pakai CRDT (Yjs atau Automerge). CRDT adalah satu-satunya pendekatan yang menangani offline editing dan sinkronisasi peer-to-peer dengan benar. Terima overhead metadata dan penumpukan tombstone sebagai harga dari desentralisasi.
  • Kalau kolaboratormu jarang mengedit section yang sama secara bersamaan: Kamu mungkin tidak butuh keduanya. Locking sederhana (hanya satu editor per section) atau last-write-wins dengan UI konflik yang baik sudah mencakup banyak pola kolaborasi di dunia nyata tanpa kompleksitas OT atau CRDT.
  • Kalau kamu sedang membangun pesaing Google Docs: Kamu butuh tim distributed systems engineer dan bertahun-tahun pengembangan. Ini bukan proyek akhir pekan. Kesederhanaan permukaan dari collaborative editing menyembunyikan kompleksitas yang luar biasa.

Penilaian Jujur

Collaborative editing adalah salah satu masalah di mana tingkat kesulitannya tidak langsung terlihat. Happy path, yaitu dua pengguna yang membuat edit tidak saling tumpang tindih, bisa berjalan dengan hampir semua pendekatan. Kasus sulitnya, seperti edit concurrent di area yang sama, offline editing dengan sync belakangan, atau struktur dokumen yang kompleks (tabel, nested list, embedded object), membutuhkan pemahaman mendalam tentang trade-off antara OT dan CRDT.

Tidak ada pendekatan yang benar-benar lebih baik. OT lebih sederhana dengan server, tapi lebih sulit tanpa server. CRDT bisa berjalan tanpa server, tapi membawa overhead metadata dan penumpukan tombstone. Keduanya butuh engineering hati-hati di luar algoritma inti, dan engineering itu (presence, persistence, permission, undo) sering kali lebih sulit daripada masalah konvergensi itu sendiri.

Industri perlahan menuju hybrid yang pragmatis: CRDT untuk data model (jaminan merge otomatis), ditambah server untuk koordinasi (presence, permission, garbage collection). Ini memberimu yang terbaik dari kedua dunia: konvergensi matematis plus infrastruktur yang praktis. Tapi kamu juga mendapat kompleksitas dari kedua dunia itu, dan inilah alasan mengapa collaborative editing tetap menjadi masalah sulit setelah 35 tahun riset.