wandres.dev
MIGRAR EL ESQUEMA · datos que no controlas

El plan realista: ventanas, telemetría y contrato público

Forzar actualizaciones tiene límites duros, la ventana de compatibilidad es una decisión de producto con coste medible y la telemetría es lo único que convierte el esquema en un contrato gobernable.

⏱ 21 min

Las cuatro lecciones anteriores han descrito el problema, la propiedad que hay que garantizar, las estrategias disponibles y el caso convergente donde casi ninguna funciona del todo. Falta la parte que convierte todo eso en algo ejecutable por un equipo con plazos: qué se puede forzar de verdad y qué no, cuánto tiempo hay que sostener cada forma antigua, cómo se sabe qué versiones siguen vivas ahí fuera y con qué disciplina se decide romper cuando romper resulta inevitable. Esta última lección es deliberadamente menos algorítmica y más organizativa, porque a estas alturas ya está claro que el cuello de botella no es técnico. La conclusión a la que llega es la que da sentido al nivel entero y conviene adelantarla: en local-first el esquema es un contrato público, con obligaciones que duran años, y la única política sostenible consiste en romperlo con muchísimo cuidado o, mejor todavía, en diseñar de manera que casi nunca haga falta romperlo.

🎯 Al terminar esta lección sabrás
  • Distinguir lo que una actualización forzada puede conseguir realmente de lo que solo aparenta conseguir.
  • Definir una ventana de compatibilidad explícita y calcular su coste en superficie de código y de pruebas.
  • Diseñar telemetría de versiones que funcione desde el cliente más antiguo y que mida la cola y no la mediana.
  • Construir un corpus de documentos reales de cada versión publicada y usarlo como red de seguridad permanente.
  • Formular el esquema como contrato con política de depreciación, plazos y vía de salida para quien se quede fuera.

Lo que de verdad se puede forzar

La primera corrección que hay que hacer es sobre el verbo. Forzar una actualización sugiere que existe un mecanismo para poner código nuevo en un dispositivo ajeno, y ese mecanismo no existe en ninguna plataforma con la fiabilidad que la palabra insinúa. Lo que sí existe es la capacidad de negarse a hablar con las versiones antiguas, que es una cosa distinta y con efectos secundarios propios.

En la web se puede comprobar la versión al arrancar y pedir al trabajador de servicio que tome el control de inmediato, y aun así no se puede recargar por sorpresa una pestaña que alguien lleva abierta desde hace días con trabajo sin guardar. En plataformas móviles se puede exigir una versión mínima, pero la comprobación solo ocurre cuando la aplicación se abre y hay conexión, de modo que el dispositivo que más te preocupa, el que lleva meses apagado, es justamente el que no se entera. En despliegues gestionados por una organización, la versión puede estar fijada por una política que no controlas y que un administrador cambiará cuando le venga bien.

{
  "puerta_de_version": {
    "minimaSoportada": 4,
    "minimaParaSincronizar": 3,
    "por_debajo_de_la_minima": "modo solo lectura con aviso y exportacion disponible",
    "nunca": "bloquear la lectura de los datos que la persona ya tiene"
  }
}

La última línea es la que separa una puerta de versión bien diseñada de una que produce pérdida de datos. Bloquear la sincronización de un cliente antiguo no impide que siga escribiendo en local, porque escribir sin conexión es la promesa central del producto; lo único que consigue es que acumule trabajo que nunca podrá enviar. Si vas a cerrar la puerta, la degradación correcta es a solo lectura, con un mensaje explícito, y con una exportación completa disponible en ese mismo momento y en un formato que no dependa de tu código.

⚠️
La actualización obligatoria no repara los datos que la versión anterior ya escribió

Hay una expectativa optimista que conviene desmontar porque aparece siempre en la conversación posterior a un incidente. Aunque consiguieras actualizar hoy el cien por cien de los dispositivos, cosa que no vas a conseguir, eso no deshace nada de lo que la versión defectuosa escribió ni retira lo que ya replicó a otros. Los documentos corruptos siguen corruptos, las operaciones mal formadas siguen en los historiales y las escrituras que borraron campos por viaje de ida y vuelta ya se propagaron. La actualización detiene la producción de daño nuevo y no repara el daño existente; para eso hace falta una migración correctiva, que es una migración más, con todos los problemas de este nivel, y además ejecutada bajo presión. Por eso el orden correcto ante un incidente de esquema es siempre el mismo: primero cortar la fuente, después medir el alcance con datos y solo entonces diseñar la corrección con calma.

La ventana de compatibilidad como decisión de producto

Con los límites de la actualización claros, la pregunta operativa se vuelve concreta: durante cuánto tiempo se sostiene cada forma antigua. Esa decisión no es técnica y no debería tomarla en silencio quien escribe la migración. Es un compromiso con coste medible, y conviene escribirlo con la misma formalidad que una política de soporte.

