wandres.dev
EL HISTORIAL INFINITO · compactar sin romper

El dispositivo que vuelve

Cuando una réplica reaparece por debajo del corte solo hay tres salidas, rechazarla, reconstruirla desde cero o aceptar que pierde sus cambios, y ninguna de las tres es buena.

⏱ 19 min

Esta es la única lección del nivel sin una línea de código, y no por falta de material: el mecanismo que hay que implementar cabe en veinte líneas y ya está insinuado en la lección anterior. Lo que no cabe en veinte líneas es la decisión que ese mecanismo ejecuta. Has declarado un corte, has producido una base, has descartado lo que quedaba debajo, y catorce meses después alguien enciende un portátil que llevaba en un cajón desde antes de esa fecha. Ese portátil tiene estado legítimo, operaciones que nadie ha visto y una versión del documento que era correcta el día que se apagó. El sistema tiene que hacer algo con él, ese algo es visible para una persona concreta y las opciones disponibles son exactamente tres. Ninguna es buena. Conviene mirar las tres de frente, entender qué se pierde en cada una y aceptar que el trabajo de ingeniería aquí consiste en elegir el daño y contarlo con honestidad, no en evitarlo.

🎯 Al terminar esta lección sabrás
  • Caracterizar con precisión qué trae una réplica rezagada y por qué su estado no es reconciliable.
  • Analizar las tres salidas posibles junto al tipo de pérdida que produce cada una.
  • Entender por qué no existe una cuarta salida y qué resultado del nivel anterior lo impide.
  • Saber redactar la política de reaparición como texto de producto antes de implementarla.
  • Reconocer los otros eventos que producen el mismo estado sin pasado común y unificarlos bajo la misma política.

Qué trae exactamente el que vuelve

Antes de nada conviene calibrar la frecuencia real del escenario, porque quien lo lee por primera vez tiende a colocarlo entre las curiosidades. En un producto con varios dispositivos por usuario y un corte de unos meses, la reaparición tardía no es rara: es semanal. Cambiar de teléfono, reinstalar el sistema, recuperar un portátil de una reparación larga, volver de una baja o abrir en un ordenador que solo se usa en verano producen todos el mismo perfil. Lo que es raro es que alguien haya diseñado qué ocurre entonces.

Conviene ser preciso sobre la situación, porque la intuición la simplifica en la dirección equivocada. El dispositivo rezagado no trae datos antiguos que haya que actualizar; eso sería un problema resuelto desde el primer nivel de sincronización. Trae dos cosas a la vez y es la combinación lo que crea el callejón. Por un lado, una copia del pasado anterior al corte, con lápidas y metadatos que el resto del sistema ya ha descartado. Por otro, operaciones propias emitidas después de haberse desconectado y que nadie más ha visto jamás, ancladas causalmente a ese pasado descartado.

Conviene retener que las dos partes tienen naturalezas distintas. La primera, el pasado obsoleto, no es un problema en absoluto: se descarta y se sustituye por la base actual sin que nadie pierda nada, porque el resto del sistema ya tiene una versión mejor de esa misma información. Toda la dificultad está concentrada en la segunda, y su volumen suele ser pequeño en bytes y enorme en importancia, porque es literalmente lo único que ese usuario aportó y que nadie más tiene.

Esa segunda parte es la que no tiene arreglo. Una inserción hecha en el portátil dice colócame a la derecha del elemento tal, y ese elemento es una lápida que la base ya no contiene porque el corte la cubría. La operación es sintácticamente válida, semánticamente coherente en su origen y absolutamente incolocable en el estado actual, no porque el algoritmo sea limitado sino porque el ancla que definía su posición ha dejado de existir. La información que hacía falta para integrarla es exactamente la que la compactación tiró.

Hay un agravante que conviene anticipar porque cambia la percepción del problema. El rezagado no siempre sabe que lo es, y el sistema tampoco lo sabe hasta que compara. Durante todo el tiempo que estuvo desconectado, ese dispositivo funcionó perfectamente: mostró el documento, aceptó ediciones, guardó en disco y no dio ningún error, porque local-first promete precisamente eso. La primera señal de que algo va mal llega en el instante de reconectar, que es también el instante en que el usuario espera ver su trabajo integrado.

