wandres.dev
ESTADO COLABORATIVO · tiempo real y presencia

Elegir: OT o CRDT, servidor autoritativo o pares

El nivel cierra con la decisión que las tres lecciones anteriores prepararon, y empieza deshaciendo el error de planteamiento más común: OT frente a CRDT no es la misma pregunta que servidor frente a red entre pares. Son dos ejes independientes, y de sus cuatro cuadrantes hay tres perfectamente habitados en producción, incluido el que más ha crecido en los últimos años, el CRDT con servidor autoritativo. Esta lección sitúa las alternativas en ese plano, expone los costes que no salen en las comparativas de blog —el consumo de memoria por metadatos, la imposibilidad de aplicar permisos por campo sin un árbitro, la dificultad de deshacer en OT, el peso del historial que no se puede recoger, el coste operativo de mantener un secuenciador con estado en memoria— y convierte todo ello en un método de decisión de cuatro preguntas ordenadas por su capacidad de descartar: cuánto tiempo debe funcionar sin conexión, quién debe poder decir que no a una escritura, qué forma tiene el dato, y qué se puede comprar hecho. Termina con el criterio que sobrevive al cambio de las herramientas: la elección no es de algoritmo sino de dónde vive la autoridad, y esa es una decisión de producto antes que de ingeniería.

⏱ 20 min

Llegados aquí ya tienes las dos familias en la mano y sabes lo que cada una promete y lo que cada una calla. Falta la parte que decide proyectos, y empieza por corregir una confusión que arruina la mayoría de las discusiones sobre este tema. Se suele plantear la elección como una única pregunta binaria, OT o CRDT, y se le asocia sin pensar una segunda que parece venir incluida, servidor o red entre pares, como si escoger CRDT implicase eliminar el servidor y escoger OT implicase depender de él para siempre. No es así. La forma de fundir cambios y el lugar donde reside la autoridad son ejes ortogonales, y confundirlos lleva a rechazar arquitecturas excelentes por motivos que no existen. El cuadrante más productivo del panorama actual es precisamente el que esa confusión hace invisible: CRDT con servidor autoritativo, es decir, una estructura de datos que sabe fundir sola y un servidor que además persiste, valida, aplica permisos y actúa de testigo para poder recoger basura. Esta lección coloca las opciones en su plano correcto, saca a la luz los costes que las comparativas superficiales omiten y termina proponiendo un método de decisión que sigue funcionando cuando cambien los nombres de las librerías, porque no pregunta por librerías sino por la naturaleza del producto.

🎯 Al terminar esta lección sabrás
  • Separar los dos ejes de la decisión: algoritmo de fusión y topología de autoridad.
  • Recorrer los cuadrantes habitados y saber qué producto vive bien en cada uno.
  • Inventariar los costes ocultos: metadatos, permisos, deshacer, historial y operación del servidor.
  • Aplicar un método de cuatro preguntas ordenadas por capacidad de descartar alternativas.

Dos ejes, cuatro cuadrantes

El primer eje es el algoritmo de fusión y responde a la pregunta de cómo se reconcilian dos cambios concurrentes: transformando índices contra la historia, que es OT, o dando identidad estable a cada átomo para que la fusión sea una unión, que es CRDT. El segundo eje es la topología de autoridad y responde a una pregunta completamente distinta: quién tiene derecho a decir que no. En un sistema con servidor autoritativo existe un componente que puede rechazar una escritura por permisos, por validación de esquema o por reglas de negocio, y que además conserva la copia canónica. En un sistema entre pares no existe tal componente: cada réplica acepta lo que le llega y la única garantía es que todas acabarán igual.

🏛️

OT con servidor: el clásico industrial

Google Docs y la familia ShareDB. Orden total, historial lineal, permisos y validación en el servidor, memoria mínima en el cliente. A cambio, el servidor es imprescindible y el trabajo sin conexión es pobre.

🌐

CRDT con servidor: el cuadrante que crece

Yjs con un servidor de sincronización, Automerge con su servidor, y los productos comerciales del sector. Fusión sin coordinación más un árbitro que persiste, valida y sirve de testigo para compactar. Lo mejor de ambos ejes con el impuesto de metadatos.

🔗

CRDT entre pares: local-first puro

Sin servidor de verdad, con un relé tonto o una red directa entre navegadores. Funciona desconectado indefinidamente y admite cifrado de extremo a extremo, porque el relé no necesita leer nada. Renuncia a permisos por campo y a la validación central.

