A Edição Colaborativa é Mais Difícil do que Parece
CRDTs vs OT na edição colaborativa em tempo real: ambos têm trade-offs que ninguém menciona. A realidade prática por trás do texto multiplayer.

O Google Docs lida com edição colaborativa em tempo real para milhões de usuários. Parece simples: você digita, seu cursor se move, e as edições do colega aparecem na hora. Mas essa simplicidade engana. Por baixo dos panos, edição colaborativa é um dos problemas mais difíceis de sistemas distribuídos, e as duas principais abordagens (Operational Transformation e CRDTs) têm trade-offs que não são óbvios até você tentar construir algo com elas.
A proposta dos CRDTs (Conflict-free Replicated Data Types) é sedutora: estruturas de dados que se fundem automaticamente sem conflitos, sem necessidade de servidor central, e convergência eventual garantida pela matemática. Mas a realidade, como várias equipes descobriram depois de escolher CRDTs para seus editores colaborativos, é mais bagunçada. A teoria é linda. A engenharia é brutal.
O Problema: Edições Concorrentes
O desafio fundamental: dois usuários editando o mesmo documento ao mesmo tempo, com atraso de rede entre eles. O usuário A insere 'Olá' na posição 5. O usuário B, que ainda não viu a edição de A, apaga o caractere na posição 5. Como o documento final deveria ficar?
Isso é ambíguo. A exclusão de B deve valer para o caractere que estava na posição 5 antes da inserção de A, ou depois? Se antes, a exclusão remove o caractere original e 'Olá' aparece. Se depois, a exclusão remove o 'O' de 'Olá'. As duas interpretações são razoáveis. O sistema precisa escolher uma e garantir que todos os clientes convirjam para o mesmo resultado. Divergência significa que dois usuários veem documentos diferentes, e essa é a única falha imperdoável.
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, a abordagem mais antiga (1989), resolve isso transformando operações umas contra as outras. Quando A recebe a operação 'deletar na posição 10' de B, o sistema de A verifica quais operações A já aplicou que B ainda não viu. Ele transforma a operação de B para levar essas mudanças em conta: como A inseriu 5 caracteres na posição 10, a exclusão de B agora deve valer para a posição 15 (o caractere original foi deslocado para a direita).
Isso funciona, e o Google Docs usa essa abordagem. O problema são as funções de transformação. Para um editor de texto com operações de inserção e exclusão, você precisa definir como cada par de operações se transforma em relação ao outro. Com dois tipos de operação, são quatro casos de transformação. Adicione formatação, tabelas, imagens, listas e comentários, e o número de casos explode combinatoriamente. Cada caso precisa estar correto, e bugs sutis causam divergência do documento que é quase impossível de reproduzir e depurar.
OT também tradicionalmente exige um servidor central para estabelecer uma ordenação total das operações. Sem servidor, operações concorrentes podem ser transformadas em ordens diferentes por clientes diferentes, produzindo resultados diferentes. O Google pode arcar com um servidor central para o Docs. Uma aplicação peer-to-peer não consegue usar OT facilmente.
CRDTs: A Promessa Teórica
Os CRDTs adotam uma abordagem fundamentalmente diferente. Em vez de transformar operações, usam estruturas de dados em que todas as ordens possíveis de fusão produzem o mesmo resultado. Isso é uma garantia matemática: se a estrutura de dados é um CRDT válido, a convergência é automática. Sem funções de transformação, sem servidor central, sem dependência de ordenação.
Para edição de texto, as principais variantes de CRDT são RGA (Replicated Growable Array) e outros CRDTs de sequência semelhantes. Em vez de rastrear posições (que mudam conforme as edições acontecem), eles atribuem a cada caractere um identificador único e ordenado globalmente. Inserções criam novos IDs entre os existentes. Exclusões marcam um ID como tombstone (não é removido, falo mais sobre isso adiante).
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)
CRDTs: A Realidade Prática
A promessa dos CRDTs (sem conflitos, descentralizados, convergência garantida matematicamente) é real. Mas os desafios de engenharia são significativos.
Tombstones se acumulam. Quando você apaga um caractere em um CRDT, ele não pode ser removido da estrutura de dados, pois outras réplicas podem não ter visto a exclusão ainda e precisam do ID para posicionar corretamente as próprias edições. Então caracteres apagados são marcados como tombstones: invisíveis, mas ainda presentes. Um documento muito editado acumula milhares de tombstones. Um documento de 1000 caracteres pode ter 50.000 tombstones do histórico de edição. Isso incha a memória e deixa as operações mais lentas.
O overhead de metadados é enorme. Cada caractere precisa de um ID único (identificador do usuário + timestamp lógico), ponteiros para os IDs vizinhos e flags de tombstone. Os metadados por caractere podem chegar a 50-100 bytes. Para um documento de 100KB, a representação em CRDT pode ter 5-10MB. Isso pesa na sincronização, pois enviar o estado completo do CRDT pela rede é caro.
O desempenho piora com o histórico. Operações em um CRDT de sequência não são O(1). Inserir um caractere exige encontrar a posição correta na sequência ordenada por IDs, que depende do número total de IDs (incluindo tombstones). Em documentos com históricos de edição longos, isso fica mensuravelmente lento.
Preservar a intenção é difícil. Os CRDTs garantem convergência: todas as réplicas chegam ao mesmo estado. Mas chegam ao estado certo? Quando dois usuários editam a mesma palavra ao mesmo tempo, o CRDT intercala os caracteres deles de forma determinística. O resultado converge, mas pode não fazer sentido. OT pode ser projetado para manter a edição de um usuário intacta e aplicar a do outro ao redor. Os CRDTs fundem de forma mecânica.
Yjs e o Meio-Termo Prático
Yjs é a biblioteca CRDT mais popular para JavaScript e alimenta recursos colaborativos em muitas aplicações web. É bem engenheirada e resolve vários problemas teóricos dos CRDTs com otimizações práticas: a codificação binária compacta reduz o overhead de metadados, e a coleta de lixo dos tombstones funciona quando todos os clientes estão online.
Mas o Yjs não é mágica. Equipes que o adotam descobrem que uma biblioteca de CRDT resolve o problema de convergência, mas não o problema de colaboração. Você ainda precisa de: um servidor de sinalização para descoberta de peers, uma camada de persistência para mudanças offline, resolução de conflitos para operações de alto nível (o que acontece quando dois usuários reestruturam a mesma seção?), rastreamento de presença (cursores, seleções), desfazer/refazer que respeite as edições de outros usuários e gerenciamento de permissões.
Algumas equipes, depois de construir um editor colaborativo com Yjs, concluíram que a abordagem CRDT adiciona complexidade desnecessária. Se sua aplicação tem um servidor central (e a maioria tem), OT ou abordagens ainda mais simples (last-write-wins com detecção de conflitos) podem ser mais práticas. Os CRDTs brilham em cenários genuinamente peer-to-peer: aplicações offline-first, software local-first e sistemas em que nenhuma autoridade central pode ser presumida.
O Que a Maioria das Aplicações Deveria Fazer
Se você está adicionando edição colaborativa à sua aplicação, aqui está um framework de decisão pragmático.
- Se você tem um servidor central e seus documentos são pequenos: Use OT. A abordagem do Google funciona. Bibliotecas como o ShareDB implementam OT para Node.js. O servidor central simplifica tudo: ordenação, persistência, resolução de conflitos, permissões.
- Se você precisa de suporte offline ou peer-to-peer: Use CRDTs (Yjs ou Automerge). CRDTs são a única abordagem que lida corretamente com edição offline de verdade e sincronização peer-to-peer. Aceite o overhead de metadados e o acúmulo de tombstones como custo da descentralização.
- Se seus colaboradores raramente editam a mesma seção ao mesmo tempo: Você talvez não precise de nenhuma das duas. Locking simples (apenas um editor por seção) ou last-write-wins com uma boa interface de conflito cobre muitos padrões reais de colaboração sem a complexidade de OT ou CRDTs.
- Se você está construindo um concorrente do Google Docs: Você precisa de uma equipe de engenheiros de sistemas distribuídos e anos de desenvolvimento. Não é projeto de fim de semana. A simplicidade superficial da edição colaborativa esconde uma complexidade extraordinária.
A Avaliação Honesta
Edição colaborativa é um desses problemas em que a dificuldade não é óbvia. O caminho feliz (dois usuários fazendo edições que não se sobrepõem) funciona com quase qualquer abordagem. Os casos difíceis (edições concorrentes na mesma região, edição offline com sincronização posterior, estruturas de documento complexas como tabelas, listas aninhadas e objetos embutidos) exigem um entendimento profundo dos trade-offs entre OT e CRDTs.
Nenhuma abordagem é estritamente melhor. OT é mais simples com servidor, mais difícil sem ele. CRDTs funcionam sem servidor, mas carregam overhead de metadados e acúmulo de tombstones. Ambos exigem engenharia cuidadosa além do algoritmo central, e essa engenharia (presença, persistência, permissões, desfazer) costuma ser mais difícil que o próprio problema de convergência.
A indústria está lentamente convergindo para um híbrido pragmático: CRDTs para o modelo de dados (garantias automáticas de fusão), com um servidor para coordenação (presença, permissões, coleta de lixo). Isso dá o melhor dos dois mundos, convergência matemática mais infraestrutura prática. Mas também traz a complexidade dos dois mundos, e é por isso que edição colaborativa continua sendo um problema difícil depois de 35 anos de pesquisa.