Conviene además distinguir dos poblaciones dentro de las operaciones que trae, porque no todas están en el mismo aprieto y tratarlas por igual destruye más de lo necesario. Algunas se anclan a elementos que siguen vivos en la base actual, y esas se integran sin ningún problema por muy antiguas que sean: la compactación no las tocó porque su ancla sobrevivió. Otras se anclan a lo que el corte se llevó, y solo esas son irrecuperables. En un caso real la proporción entre ambas suele estar muy sesgada hacia las primeras, de modo que la salida más destructiva de las tres, descartar el dispositivo entero, tira también todo lo que se podía haber salvado sin esfuerzo. Cualquier implementación decente separa las dos poblaciones antes de decidir nada.

Queda un caso intermedio incómodo que conviene nombrar aunque no tenga solución limpia: la operación cuya ancla desapareció pero cuyos vecinos siguen vivos. Un carácter insertado a la derecha de otro que hoy es lápida podada, pero cuyo párrafo entero sigue en el documento, tiene un lugar aproximado bastante evidente para un humano y ninguno definido para el algoritmo. Colocarlo donde parece razonable es plausible en la mayoría de los casos y produce, en el resto, un texto que nadie escribió. Ese resto es pequeño y no es cero, y como la operación no lleva ninguna marca que distinga un caso del otro, quien elige colocar por aproximación está eligiendo también no poder saber nunca cuándo se equivocó.

📝
Detectar la situación es fácil y hay que hacerlo antes de nada

La comparación que revela el caso es barata y conviene ejecutarla en el primer intercambio, antes de mover un solo dato. La base publicada lleva su corte, el rezagado lleva su vector de versiones, y basta comprobar si alguna de sus operaciones no emitidas cae por debajo de ese corte. Si ninguna cae, no hay problema: el dispositivo simplemente va atrasado y se pone al día como cualquiera. Si alguna cae, estás en el escenario de esta lección y la decisión ya no la puede tomar el protocolo. Lo importante es que la detección ocurra antes de intentar la fusión y no durante, porque una fusión a medias con anclas faltantes deja el documento en un estado que nadie diseñó y que resulta muy difícil de explicar después. Lo que sigue vale sobre todo como advertencia de implementación: el peor comportamiento posible no es ninguna de las tres salidas, es no haber comprobado y descubrirlo a mitad de camino.

Las tres salidas

Las opciones no son un espectro con puntos intermedios, aunque a primera vista lo parezcan. Son tres tratamientos cualitativamente distintos que se distinguen por quién absorbe la pérdida: el sistema, el dispositivo o el usuario.

Conviene además fijar de antemano el criterio con el que se comparan, porque no es el habitual. No se trata de cuál conserva más bytes, ya que las tres conservan aproximadamente lo mismo en términos de información recuperable. Se trata de dónde queda esa información y de cuánto esfuerzo tiene que hacer una persona para volver a disponer de ella, que es la única magnitud que el usuario percibe. Medido así, la ordenación entre las tres cambia por completo según el producto, y por eso ninguna es universalmente preferible.

🚫

Rechazar

El sistema se niega a fusionar y deja al dispositivo fuera. El estado local queda intacto pero aislado, y alguien tiene que decidir qué hacer con él a mano.

♻️

Reconstruir

El dispositivo descarta su copia y descarga la base actual. Vuelve a estar operativo de inmediato y sus operaciones no emitidas desaparecen sin dejar rastro.

📤

Rescatar y degradar

Se extrae el contenido de las operaciones huérfanas y se reintroduce como material nuevo, perdiendo su posición, su contexto y su relación con el resto.

🕰️

No podar nunca

La cuarta opción es evitar la situación entera. Es legítima, la eligen muchos productos y su precio es el crecimiento indefinido del primer capítulo.

Antes de entrar en cada una conviene fijar un punto que la discusión suele pasar por alto. La pérdida no la crea la elección: la creó la poda, y en el momento de la poda ya era inevitable. Lo único que se decide aquí es su forma y su visibilidad, es decir, si el usuario se entera, cuándo se entera y qué le queda entre las manos después. Formulado así, la comparación entre las tres deja de ser un problema de minimizar daño, que no tiene solución, y pasa a ser un problema de distribuirlo de la manera que resulte más comprensible, que sí la tiene.

Rechazar es la opción honesta y la más incómoda de sostener. Preserva íntegramente el trabajo del rezagado, no destruye nada y traslada el problema a una persona: el usuario ve dos documentos, uno actual y otro suyo, y tiene que reconciliar a mano lo que el algoritmo no pudo. Funciona bien cuando la unidad de trabajo es grande y separable, como un archivo o un proyecto, y funciona muy mal cuando es un párrafo dentro de un documento vivo, porque exige una comparación manual que nadie va a hacer.