Las dos formas habituales de expresarla son por número de versiones o por tiempo transcurrido, y la segunda envejece mejor porque no depende de tu cadencia de publicación. Una ventana de dieciocho meses significa que cualquier forma publicada hoy se seguirá leyendo hasta dentro de dieciocho meses como mínimo, y que el código que la lee no se retira antes aunque parezca muerto. El coste de esa ventana no es abstracto y se puede calcular.

// El coste de la ventana crece con el numero de versiones vivas
const vivas = 6;
const rutasDeLectura = vivas;              // una por forma que hay que saber leer
const paresDeInteroperabilidad = vivas ** 2; // la matriz de la leccion 2
const casosDeCorpus = vivas * 4;           // cuatro documentos reales por version

// Y ese coste se paga en cada cambio, no una sola vez
const coste = (cambiosPorTrimestre) => cambiosPorTrimestre * paresDeInteroperabilidad;
coste(5); // 180 comprobaciones por trimestre con seis versiones vivas

Ese número es el argumento más útil que existe para defender la disciplina de la lección tercera ante quien decide prioridades. Cada ruptura evitada no ahorra una migración: ahorra una fila y una columna enteras de la matriz durante toda la ventana. Y a la inversa, alargar la ventana sin reducir el número de rupturas hace crecer el coste de forma cuadrática, hasta el punto en que el equipo deja de comprobar la matriz completa, que es exactamente el momento en que empiezan los incidentes silenciosos.

🚪

Puerta de versión

Define una mínima para sincronizar y otra para funcionar. Degrada a solo lectura con exportación, nunca a inutilizable.

🗓️

Ventana declarada

Expresada en meses y no en número de versiones, escrita, con fecha de retirada para cada forma antigua.

📊

Telemetría de la cola

Lo que importa es el percentil noventa y nueve de antigüedad, ponderado por documentos activos y no por personas.

🧪

Corpus dorado

Documentos reales de cada versión publicada, guardados en el repositorio y comprobados en cada entrega.

Medir la cola, no la mediana

Ninguna de las decisiones anteriores se puede tomar sin datos, y los datos de esta área tienen dos particularidades que conviene resolver desde el diseño. La primera es que la magnitud interesante no es la típica sino la extrema: la mediana de versiones en uso es casi siempre la última y no informa de nada. Lo que decide si puedes retirar una forma es el percentil alto, y sobre todo el peso de esos rezagados medido en documentos activos, porque un solo dispositivo antiguo que participa en un documento compartido por veinte personas hace más daño que cien dispositivos antiguos aislados.

La segunda particularidad es más sutil y es la que casi siempre se descubre tarde: la telemetría tiene que funcionar precisamente en los clientes más antiguos, que son los que menos código nuevo pueden ejecutar. Eso obliga a congelar el formato del propio informe desde la primera versión y a no cambiarlo nunca, porque un informe con forma nueva no lo va a emitir un cliente viejo, y el cliente viejo es el único del que necesitas noticias.

{
  "informe_congelado_desde_la_version_1": {
    "v": 1,
    "app": "5.2.0",
    "esquemaMax": 6,
    "esquemaMin": 4,
    "docsActivos": 118,
    "ultimaSync": "2026-07-30"
  },
  "regla": "este formato no cambia nunca, solo se le agregan campos opcionales"
}

Con esa serie recogida se pueden responder por fin las preguntas que hasta ahora se contestaban por intuición: qué formas siguen vivas, cuánto pesan, si la cola decae o se ha estancado, y cuántos documentos compartidos tienen al menos un participante por debajo de la versión mínima. Esa última cifra es la más accionable de todas, porque identifica exactamente los documentos donde el viaje de ida y vuelta puede estar destruyendo datos ahora mismo.

💡
El corpus dorado es la práctica con mejor retorno de todo el nivel

Si solo se pudiera adoptar una cosa de estas cinco lecciones, sería esta. Consiste en guardar en el repositorio, junto al código, documentos reales de cada versión que hayas publicado, anonimizados si hace falta, incluyendo los raros: el enorme, el que tiene caracteres extraños, el que quedó a medio migrar y el que llegó de un dispositivo con la forma antigua. En cada entrega se comprueba que la versión actual los lee todos, que el viaje de ida y vuelta no pierde nada en ninguna de las dos direcciones y que la fusión de dos de ellos con versiones distintas produce algo sano. El corpus tarda una tarde en montarse, crece solo con cada publicación y es la única red de seguridad que sigue funcionando cuando la persona que escribió la migración de la versión tres ya no trabaja en el proyecto. Añadirlo cuando ya has publicado seis versiones es posible pero mucho peor, porque para entonces los documentos de las versiones antiguas ya no están a mano.

