I built Synapse to see if I could make two people edit the same paragraph offline, reconnect, and lose nothing. This is what I learned about CRDTs along the way, including two things I got wrong.
Why not OT
Operational transformation needs a server to put every edit in order and rewrite late ones. That works, but it means a client that was offline for 20 minutes comes back with a pile of old edits the server has to rebase. I did not want my design to depend on that, so I used Yjs, which is a CRDT. Every character has an identity, so copies can merge in any order and end up the same.
The first thing I got wrong
I wrote a small script where two people type at the same spot while offline, then I sent their edits to two observers in opposite orders. I expected the arrival order to decide the result. It did not. Both observers ended with the same text. Yjs breaks the tie using each copy's client ID, and every copy compares the same IDs. I only understood this when I set the IDs by hand and flipped the result by flipping the IDs.
The second thing I got wrong
My compaction code saved a snapshot and then deleted every edit up to number N. Under a test that wrote from two places at once, edits went missing. An edit gets its number before it is saved, so compaction could read "up to 10" while edit 8 was still being written, and then delete it.
The fix was not a lock. Applying the same Yjs update twice changes nothing, so loading can merge everything it finds, duplicates included, and compaction only deletes the exact records it read. Two compactions at once can no longer lose data. A property of the data type replaced a coordination protocol, and that is why I trust the choice.
What it costs
Documents get bigger than their text, because every character has an identity and deleted text leaves a marker. I merge the edit log into a snapshot every 100 edits to keep it in check.
A CRDT also only guarantees that copies agree. If one person deletes a word while another types inside it, both copies agree on the result, and it may not be what either person wanted.
How I checked it
I wrote a property based test: three copies take random inserts, deletes and formatting, sync in random orders, and must end identical. It has run 10,000 times. My first version failed after 2,771 runs and I assumed I had broken convergence. I had not. The text and formatting matched. What differed was an internal record that each copy tidies up on its own schedule. So the real guarantee is identical content immediately, and identical stored bytes once syncing goes quiet. I rewrote the test to check exactly that, and it has passed every run since.
I also simulated 30 minutes of two people typing offline, about 7,200 edits each. After reconnecting, both screens agreed in 58 ms. That was one laptop with no real network, so it shows merge cost, not what a user would wait for.
Code
Repo: https://github.com/yashwanthvarma74-ai/synapse
Longer writeup with the benchmark table: https://github.com/yashwanthvarma74-ai/synapse/blob/main/docs/blog/why-a-crdt-not-ot.md