Su mayor virtud es también su mayor problema de producto: no oculta nada. El usuario ve exactamente lo que ocurrió, entiende que hay dos versiones y sabe que su trabajo sigue estando. A cambio, el sistema ha dejado de cumplir su promesa más visible, que era que las copias convergen solas, y lo ha hecho de un modo imposible de ignorar. Muchos equipos descartan esta opción no por su coste técnico, que es el menor de los tres, sino porque no soportan enseñarla; conviene saber que esa es la razón real y decidirlo conscientemente en lugar de justificarlo con argumentos de ingeniería que no se sostienen.

Reconstruir es la opción que casi todo el mundo implementa primero, porque es trivial y porque deja el sistema en un estado limpio. Su coste es el que peor se percibe: el usuario recupera el acceso, todo funciona, y sus dos semanas de trabajo simplemente no están. Si además la aplicación no le avisa, la conclusión razonable desde su punto de vista es que el producto perdió sus datos, que es exactamente lo que ocurrió. Una reconstrucción silenciosa es la peor combinación posible de las tres.

Hay una versión mitigada de reconstruir que merece considerarse siempre y que cuesta muy poco: archivar antes de descartar. Antes de sustituir la copia local por la base actual, se serializa el estado antiguo entero a un fichero y se deja donde el usuario pueda encontrarlo. No resuelve nada en términos de fusión y no requiere entender el contenido, y convierte una pérdida definitiva en una pérdida de comodidad, que es una categoría completamente distinta. La diferencia entre un producto que hace esto y uno que no lo hace es de unas pocas líneas y de un incidente de soporte con final feliz frente a otro sin él.

Rescatar y degradar es el punto medio y es el más caro de construir. Consiste en recorrer las operaciones huérfanas, extraer su contenido con independencia de sus anclas y reinyectarlo como material nuevo: el texto perdido aparece como un bloque al final, como un comentario, como una versión adjunta o como un archivo aparte. Nada se pierde en el sentido literal y todo se degrada, porque el contenido llega sin su lugar. Para un documento de texto suele ser aceptable; para una estructura donde la posición es el significado, como una lista ordenada de tareas o un lienzo, el resultado puede ser peor que no rescatar nada.

Hay un criterio bastante fiable para saber cuál de las tres encaja en tu producto, y consiste en preguntarse dónde vive el significado. Si el contenido de cada elemento se sostiene por sí mismo y su posición es secundaria, como en un cuaderno de notas o una bandeja de mensajes, el rescate funciona bien y es claramente la mejor opción. Si el significado está en la relación entre elementos, como en un texto redactado, un diagrama o una hoja de cálculo con referencias, el rescate produce fragmentos ininteligibles y suele ser preferible rechazar y dejar que una persona decida. Y si la unidad de trabajo es lo bastante grande como para tener sentido aislada, un archivo o un proyecto, rechazar es directamente lo natural porque el sistema ya sabe presentar dos versiones de la misma cosa.

Una precisión sobre la cuarta tarjeta, que es la que más equipos acaban eligiendo sin decirlo. No podar nunca es una decisión legítima, tiene consecuencias acotadas y muchos productos pueden permitírsela durante toda su vida útil sin problemas. Lo que la vuelve peligrosa es adoptarla por omisión, es decir, no haber decidido nada y descubrir tres años después que la aplicación tarda catorce segundos en abrir para tus mejores usuarios. La diferencia entre elegirla y caer en ella es simplemente si alguien escribió por qué, con qué cifras y bajo qué condiciones habría que revisarlo.

La tabla anterior omite deliberadamente una variante que se propone siempre y que conviene descartar por escrito: dejar que el usuario elija en el momento. Suena respetuoso y en la práctica traslada a alguien sin contexto una decisión técnica que no puede evaluar, formulada en un vocabulario que no maneja, en un instante en que solo quiere volver a trabajar. Preguntar tiene sentido cuando las opciones son comprensibles y sus consecuencias visibles; aquí no lo son ninguna de las dos cosas. Lo que sí tiene sentido es informar de lo ocurrido y ofrecer una acción concreta después, cuando la persona ya ha recuperado el acceso y puede mirar con calma lo que se rescató.