flowchart LR
T[telemetria congelada desde la version 1] --> D[distribucion real de versiones vivas]
D --> V[ventana de compatibilidad declarada]
V --> P[politica de depreciacion con fechas]
P --> C[corpus dorado por version]
C --> M[matriz de pares comprobada en cada entrega]
M --> R[romper solo cuando el coste esta medido]
style T fill:#89b4fa,color:#11111b
style R fill:#a6e3a1,color:#11111b

El esquema como contrato público

Todo lo anterior se ordena bajo una sola idea de gobierno, y es la que cierra el nivel. Un esquema local-first tiene exactamente las propiedades de una interfaz pública: lo consumen implementaciones que no controlas, ninguna de ellas se puede actualizar por decreto, la retirada de una parte afecta a terceros que no participaron en la decisión, y los cambios incompatibles producen daño en sistemas ajenos. La conclusión razonable es tratarlo con el mismo instrumental que se usa desde hace décadas para las interfaces públicas, sin inventar nada nuevo.

Eso significa cuatro cosas concretas. Versionado explícito y visible, con el número dentro del documento y no fuera. Una política de depreciación escrita, con plazos publicados, donde deprecar signifique dejar de escribir y seguir leyendo, nunca borrar de golpe. Una revisión obligatoria para cualquier cambio que toque una forma ya publicada, con la matriz de pares como criterio de aceptación. Y una vía de salida permanente, es decir, una exportación completa a un formato documentado e independiente de tu código, disponible en todas las versiones y también, sobre todo, en las que están a punto de quedarse fuera de la ventana.

El esquema es un contrato público y romperlo es una decisión que se paga en datos ajenos

Llegados al final del nivel merece la pena reunir lo que se ha ido demostrando, porque forma una sola tesis y no cinco observaciones sueltas. La primera lección estableció que al publicar dejas de ser el dueño de la forma de tus datos: el esquema se convierte a la vez en formato de fichero de larga duración y en protocolo de red, y ambas categorías tienen dueños múltiples por definición. La segunda estableció que la compatibilidad no es un adjetivo de una versión sino una relación entre pares, y que el esquema real de tu sistema es la intersección de lo que todas las versiones vivas saben interpretar sin destruir nada, un objeto que no está escrito en ninguna parte y que avanza por defunción. La tercera mostró que las herramientas disponibles solo funcionan si se instalaron antes de necesitarlas. Y la cuarta mostró que en la parte convergente del modelo hay cambios que sencillamente no tienen migración correcta. Todo eso apunta al mismo sitio: en local-first no existe la ruptura barata, y el diseño que lo ignora no evita el coste, solo lo traslada a personas que no participaron en la decisión y que se enterarán meses después, sin registro, sin explicación y sin forma de recuperar lo perdido. De ahí sale el criterio con el que conviene salir de este nivel, y es más sobrio de lo que a nadie le gustaría. La pregunta correcta ante un cambio de esquema no es cómo migro, sino cuánto vale este cambio comparado con el número de años que voy a tener que sostener las dos formas. Formulada así, una fracción sorprendentemente grande de los cambios que parecían necesarios deja de parecerlo: el renombrado que mejoraba la claridad del código no vale dieciocho meses de doble escritura, la reorganización que hacía el modelo más elegante no vale una fila entera de la matriz de pares, y el cambio de unidad que ahorraba una división no vale el riesgo de un fallo silencioso que ningún validador detecta. Lo que sí vale la pena se reconoce porque sobrevive a esa comparación sin esfuerzo. Y el equipo que la hace de forma sistemática acaba con un modelo de datos que a los seis meses parece innecesariamente rígido y que a los seis años sigue leyendo sin incidentes documentos escritos por una versión que ya nadie recuerda haber publicado. Ese es el objetivo real: no migrar mejor, sino necesitar migrar mucho menos.

⚔️ Convierte tu esquema en un contrato con obligaciones escritas
  1. Escribe la política de versión mínima de tu aplicación y define exactamente qué ve una persona cuyo cliente queda por debajo.
  2. Comprueba que existe una exportación completa e independiente de tu código y que está disponible también en las versiones antiguas.
  3. Declara por escrito tu ventana de compatibilidad en meses y calcula el número de pares que implica sostenerla.
  4. Diseña el informe de telemetría de versiones, congela su formato y comprueba que el cliente publicado más antiguo puede emitirlo.
  5. Monta el corpus dorado con documentos reales de cada versión publicada y añádelo a la comprobación de cada entrega.
  6. Toma el próximo cambio de esquema que tengas previsto y justifícalo por escrito frente al coste de sostener las dos formas durante toda la ventana.