🚫

OT entre pares: el cuadrante vacío

Teóricamente posible y prácticamente abandonado: exige satisfacer TP2, la propiedad que resistió veinte años de literatura. Si necesitas topología entre pares, el motivo mismo te empuja a la otra familia.

flowchart TD
A[necesito colaboracion en tiempo real] --> B{debe funcionar sin conexion largo tiempo?}
B -->|si, dias o semanas| C[CRDT]
B -->|no, minutos como mucho| D{alguien debe poder rechazar una escritura?}
D -->|si, permisos y validacion estricta| E[servidor autoritativo]
D -->|no, todos los pares son iguales| C
C --> F{hace falta persistir, validar o compactar?}
F -->|si| G[CRDT con servidor de sincronizacion]
F -->|no| H[CRDT entre pares con rele]
E --> I{quiero orden total y auditoria lineal?}
I -->|si| J[OT con servidor]
I -->|no| G
style G fill:#a6e3a1,color:#11111b
style H fill:#94e2d5,color:#11111b
style J fill:#89b4fa,color:#11111b

La lectura importante del diagrama es que la primera pregunta descarta un eje entero y la segunda descarta el otro, y que hacerlas en ese orden ahorra discusiones inútiles. Si tu producto debe funcionar desconectado durante días, el eje del algoritmo ya está decidido y no hay nada que debatir: OT necesita reconciliar contra una historia y esa reconciliación se degrada con el tiempo de ausencia. Si tu producto exige que un servidor pueda rechazar una escritura porque este usuario no puede editar esta celda, el eje de la topología también está decidido, y da igual qué familia de fusión elijas después. Solo cuando ninguna de las dos preguntas es tajante entra en juego el resto de criterios, y entonces conviene mirar los costes que nadie enseña.

Los costes que no salen en la comparativa

Las tablas comparativas suelen limitarse a la corrección y a la latencia, que son precisamente los ejes en los que ambas familias están resueltas. Los costes que de verdad deciden aparecen después, cuando el producto lleva meses en producción.

💾

Memoria e historia

Un CRDT guarda identidad por átomo y lápidas por borrado. Un documento muy editado puede pesar varias veces su contenido visible, y recoger esa basura exige saber que todos han visto los borrados, es decir, coordinación. OT no tiene este problema: guarda texto plano y un registro de operaciones que sí se puede truncar.

🔐

Permisos y validación

Aplicar reglas del tipo este usuario solo edita esta sección requiere un árbitro que inspeccione y rechace. En una topología entre pares eso no existe, y lo único que se puede hacer es cifrar y confiar. Si tu dominio tiene permisos finos, el servidor no es opcional.

↩️

Deshacer

Deshacer en colaboración significa deshacer lo mío, no lo último que pasó. En CRDT esto es relativamente natural porque cada operación tiene identidad y autor. En OT exige transformar la operación inversa contra todo lo ocurrido después, que es de las partes más delicadas de una implementación.

⚙️

Coste operativo

Un servidor de OT mantiene estado en memoria por documento y debe secuenciar: escala mal horizontalmente y una caída interrumpe la sesión. Un servidor de sincronización de CRDT puede ser casi sin estado, porque la corrección no depende de él, solo la persistencia.

// El punto donde los dos ejes se tocan de verdad: rechazar una escritura.
// Con servidor autoritativo esto existe; entre pares, no hay donde ponerlo.
function alRecibirCambio(usuario: Usuario, cambio: Cambio) {
  if (!puedeEditar(usuario, cambio.seccion)) return rechazar(cambio)
  if (!esquema.safeParse(cambio.valor).success) return rechazar(cambio)
  aplicarYDifundir(cambio)          // solo ahora entra en el documento
}

Conviene además situar una tercera categoría que no encaja limpiamente en ninguno de los dos ejes y que domina cada vez más productos: los motores de sincronización. En lugar de sincronizar un documento colaborativo, sincronizan el conjunto de registros que un cliente necesita, aplican los cambios locales al instante y los reconcilian después contra un servidor que sigue siendo la autoridad. Su reconciliación no suele ser ni OT ni un CRDT completo, sino un esquema más modesto de mutaciones con identidad, reintento y reordenación, que resulta suficiente porque su granularidad es el registro y no el carácter. Si tu producto se parece más a un gestor de incidencias que a un editor de texto, ese es probablemente tu cuadrante real, y llegar a él tras haber entendido las dos familias es la única manera de elegirlo por convicción y no por descarte.