flowchart TB
R[replica reaparece tras el corte] --> D[comparar su vector con el corte publicado]
D -->|nada por debajo| N[se pone al dia como cualquiera]
D -->|hay operaciones huerfanas| T[tres salidas posibles]
T --> A[rechazar y aislar]
T --> B[reconstruir y perder]
T --> C[rescatar y degradar]
A --> U[el usuario reconcilia a mano]
B --> U2[el trabajo desaparece]
C --> U3[el contenido vuelve sin su lugar]
style T fill:#f9e2af,color:#11111b
style U2 fill:#f38ba8,color:#11111b

Por qué no hay una cuarta

La pregunta que siempre aparece en este punto es si no bastaría con conservar un poco más de historia, lo justo para poder colocar operaciones antiguas. La respuesta es que sí bastaría, y que eso es precisamente no podar. Cualquier cantidad de historia que se conserve define un corte, y para cualquier corte existe un dispositivo que estuvo apagado más tiempo. Mover el corte hacia atrás no elimina el caso, solo reduce su frecuencia y aumenta el estado.

Esa observación tiene una consecuencia que conviene aceptar pronto porque ahorra discusiones circulares. No existe un valor del corte que haga desaparecer el problema, solo valores que lo hacen más raro, y la rareza tiene un efecto secundario desagradable: cuanto menos ocurre, peor preparado está el sistema para cuando ocurre. Un caso que se da una vez al mes se prueba, se instrumenta y se cuida; uno que se da dos veces al año se descubre en producción, con el código escrito hace dieciocho meses por alguien que ya no está y sin ninguna prueba que lo cubra. Elegir un corte muy conservador no es, por tanto, la opción prudente que parece: es la que garantiza que el camino de recuperación nunca se ejerza y esté roto el día que haga falta.

La segunda propuesta habitual es reconstruir la parte de historia que falta a partir de la base. No se puede, y la razón es informativa y no algorítmica: la base es el resultado de aplicar la historia, y esa aplicación no es invertible. Muchas historias distintas producen la misma base, de modo que a partir de la base no hay forma de saber qué identificadores existieron, cuáles fueron retirados ni en qué orden relativo. Las anclas que el rezagado necesita no están comprimidas en la base: fueron descartadas, que es una cosa distinta.

La confusión entre comprimido y descartado está en el fondo de casi todas las expectativas equivocadas sobre este problema, y conviene desmontarla explícitamente porque reaparece en cada conversación. Comprimir es una transformación reversible: los bytes cambian de forma y la información sigue entera, de modo que siempre hay un procedimiento que la devuelve. Descartar no lo es, y ninguna cantidad de ingenio posterior lo compensa, porque no queda nada de donde sacar lo que falta. Toda la lección tercera consistía en técnicas del primer tipo salvo una, y esa una es la que produce este capítulo entero.

La tercera propuesta es pedirle al rezagado que traduzca sus operaciones a la nueva base antes de enviarlas. Esto sí es posible en casos concretos y es esencialmente lo que hace la salida de rescate, pero conviene ver por qué no generaliza. Traducir una operación exige saber a qué posición del estado actual corresponde su ancla desaparecida, y esa correspondencia solo existe si el ancla sobrevivió de alguna forma reconocible. Cuando el ancla era un carácter que alguien borró hace un año, no hay correspondencia, hay una elección arbitraria disfrazada de traducción.

Vale la pena notar que esta imposibilidad es específica de las estructuras que anclan por identidad de vecinos, es decir, exactamente las que el nivel 32 presentó como la solución correcta para secuencias. Un tipo de dato cuyas operaciones sean absolutas, como fijar el valor de una clave o sumar una cantidad a un contador, no tiene este problema: sus operaciones antiguas siguen siendo aplicables porque no dependen de que nada anterior siga existiendo. El caso difícil no es una propiedad de los CRDT en general sino el precio concreto de haber resuelto bien el orden en una secuencia, y esa correlación entre la calidad de la solución y la gravedad de su factura es un patrón que ya ha aparecido varias veces en el track.

La cuarta propuesta, y la más sofisticada, es conservar un índice reducido que registre a qué se convirtió cada identificador podado, una especie de tabla de reenvío. Es una idea buena y tiene un problema de tamaño insalvable: para que sirva, la tabla debe cubrir todos los identificadores descartados, con lo cual crece exactamente igual que aquello que se quería tirar. Se puede acotar guardando solo los últimos meses, y entonces se ha vuelto al punto de partida con un corte más lejano y un caso de reaparición idéntico un poco más allá. La tabla de reenvío no elimina el problema, lo desplaza y cobra por el desplazamiento.

