La forma de tus datos manda: texto, documento o registro
Editar texto largo a la vez, versionar documentos estructurados o replicar registros de base de datos son tres problemas distintos, y la respuesta correcta cambia en cada uno.
Las dos lecciones anteriores construyeron un orden de ejes y una tabla honesta, y la conclusión de ambas apuntaba al mismo sitio: los números solo empiezan a significar algo cuando ya sabes qué pregunta estabas contestando. Esa pregunta la fija la forma del dato, y hay tres formas que llevan a tres respuestas genuinamente distintas. Editar un texto largo entre varias personas a la vez es un problema de secuencia con presencia y latencia. Versionar documentos estructurados es un problema de historia, ramas y comparación entre estados. Replicar registros de base de datos es un problema de claves, índices e invariantes que los tres anteriores no tienen. Aplicar la respuesta correcta a la forma equivocada produce sistemas que funcionan en la demostración y se rompen en el segundo año, y esa es probablemente la forma más cara de equivocarse en este nivel.
- Identificar cuál de las tres formas de dato domina tu producto antes de comparar librerías.
- Entender por qué el texto colaborativo premia el estado actual y penaliza el grafo de historia.
- Ver por qué el documento estructurado con versiones invierte esa preferencia por completo.
- Reconocer el caso del registro relacional, donde el CRDT resuelve la parte fácil del problema.
Texto largo editado a la vez
Cuando varias personas escriben en el mismo párrafo con el cursor visible, el problema tiene una firma reconocible: muchísimas operaciones diminutas, latencia perceptible entre teclado y pantalla remota, y un estado final que a nadie le interesa desmontar. Nadie pide ver quién insertó el carácter número 4.812. Lo que se pide es que el texto entrelace bien, que el cursor ajeno no salte y que la sesión aguante horas sin degradarse.
Esa firma tiene consecuencias directas sobre los ejes del primer capítulo. El tamaño en disco importa mucho, porque un documento de texto vivo acumula operaciones a un ritmo que ningún otro tipo de dato alcanza. El grafo completo de historia pasa de virtud a lastre: guardarlo significa que cada pulsación deja un rastro permanente que nadie va a consultar y que sin embargo viaja en cada sincronización. Y el ecosistema sube de peso más que en ningún otro caso, porque el texto colaborativo no se resuelve solo con un CRDT: necesita enlace con un editor, presencia de cursores, deshacer por usuario y un proveedor de red.
Hay además una parte del texto colaborativo que no es un CRDT en absoluto y que conviene separar desde el principio: la presencia. Los cursores ajenos, la selección de cada persona y el indicador de quién está mirando son datos efímeros que no deben persistirse, que no tienen que converger y que caducan cuando alguien cierra la pestaña. Meterlos dentro del documento es un error frecuente y caro, porque convierte información desechable en historia permanente que viaja en cada sincronización. Las librerías maduras ofrecen un canal aparte precisamente para esto, y que lo ofrezcan es parte de lo que se está comprando al elegir un ecosistema grande.
Ese conjunto de exigencias describe con precisión el perfil de Yjs, y explica por qué está detrás de Notion, Jupyter, Tiptap y BlockNote y por qué ronda las 920 mil descargas semanales frente a las 85 mil y las 12 mil de las otras dos. Su algoritmo de secuencia, YATA, resuelve el entrelazado; su decisión de no guardar el grafo completo del historial encaja con un caso donde el pasado no se consulta; y la masa de integraciones existentes cubre justo la parte del trabajo que no es el CRDT. Conviene notar el matiz que compensa: Yjs necesita guardar un vector de versión y un conjunto de borrados por cada versión persistida, de modo que si tu producto guarda instantáneas con mucha frecuencia, ese coste reaparece por otra puerta.
En un editor colaborativo, deshacer no puede significar volver al estado anterior del documento, porque ese estado incluía las ediciones de otros que nadie quiere revertir. Tiene que significar deshacer las operaciones propias respetando las ajenas, y eso exige que la librería mantenga la autoría a nivel de operación y ofrezca un gestor de deshacer con ámbito. Es una de las funcionalidades donde la madurez del ecosistema se convierte en semanas de trabajo evitadas, y es sistemáticamente la que se descubre tarde, cuando el prototipo ya funcionaba y el equipo creía que la parte difícil estaba hecha.
Documentos estructurados con historia
La segunda forma tiene una firma casi opuesta. Un documento estructurado —una ficha, un proyecto, un formulario complejo, un modelo con secciones anidadas— recibe menos operaciones y de mayor tamaño semántico, se edita a menudo sin simultaneidad estricta y, sobre todo, su pasado es funcionalidad. Comparar dos versiones, ver qué cambió entre revisiones, restaurar un estado anterior o auditar quién modificó qué no son adornos: suelen ser el motivo por el que el producto existe.
Aquí el grafo completo del historial deja de ser un lastre y se convierte en el activo principal. Loro y Automerge lo guardan, y Automerge sobresale específicamente en versionado a nivel de documento, que es la operación central de este caso. La objeción histórica a esa elección era su coste, y esa objeción cambió de tamaño a finales de 2025: la versión 3.0 recortó el tamaño entre un 40 y un 60 por ciento con almacenamiento columnar y bajó la memoria un orden de magnitud, de 700 MB a 1,3 MB en un documento del tamaño de Moby Dick, con una carga que pasó de 17 horas a 9 segundos. Un salto así no mejora una posición en un ranking: reabre casos de uso que estaban cerrados.
flowchart TD D[que forma tiene tu dato dominante] --> T[texto largo editado a la vez] D --> E[documento estructurado con historia] D --> R[registros de base de datos] T --> T1[premia estado actual y ecosistema] T --> T2[penaliza guardar el grafo completo] E --> E1[premia grafo de historia y comparacion] E --> E2[penaliza formatos que olvidan el pasado] R --> R1[premia consultas indices e invariantes] R --> R2[el CRDT no cubre la parte dificil] style T fill:#a6e3a1,color:#11111b style E fill:#cba6f7,color:#11111b style R fill:#f38ba8,color:#11111b
La diferencia entre tener el grafo y no tenerlo se ve mejor en la forma que toma el código de producto que lo consume, porque con grafo la funcionalidad es una consulta y sin grafo es una infraestructura paralela.
// Con grafo de historia: la funcionalidad es una consulta al propio documento
function cambiosEntre(doc, versionA, versionB) {
return diferencia(estadoEn(doc, versionA), estadoEn(doc, versionB));
}
// Sin grafo de historia: hay que construir la infraestructura por fuera
// - guardar instantaneas periodicas y decidir su frecuencia
// - versionarlas y podarlas para que no crezcan sin limite
// - reconstruir la autoria, que no estaba en ninguna instantanea
// - aceptar que solo puedes comparar los puntos que decidiste guardar
El segundo bloque no es imposible y se ha escrito muchas veces. Lo que hay que ver es que es una funcionalidad completa con su propio mantenimiento, y que su resultado es estrictamente peor: solo puedes comparar los instantes que tuviste el acierto de capturar, mientras que el grafo permite comparar cualquier par de puntos porque no guardó fotos, guardó el camino.
Conviene separar dos cosas que este caso mezcla con facilidad. Una es guardar el pasado, que es lo que hace el grafo. Otra es presentarlo, que exige comparar dos estados y describir la diferencia en términos que el usuario entienda: este campo cambió, esta sección se movió, este bloque lo escribió aquella persona. Lo primero lo resuelve la librería; lo segundo lo escribes tú encima, y su coste depende de que la librería te dé acceso cómodo a los estados intermedios. Elegir una que no guarda el grafo y reconstruir el historial por fuera es posible y es, casi siempre, escribir peor una funcionalidad que ya existía.
Registros de base de datos
La tercera forma es la que más gente tiene y la que peor se lleva con la ilusión de que un CRDT resuelve el problema. Filas con clave primaria, relaciones entre tablas, consultas con filtros y ordenaciones, índices, y reglas que deben cumplirse siempre: un pedido pertenece a un cliente que existe, un saldo no baja de cero, dos reservas no ocupan la misma plaza. Un CRDT garantiza que todas las réplicas convergen al mismo estado; no garantiza que ese estado cumpla tus reglas.
// Dos replicas convergen al mismo estado y ese estado viola la invariante
const plazasLibres = 1;
// Replica A, sin conexion
reservas.set("r1", { plaza: 7, usuario: "ana" });
// Replica B, sin conexion y a la vez
reservas.set("r2", { plaza: 7, usuario: "luis" });
// Tras fusionar: ambas reservas existen, ambas replicas coinciden,
// y la plaza 7 esta asignada dos veces. No hubo conflicto detectable:
// las claves eran distintas y nadie escribio sobre lo que otro escribio.
El ejemplo no es un fallo de ninguna librería, es el límite del contrato. La convergencia es una propiedad sobre la igualdad entre réplicas, no sobre la validez del contenido, y las invariantes que ligan registros distintos caen fuera de lo que cualquiera de las tres puede prometer. En esta forma de dato, la elección de librería resuelve la parte fácil —que todos vean lo mismo— y deja intacta la difícil, que es qué hacer cuando lo que todos ven es inválido. La cuarta lección de este nivel está dedicada entera a esa situación.
Conviene además notar por qué esta forma engaña tanto en las demostraciones. Un prototipo con veinte filas y dos usuarios converge perfectamente y parece resolver el problema, porque con ese volumen las invariantes casi nunca se violan y, cuando lo hacen, nadie mira. El fallo aparece con densidad real de escritura concurrente y con usuarios que trabajan de verdad sin conexión, es decir, en el segundo año y con datos de clientes. Una demostración de esta forma de dato no prueba nada sobre su viabilidad, y confundir ambas cosas es la trampa más común de todo el nivel.
Cuando la forma de registro convive con una necesidad real de trabajo sin conexión, los ejes se reordenan otra vez. Pasa a pesar mucho la capacidad de consultar sin cargar todo en memoria, algo que ningún CRDT ofrece de serie porque su unidad es el documento y no la fila. Pesa el tamaño total del conjunto replicado, que crece con el número de registros y no con la actividad. Y pesa una pregunta que las otras dos formas no se hacen: si de verdad hace falta replicar todo o basta con un subconjunto por usuario, porque replicar una base entera en un cliente rara vez es lo que el producto necesitaba.
Texto: el presente es el producto
Operaciones diminutas y constantes, pasado que nadie consulta, latencia visible y una enorme superficie de integración con editores, cursores y deshacer.
Documento: el pasado es el producto
Menos operaciones y más grandes, comparación entre versiones como funcionalidad central, y un grafo de historia que deja de ser coste para ser el activo.
Registro: la convergencia no basta
Claves, relaciones, índices e invariantes entre filas. El CRDT garantiza que todos vean lo mismo, no que lo que vean sea válido.
Mezcla: decide por dominancia
Casi todo producto real tiene las tres. Elegir por la que manda y aislar las otras es más barato que buscar una librería que sirva igual para todas.
Cuando tu aplicación tiene las tres a la vez
Antes de aceptar esa tabla conviene advertir de su límite: describe tendencias, no leyes. Un producto de texto colaborativo con requisitos de auditoría legal necesita el grafo aunque su forma dominante sea el texto, y un catálogo de registros de solo lectura por usuario puede resolverse con la maquinaria más simple de las tres. La forma del dato ordena los ejes; no los sustituye por una respuesta automática.
Casi todos los productos reales mezclan las tres formas: un editor de texto dentro de un documento estructurado que vive dentro de un espacio de trabajo con listas, permisos y miembros. La tentación es buscar una librería que sea aceptable en las tres, y suele ser una decisión peor que elegir por la forma dominante y tratar las otras dos con la herramienta que les corresponda.
El resumen de cómo cada forma reordena los seis ejes de la primera lección cabe en una tabla, y esa reordenación es la respuesta concreta a por qué no existe una ganadora general.
| Forma dominante | Eje que sube | Eje que baja | Grafo de historia |
|---|---|---|---|
| Texto largo colaborativo | Ecosistema e integraciones | Versionado por documento | Lastre que viaja en cada sincronización |
| Documento estructurado | Grafo y comparación de estados | Latencia entre colaboradores | Activo principal y motivo del producto |
| Registros de base de datos | Consulta parcial e invariantes | Todos los demás | Depende, y rara vez es lo decisivo |
La razón es que las tres formas no tienen la misma frecuencia de escritura ni el mismo volumen. El texto genera órdenes de magnitud más operaciones que la estructura que lo rodea, y los registros del espacio de trabajo cambian rarísima vez comparados con ambos. Optimizar el sistema entero para el caso que representa el uno por ciento del tráfico de escritura es un error de asignación, y aplicar la maquinaria del texto colaborativo a una lista de miembros que cambia una vez al mes es maquinaria que pagas todos los días sin usarla.
La frontera entre formas conviene trazarla por unidad de sincronización y no por módulo de código, porque es ahí donde se paga. Cada documento de texto puede ser una unidad que se sincroniza sola, mientras la estructura que lo contiene es otra que se sincroniza mucho menos y los registros del espacio de trabajo son una tercera que quizá ni siquiera sea local-first. Separarlas así hace que el tráfico de la forma más activa no arrastre a las demás, y tiene un efecto secundario valioso: permite adoptar herramientas distintas en cada una sin que la decisión de una condicione a la siguiente.
Haz el inventario de tu modelo y clasifica cada parte en una de las tres formas, anotando junto a cada una su frecuencia de escritura y si su pasado se consulta. El resultado casi siempre muestra una forma que domina en tráfico y otra que domina en importancia, y esas dos no suelen coincidir. Esa tabla, hecha antes de instalar nada, evita la discusión estéril de qué librería es mejor y la sustituye por una pregunta contestable: qué herramienta para la forma que manda, y qué frontera la separa de las demás.
Detrás de las tres formas hay un mismo movimiento repetido tres veces, y verlo convierte esta lección en un criterio general en lugar de en un catálogo de casos. Cada forma de dato tiene un sitio donde su complejidad es irreducible, y la librería que le conviene es la que absorbe exactamente esa complejidad y ninguna otra. En el texto largo la complejidad irreducible está en el entrelazado y la presencia: dos personas escribiendo en el mismo punto a la vez es un problema con solución algorítmica no evidente, y todo lo demás —el pasado, la auditoría, la comparación— es prescindible. Por eso una librería que resuelve el entrelazado con YATA, se rodea de integraciones y renuncia deliberadamente a guardar el grafo completo del historial no está incompleta: está afilada. En el documento estructurado la complejidad irreducible está en la relación entre estados, y ahí el grafo no es sobrecoste sino el objeto mismo; una librería que sobresale en versionado a nivel de documento está resolviendo la parte que tú no quieres escribir, y por eso el salto de la 3.0 importó tanto: no cambió lo que Automerge sabía hacer, cambió el precio de que lo hiciera, y un precio que baja un orden de magnitud convierte en viable un catálogo entero de productos que antes no lo eran. Y en los registros de base de datos la complejidad irreducible está en las invariantes entre entidades distintas, que es precisamente lo único que ninguna de las tres librerías puede tocar, porque no es un problema de convergencia sino de validez, y la convergencia y la validez son propiedades independientes. De ahí se deduce la regla que ordena toda la decisión y que sobrevive a cualquier cambio de versión: mira dónde está tu complejidad irreducible y elige la herramienta que la absorbe, no la que gana más columnas. Si tu complejidad está en un sitio que ninguna librería absorbe, la elección de librería es una decisión menor disfrazada de decisión mayor, y estás a punto de dedicar semanas a comparar opciones cuando la conversación que necesitabas era otra. Esa conversación es la de la lección siguiente.
- Divide tu modelo de datos en las tres formas y anota junto a cada parte su frecuencia de escritura estimada y su volumen.
- Marca cuáles de esas partes necesitan que su pasado sea consultable por el usuario, y distingue guardarlo de presentarlo.
- Identifica la forma dominante en tráfico y la dominante en importancia de producto, y comprueba si coinciden o no.
- Para la forma dominante, reordena los seis ejes de la primera lección según lo que esta lección explica y anota qué cambia.
- Busca en tu modelo al menos una invariante que ligue dos entidades distintas y escribe qué ocurre si dos réplicas la violan concurrentemente.
- Decide qué frontera separará la forma dominante de las demás y escribe qué atraviesa esa frontera y en qué formato.