ℹ️
La colaboración fina es una decisión de producto con coste permanente

Adoptar un motor de fusión de grano fino no es una elección reversible de biblioteca: condiciona el formato de tus datos, tu estrategia de persistencia, tu modelo de permisos y la forma de tu API durante toda la vida del producto. Un documento almacenado como estructura replicada no se consulta con las herramientas habituales, no se filtra en el servidor sin reconstruirlo y no se migra con una sentencia. Todo eso es asumible cuando la colaboración es el producto y desproporcionado cuando es una comodidad. Decide con esa asimetría delante.

Hay además un criterio que pesa más de lo que a un ingeniero le gusta admitir, y es qué se puede comprar hecho. Escribir OT correcto desde cero es un proyecto de meses con riesgo real de incorrección silenciosa, y escribir un CRDT de secuencia competitivo en rendimiento es todavía peor. El ecosistema maduro está hoy claramente del lado de los CRDTYjs y su familia de conectores para los editores habituales, Automerge con su modelo de documento y su historial, Loro como alternativa reciente— y del lado de OT sobreviven sobre todo ShareDB y las implementaciones internas de las grandes empresas. Elegir la familia con mejor ecosistema no es pereza: es reconocer que la parte difícil de este problema ya fue resuelta por gente que dedicó años a ella y que tus contraejemplos de TP2 no van a ser mejores que los suyos.

Cuatro preguntas en orden de descarte

Un método de decisión útil no es una lista de criterios, es una secuencia ordenada por capacidad de eliminar alternativas, de modo que las preguntas caras solo se hagan si las baratas no han decidido ya. Estas cuatro, en este orden, resuelven la inmensa mayoría de los casos reales.

  • Cuánto tiempo debe funcionar sin conexión. Si la respuesta se mide en días, el eje del algoritmo está decidido y es CRDT. Si se mide en segundos de red inestable, ambas familias sirven y la pregunta no descarta nada.
  • Quién debe poder rechazar una escritura. Si existe una sola regla de permisos, validación o negocio que deba imponerse aunque el cliente esté manipulado, necesitas servidor autoritativo. Si no existe ninguna, tienes abierto el cuadrante entre pares y con él el cifrado de extremo a extremo.
  • Qué forma tiene el dato. Texto plano, texto enriquecido con atributos, árbol de bloques, lista de registros o campos independientes. Cuanto más estructurado y más propenso a crecer el modelo, más pesa la composicionalidad que la lección anterior atribuyó a los CRDT.
  • Qué se puede comprar hecho. Qué editor usas, qué conectores existen para él, quién mantiene la librería y cuántos productos serios dependen de ella. Es la pregunta menos noble y la que más proyectos ha salvado.

Obsérvese que ninguna de las cuatro menciona rendimiento. No es un descuido: en el rango de tamaños en que trabajan casi todos los productos, ambas familias son suficientemente rápidas, y elegir por comparativas de milisegundos es optimizar el eje que no te va a doler mientras se ignoran los tres que sí. El rendimiento entra en la conversación cuando el documento es descomunal o el dispositivo es muy limitado, y entonces se convierte en una restricción dura que hay que medir con tus propios datos y no con los del anuncio de nadie.

Los casos límite que deciden en la práctica

Cuando las cuatro preguntas dejan empate, la decisión suele resolverla uno de estos tres detalles, que comparten la propiedad de parecer secundarios en el diseño y volverse centrales en el mes tres de desarrollo.

🔏

Cifrado de extremo a extremo

Si el servidor no debe poder leer el documento, no puede transformar operaciones, y OT queda descartado de raíz porque su servidor necesita entender lo que secuencia. Un CRDT funde en el cliente y el servidor puede ser un relé ciego que mueve bytes opacos.

🧬

Migración de esquema

Los documentos duran años y su forma cambia. Con servidor autoritativo se migra una vez, en el servidor. Entre pares hay réplicas antiguas que reaparecen con el esquema viejo, y toda la lógica de migración debe vivir en el cliente y ser conmutativa con la fusión.

🕰️

Historial y atribución

Si el producto promete ver quién escribió qué y volver a cualquier versión, el CRDT ya guarda esa información por construcción y el OT la obtiene de su registro lineal. Si además prometes borrar el rastro de alguien bajo demanda, revisa antes cuánto cuesta reescribir un historial fusionado.

💡
Antes de elegir familia, pregúntate si necesitas tiempo real