Existe una quinta propuesta que sí funciona y que conviene mencionar aunque no sea una salida al caso, sino su prevención: hacer que el dispositivo rezagado no llegue nunca a estar por debajo del corte, avisándole antes de que ocurra. Si un dispositivo lleva mucho sin conectar y el sistema tiene alguna forma de alcanzarlo, una notificación oportuna convierte el problema en una sincronización normal. No siempre hay forma de alcanzarlo, no siempre el usuario reacciona y el caso sigue existiendo para quienes no lo hagan. Pero reduce la población afectada lo bastante como para que merezca construirse antes que cualquiera de las tres salidas, y con frecuencia se olvida porque no es parte del compactador y nadie se siente dueño de ello.

Vale la pena enunciar el patrón común a las cuatro, porque es el que explica que no haya una quinta. Cada propuesta intenta recuperar información que la compactación destruyó, usando algo que la compactación conservó. Como la compactación consiste precisamente en destruir lo que ya no se puede reconstruir desde lo conservado, cualquier propuesta de esa forma es contradictoria consigo misma. Lo único que queda por decidir es qué hacer sin esa información, y ahí es donde la ingeniería cede el paso a la política.

⚠️
La política tiene que existir aunque decidas no podar

Un equipo puede concluir legítimamente que en su producto el crecimiento es tolerable y que no va a podar nunca, y esa es una decisión defendible que muchos sistemas serios toman. Lo que no es defendible es creer que esa decisión elimina el problema de esta lección. En cuanto exista cualquier mecanismo que descarte historia, aunque sea indirecto, el caso reaparece: un cambio de formato de almacenamiento, una migración de esquema, un vaciado de caché en el navegador, un borrado de datos del sitio, una recuperación desde copia de seguridad o simplemente un usuario que reinstala la aplicación. Todos esos eventos producen exactamente la misma situación, un estado local anclado a un pasado que el resto del sistema ya no reconoce, y todos ocurren en producción con más frecuencia que la poda deliberada. La política de reaparición no es un accesorio del compactador: es lo que tu producto hace cuando dos copias dejan de tener pasado común, y eso pasa con o sin compactación.

Escribir la política antes que el código

El orden correcto de trabajo aquí es inverso al habitual y merece defenderse. Primero se redacta, en lenguaje natural y en la lengua del usuario, qué ocurre cuando un dispositivo reaparece tarde. Después se comprueba que esa frase resulta aceptable leída en voz alta por alguien que no ha participado en el diseño. Y solo entonces se implementa el mecanismo que la ejecuta. Hacerlo al revés produce sistemas cuya política es un efecto secundario de una decisión de implementación que nadie recuerda haber tomado.

💡
Convierte el caso raro en una prueba que se ejecuta siempre

La medida más eficaz para que esta política no se pudra es sacarla del terreno de lo excepcional y meterla en el conjunto de pruebas que corre en cada cambio. El escenario cabe en pocas líneas: crear un documento, bifurcar una réplica, editarla, podar en la rama principal por debajo del punto de bifurcación y reconectar. Lo importante no es que la prueba pase, porque no hay un resultado correcto; lo importante es que afirme exactamente lo que tu política dice que debe ocurrir. Si la política es rescatar, la prueba comprueba que el contenido huérfano aparece en el sitio previsto. Si es rechazar, comprueba que el documento aislado existe y es accesible. Si es reconstruir, comprueba que se emite el aviso. Escrita así, la prueba deja de verificar código y pasa a verificar una promesa, y el día que alguien cambie la política sin darse cuenta, fallará y obligará a que la decisión se vuelva a tomar de forma consciente.

La razón de ese orden es que la frase es una restricción mucho más fuerte que el código. Si la frase honesta es tu trabajo de las últimas tres semanas no se ha podido integrar y se ha guardado como documento aparte, el producto tiene que tener un lugar donde guardar documentos aparte y una manera de que el usuario los encuentre, y eso son decisiones de interfaz que hay que tomar antes. Si la frase honesta resulta ser hemos perdido tu trabajo, la conclusión no es escribirla mejor: es que has elegido mal la salida o que no puedes podar con ese plazo.

Hay una prueba de honestidad sencilla que conviene aplicar a la frase resultante, y es comprobar si sobrevive a ser leída por la persona afectada en el peor momento posible. No en una página de documentación, no en una nota de versión, sino en pantalla, el lunes por la mañana, después de dos semanas de trabajo. Muchas formulaciones que parecen aceptables en un documento de diseño se caen ahí, y se caen por un motivo concreto: usan la voz pasiva para ocultar quién decidió. No se pudieron integrar los cambios describe un accidente meteorológico; el sistema descartó el historial anterior a marzo y por eso tus cambios no encajan describe una decisión que alguien tomó, que es lo que realmente ocurrió y lo que el usuario tiene derecho a saber.

