- Las apps local-first prometen respuesta rápida y privacidad básica por defecto, pero tienen la limitación de que implementar un soporte offline real es muy difícil.
- La razón principal es la complejidad de la sincronización: cuando se modifican datos al mismo tiempo en varios dispositivos, el sistema debe converger exactamente al mismo estado.
- Hay dos grandes retos técnicos: la incertidumbre del orden temporal y los conflictos.
- Para resolver esto, es necesario aplicar diseños de sistemas distribuidos como Hybrid Logical Clocks (HLCs) y CRDTs.
- Al aprovechar extensiones basadas en SQLite, se puede ofrecer una arquitectura de sincronización simple y confiable, utilizable en todas las plataformas.
La promesa y la realidad de las apps offline-first
- Las apps offline-first prometen respuesta inmediata, privacidad básica por defecto y uso sin esperas de carga incluso en redes inestables.
- En la práctica, la mayoría de las apps no implementan bien el soporte offline; la gran mayoría solo adopta un enfoque de guardar temporalmente los cambios en local y enviarlos después cuando vuelve la conexión.
- Esta implementación es poco confiable y termina derivando en mensajes de advertencia como "es posible que los cambios no se guarden".
La dificultad fundamental de la sincronización
- Al crear una app local-first, inevitablemente se termina construyendo un sistema distribuido.
- Varios dispositivos pueden modificar los datos de forma independiente mientras están offline y, cuando vuelven a conectarse, deben converger con exactitud al mismo estado.
- Para lograrlo, hay dos grandes desafíos:
- Incertidumbre en el orden de los eventos
- Conflictos sobre los mismos datos
1. Incertidumbre en el orden de los eventos
- Los eventos ocurren en distintos momentos en varios dispositivos, y el estado puede cambiar según el orden.
- Ejemplo: el dispositivo A establece x=3, el dispositivo B establece x=5 → tras hacer cambios por separado en offline, al sincronizar puede haber resultados distintos.
- Las bases de datos centralizadas tradicionales lo resuelven con consistencia fuerte, pero eso requiere sincronización global y no encaja con un sistema local-first.
- Al final, hace falta determinar el orden adecuado para cada evento incluso en un entorno dinámico y distribuido; se necesita una forma de ordenar sin un reloj central.
Introducción de Hybrid Logical Clocks (HLCs)
- Hybrid Logical Clocks (HLCs) son un algoritmo simple pero efectivo que permite acordar en la práctica el orden de los eventos entre dispositivos.
- HLC combina información de tiempo físico y un contador lógico.
- Por ejemplo:
- El dispositivo A registra un evento a las 10:00:00.100, y su HLC es (10:00:00.100, 0)
- El dispositivo B, al recibir el mensaje, aunque su reloj vaya más lento, eleva su HLC a (10:00:00.100, 1)
- Así, es posible fijar correctamente el orden de los eventos sin importar la diferencia entre los relojes físicos de ambos dispositivos.
2. El problema de los conflictos
- Aplicar el orden correcto no es suficiente: si distintos dispositivos modifican de forma independiente los mismos datos, los conflictos son inevitables.
- La mayoría de los sistemas exige que el desarrollador escriba manualmente el código de resolución de conflictos, lo que provoca riesgo de errores y carga de mantenimiento.
Uso de CRDTs
- La mejor opción es aplicar Conflict-Free Replicated Data Types (CRDTs).
- Los CRDTs garantizan que sin importar el orden de sincronización, o incluso si una operación se aplica duplicada, el estado final de cada dispositivo siempre será el mismo.
- La estrategia CRDT más simple es Last-Write-Wins (LWW).
- A cada actualización se le asigna una marca de tiempo.
- Durante la sincronización, se elige el valor con la marca de tiempo más reciente.
Ventajas de SQLite
- Para construir una app local-first, es indispensable contar con una base de datos local ligera y confiable, y SQLite es la mejor elección.
- Si se implementa la sincronización mediante extensiones del framework basadas en SQLite, se obtienen ventajas como:
- Aplicar mensajes es simple: consultar el valor actual → si la nueva marca de tiempo es más reciente, sobrescribir → si no, ignorar
- Este método garantiza la convergencia del estado en todos los dispositivos sin depender del orden de sincronización
El significado de esta arquitectura
- Esta estructura hace posible una sincronización simple y confiable:
- Confiabilidad sin pérdida de datos incluso tras semanas en modo offline
- Propiedad determinista de converger siempre al estado final
- Se resuelve solo con una extensión ligera de SQLite y sin dependencias pesadas
- Soporte para todas las plataformas principales: iOS, Android, macOS, Windows, Linux, WASM, etc.
Recomendaciones para desarrolladores
- Conviene evitar enfoques que solo "simulan" soporte offline con una cola simple de requests.
- Es necesario adoptar el concepto de eventual consistency y aprovechar tecnologías probadas de sistemas distribuidos como HLC y CRDT.
- En lugar de frameworks grandes y complejos, es preferible buscar estructuras pequeñas y sin dependencias.
- Como resultado, la app puede disfrutar de ventajas como inicio inmediato, uso offline y privacidad básica por defecto.
Referencia al open source SQLite-Sync
- Si te interesa un motor offline-first multiplataforma, listo para usar en producción, puedes revisar la extensión open source SQLite-Sync
1 comentarios
Opiniones en Hacker News
Cache-Controlen las respuestas de la API y respetándolo en la capa de red. Así, aunque se cambie la vida útil del caché desde el servidor, se aplica de inmediato sin necesidad de actualizar la app