La mayoría de los productos que creen necesitar colaboración en tiempo real necesitan en realidad edición concurrente con granularidad gruesa: distintas personas trabajando en distintos registros, no dos cursores en la misma frase. Para eso, el bloqueo optimista con versión de la primera lección resuelve el problema en una tarde, con datos planos, sin metadatos y sin dependencias. La colaboración de grano fino es cara en complejidad, en memoria y en superficie de fallo, y solo se justifica cuando el producto consiste en que dos personas toquen literalmente lo mismo a la vez. Sustituir un campo de versión por un motor de CRDT porque queda moderno es el error de sobreingeniería más caro de este nivel.

📝
La comparativa cambia; los ejes no

Los nombres de esta lección envejecerán. Aparecerán motores más rápidos, formatos de metadatos más compactos, técnicas de compactación que hoy son artículos y mañana serán opciones de configuración, y el cuadrante entre pares ganará terreno a medida que la identidad descentralizada y el cifrado de extremo a extremo se abaraten. Lo que no envejecerá son los dos ejes, porque no son tecnológicos: cómo se funden dos cambios concurrentes y quién puede rechazar una escritura son preguntas sobre la estructura del problema, no sobre la moda de la herramienta. Si aprendes a situar cualquier producto nuevo en ese plano en menos de un minuto, habrás sacado del nivel lo único que no caduca.

La pregunta nunca fue qué algoritmo funde mejor, sino dónde vive la autoridad

Vale la pena volver sobre el recorrido entero del nivel, porque su forma es más instructiva que cualquiera de sus piezas. Empezó con un fallo trivial —dos guardados que se pisan— y de él salió, sin añadir nada artificial, todo lo demás: la actualización perdida obligó a operaciones finas, las operaciones finas destaparon que un índice solo significa algo en su marco de referencia, y de esa única grieta nacieron las dos únicas respuestas posibles, traducir la coordenada o abolirla. OT y CRDT no son escuelas rivales elegidas por temperamento, son las dos ramas de una bifurcación forzada, y por eso ninguna es mejor: cada una paga en una moneda distinta la misma deuda. Pero la lección más transferible es la del segundo eje, el que se descubre solo cuando ya has entendido el primero. Cuando comparas mecánicamente las dos familias, ninguna gana con claridad y el debate se eterniza en detalles de rendimiento. Cuando en cambio preguntas quién debe poder decir que no a una escritura, la decisión suele resolverse en una frase, porque esa pregunta no es técnica: es una pregunta sobre a quién pertenece el dato. Si el documento es de la empresa y hay que auditarlo, restringir quién toca qué e impedir que un cliente manipulado escriba basura, entonces necesitas un árbitro, y el árbitro condiciona todo lo demás. Si el documento es del usuario y debe seguir siendo suyo cuando tu servidor desaparezca, entonces no puedes poner un árbitro en el camino crítico, y esa restricción condiciona todo lo demás. La topología de tu sistema colaborativo es, literalmente, una declaración sobre la propiedad de los datos, y por eso es una decisión de producto que un equipo de ingeniería no debería tomar en solitario ni deducir del benchmark de una librería. Esta es la lectura que convierte un nivel sobre algoritmos de fusión en algo más grande: en sistemas distribuidos, las estructuras técnicas son cristalizaciones de decisiones sobre poder, y quien entiende dónde puso la autoridad entiende su sistema mucho mejor que quien solo sabe cómo funde sus operaciones.

⚔️ Sitúa tres productos reales en el plano y decide el tuyo
  1. Coloca en el plano de dos ejes tres productos colaborativos que uses, y justifica cada posición con evidencias observables: qué hacen sin conexión, qué permisos aplican, si hay historial.
  2. Toma tu propio caso y responde la primera pregunta con honestidad: cuánto tiempo debe funcionar sin conexión de verdad, no en el mejor de los deseos.
  3. Responde la segunda: enumera las reglas que un servidor debería poder aplicar para rechazar una escritura. Si la lista está vacía, tienes una arquitectura entre pares al alcance.
  4. Estima el impuesto de metadatos de tu documento típico: cuántos átomos, cuántos borrados históricos y qué tamaño tendría su representación con identidad por carácter.
  5. Diseña la funcionalidad de deshacer para tu caso y comprueba que deshace lo del usuario y no lo último que ocurrió en el documento.
  6. Antes de escribir una línea, contesta si tu producto necesita grano fino o le basta el bloqueo optimista con versión, y defiende la respuesta con un caso de uso concreto.