Merece la pena señalar dónde vive esa frase una vez escrita, porque suele quedarse en el documento de diseño y no llegar a ninguna parte. Tiene tres destinos y los tres importan: la pantalla que el usuario ve al reconectar, la documentación pública del producto y el guion que usa el equipo de soporte cuando alguien escribe preguntando dónde está su trabajo. Que las tres versiones digan lo mismo es una prueba bastante fiable de que la política es real; que difieran, o que dos de las tres no existan, indica que lo que hay es una implementación y no una decisión.

Conviene también fijar quién es el dueño de esta decisión dentro del equipo, porque en su ausencia se toma sola. En la práctica acaba decidiéndola quien implementa el compactador, normalmente eligiendo la opción que deja el código más limpio, que es reconstruir. Nadie lo aprueba, nadie lo documenta y nadie lo comunica, y el producto adquiere una política de pérdida de datos por omisión. La forma de evitarlo no es un proceso pesado: basta con que la frase de la sección anterior figure en el documento de diseño de la función y que alguien que no sea quien la implementa la haya leído y esté de acuerdo con lo que dice.

No estás resolviendo un caso raro: estás fijando el límite de la promesa

Conviene entender qué se decide realmente aquí, porque el escenario parece marginal y no lo es en absoluto. Local-first hace una promesa muy concreta y muy fuerte: tus datos están en tu dispositivo, funcionan sin red, son tuyos y no dependen de que nadie más esté encendido. Esa promesa es la razón entera por la que alguien acepta la complejidad de todo lo anterior. La política de reaparición es, literalmente, el enunciado del plazo tras el cual esa promesa deja de aplicarse. No es un caso límite del compactador: es la letra pequeña del argumento de venta, y hasta que alguien la redacta, el producto está prometiendo algo que no puede cumplir. Fíjate además en la simetría con lo que ocurre del otro lado. Un sistema centralizado no tiene este problema porque nunca hizo la promesa: si tu dispositivo lleva seis meses sin conectarse, no tenías datos, tenías una caché, y una caché caducada no genera ninguna expectativa. Local-first cambia esa relación por completo y con ella cambia la naturaleza del fallo. Lo que en un sistema centralizado sería una sesión expirada, aquí es la pérdida de algo que le dijiste al usuario que le pertenecía. Ahí está el peso real de esta decisión, y explica por qué no puede delegarse en un valor de configuración ni resolverse con una mejora del algoritmo. Elegir entre rechazar, reconstruir y rescatar es elegir cómo se comporta tu producto en el único momento en que su tesis fundacional se pone a prueba, y hacerlo bien no consiste en encontrar la opción sin daño, que no existe, sino en asegurarse de que el daño sea visible, esté explicado y recaiga donde el usuario habría elegido que recayera si le hubieran preguntado. La ingeniería, en este punto, se agota; lo que queda es una decisión sobre qué clase de producto quieres tener.

⚔️ Redacta tu política antes de implementarla
  1. Escribe en una frase, en la lengua de tus usuarios, qué ocurre cuando un dispositivo reaparece por debajo del corte. Sin jerga y sin condicionales.
  2. Léesela en voz alta a alguien ajeno al proyecto y anota su primera reacción. Esa reacción es tu métrica.
  3. Decide cuál de las tres salidas implementas y escribe el motivo en dos líneas, en términos de qué pierde el usuario y no de qué es más fácil.
  4. Diseña la pantalla que ese usuario ve al reconectar. Si tu salida es reconstruir y no hay pantalla, tu producto pierde datos en silencio.
  5. Enumera los otros cinco eventos que producen el mismo estado sin pasado común en tu sistema y comprueba que los cinco pasan por la misma política.
  6. Implementa el archivado previo aunque tu salida sea reconstruir, y verifica que el fichero resultante se puede abrir sin tu aplicación.
  7. Separa en tu código las operaciones huérfanas de las que sí tienen ancla viva, y mide la proporción real en un caso de prueba de tres meses.
  8. Prueba el caso de verdad: apaga un dispositivo, edita en él, poda en los demás y reconéctalo. Anota qué hizo tu sistema realmente frente a lo que creías.