Un bug de 16 años no suena como algo que debería seguir vivo en 2026, pero pasó. SQLite, una de las bases de datos más usadas del mundo, tenía un problema en su modo WAL que se mantuvo oculto durante años hasta que apareció TLA+, una herramienta de verificación formal, para poner el modelo bajo presión y encontrar una secuencia de eventos que rompía las reglas.
La historia importa por dos razones. La primera es obvia: si usas SQLite en producción, en un móvil, en un dispositivo embebido, en un edge server o en un servicio pequeño que no quiere cargar con una base pesada, te conviene entender qué clase de errores pueden esconderse durante tanto tiempo. La segunda es más interesante: esto no fue un bug cazado con pruebas unitarias más grandes ni con un profiler. Fue encontrado modelando el sistema y dejando que una herramienta matemática explorara estados que una persona no revisaría a mano.
Qué pasó con SQLite y WAL
SQLite tiene una reputación fuerte por una razón simple: es pequeña, rápida, fácil de integrar y muy estable. Pero esa estabilidad no significa que todo esté blindado. El caso que se volvió público en el blog de Ubuntu se centra en WAL, sigla de Write-Ahead Logging, un modo de journaling que permite mejor concurrencia y recuperación más eficiente. Según la publicación original, el bug llevaba cerca de 16 años sin detectarse en ese camino específico de ejecución.
La idea básica de WAL es conocida: en vez de escribir directamente al archivo principal de la base, primero registras los cambios en un log. Eso ayuda a recuperar consistencia si algo falla en medio de una escritura. El problema es que, cuando combinas lectores, escritores, checkpoints y recuperación, el espacio de estados crece rápido. No necesitas una falla frecuente para tener un bug serio; basta con una secuencia rara de eventos que ocurra una vez cada tanto en producción.
Aquí entra lo valioso de la historia: el error no era un “se cayó todo” visible. Era el tipo de fallo que puede quedarse escondido mientras el sistema parece funcionar bien. Y eso es justo lo que hace que la verificación formal tenga sentido en componentes críticos o muy reutilizados. No busca solo casos comunes, busca casos posibles.
WAL en una frase
WAL no reemplaza la base principal de golpe. Primero escribe un registro de cambios, luego consolida. Ese patrón reduce riesgos de corrupción y mejora el rendimiento en ciertos escenarios, pero también introduce más estados intermedios que validar.
Por qué 16 años no es una exageración
En software de infraestructura, un bug viejo no siempre significa que nadie lo vio. A veces significa que nadie activó la combinación exacta de condiciones. Puede depender de timing, concurrencia, checkpoints, tamaño del archivo o una secuencia de operaciones poco común. En producción, esos casos raros sí aparecen, solo que no todos los días.
TLA+: qué hace y por qué sirvió aquí
TLA+ es un lenguaje y ecosistema para especificar sistemas concurrentes y distribuidos de forma matemática. No es un framework de testing, ni un linter, ni una herramienta para medir performance. Sirve para describir cómo debería comportarse un sistema y luego explorar si existen estados inválidos. Si el modelo permite una secuencia imposible, la herramienta te lo muestra.
La gracia de TLA+ es que te obliga a pensar en términos de estados, transiciones e invariantes. En lugar de preguntar “¿este test cubre este caso?”, preguntas “¿puede el sistema llegar a un estado que rompa esta regla?”. Ese cambio de enfoque es muy útil para almacenamiento, consenso, locks, cachés y cualquier cosa con concurrencia real.
En el caso de SQLite, el trabajo no consistió en “probar más”. Consistió en modelar el comportamiento relevante del subsistema y dejar que el model checker buscara contraejemplos. Ese tipo de análisis encuentra bugs que no aparecen en cientos de pruebas, porque las pruebas suelen depender de rutas que alguien ya imaginó.
Lo que sí y lo que no hace TLA+
TLA+ no reemplaza tus tests, tus fuzzers ni tu monitoreo. Tampoco garantiza que tu implementación final sea perfecta. Lo que sí hace es reducir el espacio de incertidumbre antes de que el código toque producción. Si el modelo ya muestra una inconsistencia, tienes una señal fuerte de que vale la pena revisar la implementación.
Ejemplo simple de lo que modelas
En un sistema de almacenamiento puedes modelar cosas como:
- qué pasa si un escritor se interrumpe a mitad de una operación
- cómo se sincronizan lectores y checkpoints
- qué estado debe permanecer siempre verdadero, incluso con fallas
- cómo se recupera la base después de un corte de energía
No necesitas modelar cada línea de código. Modelas lo suficiente para capturar la lógica que puede romperse.
Verificación formal vs testing tradicional
Aquí conviene ser muy claro: no se trata de elegir entre pruebas y verificación formal. Se complementan. Las pruebas te muestran fallos en casos concretos. La verificación formal te muestra si, dentro del modelo, existe una ruta que viola una propiedad. Una cubre ejemplos; la otra explora posibilidades.
Si trabajas con una base de datos embebida, el costo de un bug puede ser alto aunque el componente sea pequeño. Piensa en un POS que guarda ventas offline, un sistema de inventario en una tienda, un dispositivo industrial que registra telemetría o una app móvil que sincroniza datos cuando vuelve la conexión. Un fallo de consistencia no siempre tumba el sistema, pero sí puede dejar datos perdidos o duplicados.
La comparación práctica se ve mejor en una tabla:
| Enfoque | Qué detecta | Costo inicial | Mejor uso |
|---|---|---|---|
| Tests unitarios | Errores en funciones específicas | Bajo | Lógica local y regresiones |
| Integración | Fallos entre componentes | Medio | Flujos completos |
| Fuzzing | Entradas raras o inesperadas | Medio | Parsers, protocolos, formatos |
| Verificación formal | Estados inválidos y carreras lógicas | Alto al inicio | Concurrencia, protocolos, almacenamiento |
Lo interesante es que TLA+ no compite con tu pipeline de CI. Más bien te ahorra tiempo cuando el problema de verdad no está en una función aislada, sino en la interacción entre partes. Y ahí es donde SQLite y WAL son un caso perfecto: el error no era una suma de líneas mal escritas, sino una combinación de estados.
Cuándo sí vale la pena usarlo
No necesitas TLA+ para cada feature. Sí tiene sentido cuando:
- El componente maneja estado compartido o concurrencia.
- Un fallo puede corromper datos o romper invariantes.
- El sistema es pequeño o mediano, pero muy crítico.
- Ya viste bugs difíciles de reproducir y quieres cerrar huecos de diseño.
- El costo de un error en producción supera el costo de modelar.
Cuándo probablemente no
Si estás cambiando un botón de UI, escribiendo una validación de formulario o ajustando una query simple, TLA+ probablemente es demasiado. Ahí te sirven más tests, code review y observabilidad. La clave es usar la herramienta correcta según el riesgo.
Qué le enseña esto a quienes usan SQLite en producción
SQLite sigue siendo una gran opción para muchísimos casos, especialmente cuando quieres cero fricción operativa. No administras un servicio aparte, no dependes de una red para cada consulta y el archivo vive donde lo necesitas. Eso la vuelve ideal para apps de escritorio, móviles, IoT y muchas cargas pequeñas o medianas.
Pero justamente por ser tan fácil de usar, a veces se subestima su complejidad interna. No hay que caer en el error de pensar que “si es embebida, entonces es simple”. SQLite resuelve problemas delicados de concurrencia, locking, journaling y recuperación. Mientras más cerca estás del almacenamiento, más caro puede salir un bug raro.
Para equipos en LatAm, esto tiene una lectura muy práctica. Hay muchos productos que operan con conectividad intermitente, hardware limitado o presupuestos ajustados. En esos contextos, una base embebida bien elegida te ahorra infraestructura. Pero también te obliga a cuidar más la confiabilidad porque no siempre tienes un plan B de failover sofisticado.
Señales de que debes prestar más atención
- Tu app guarda datos offline y luego sincroniza.
- El mismo archivo SQLite es leído y escrito por varios procesos o hilos.
- Dependés de recuperación después de cortes de energía.
- Usas WAL por rendimiento o concurrencia.
- Un registro duplicado o perdido te cuesta dinero o soporte.
Si te reconoces en uno o más puntos, no significa que debas migrar de base. Significa que deberías entender mejor el comportamiento interno y probar escenarios raros, no solo el happy path.
Qué revisar en tu stack
Empieza por lo básico: versión de SQLite, modo de journaling, configuración de busy timeout, patrón de acceso y estrategia de backups. Según la documentación oficial de SQLite, WAL es una pieza central para concurrencia y recuperación, así que vale la pena leerla con calma: https://www.sqlite.org/wal.html
También conviene revisar cómo se comporta tu app ante cierres inesperados, escrituras concurrentes y reintentos. Muchas veces el bug no está en la base, sino en cómo la integras. Un cliente mal diseñado puede convertir una base robusta en un cuello de botella o en una fuente de inconsistencias.
Cómo empezar con verificación formal sin volverte loco
No hace falta que conviertas todo tu sistema en un modelo matemático. De hecho, ese suele ser el error de entrada. Lo razonable es elegir una parte pequeña pero crítica, describir sus reglas y revisar si el modelo admite comportamientos prohibidos.
Un camino práctico sería este:
- Define una propiedad clara. Por ejemplo: “nunca debe haber dos versiones finales incompatibles del mismo registro”.
- Reduce el sistema a sus estados relevantes. No metas detalles de UI ni de red si no afectan la propiedad.
- Modela actores y transiciones. Escribe qué puede hacer un lector, un escritor, un checkpoint o un recovery.
- Ejecuta el model checker y mira contraejemplos.
- Corrige el diseño o la implementación, y vuelve a validar.
Si quieres ir más a fondo, la documentación oficial de TLA+ y TLC está en la guía de Leslie Lamport y en los recursos del ecosistema. Un punto de partida útil es la documentación del proyecto en GitHub: https://github.com/tlaplus/tlaplus
Lo más valioso es cambiar la cultura del equipo. En vez de asumir que un protocolo está bien porque “parece lógico”, lo documentas de forma precisa. Eso reduce discusiones vagas en code review y hace visibles los supuestos que normalmente quedan escondidos.
Un ejemplo mental simple
Supón una base embebida que debe garantizar que un registro pasa de pending a committed solo una vez. Si modelas el sistema y descubres una secuencia donde un checkpoint y un retry dejan el estado ambiguo, ya encontraste un problema antes de escribir una sola línea de producción. Ese es el tipo de ahorro que justifica el esfuerzo.
Tabla resumen
| Pregunta | Respuesta corta |
|---|---|
| ¿Qué encontró TLA+ en SQLite? | Un bug histórico en WAL que llevaba años oculto. |
| ¿Por qué no lo detectaron antes? | Porque requería una secuencia rara de estados y concurrencia. |
| ¿TLA+ reemplaza los tests? | No, los complementa. |
| ¿Sirve para cualquier proyecto? | No, sobre todo para sistemas con estado crítico o concurrencia. |
| ¿SQLite sigue siendo confiable? | Sí, pero como cualquier software complejo, conviene entender sus límites. |
| ¿Qué gana un equipo en LatAm con esto? | Menos riesgo de pérdida de datos en sistemas embebidos y offline. |
La lección de fondo es bastante simple: los bugs viejos no dejan de importar solo porque ya pasaron muchos años. En software, el tiempo no borra los errores, solo los vuelve más caros si siguen escondidos en producción. Si una herramienta como TLA+ puede ayudarte a exponer una falla antes de que afecte a usuarios reales, vale la pena mirarla con seriedad.
Y si usas SQLite en productos que no pueden darse el lujo de perder datos, este caso debería empujarte a revisar más que la API básica. Revisa el modo de journaling, tus patrones de concurrencia y tus supuestos sobre recuperación. A veces el problema no está en el código que escribiste hoy, sino en una esquina del sistema que nadie volvió a mirar en 16 años.
Preguntas frecuentes
¿Qué es exactamente TLA+?
¿SQLite sigue siendo una buena opción para producción?
¿Qué es WAL en SQLite?
¿Por qué un bug puede tardar 16 años en aparecer?
¿TLA+ sirve para equipos pequeños?
¿Qué tipo de proyectos en LatAm se benefician más de SQLite?
¿Cómo empiezo sin aprender TLA+ completo?
Azirgo
¿Listo para construir tu Producto Digital?
Sitios web, apps móviles, software a medida y soluciones blockchain. Cuéntanos qué tienes en mente y armamos un plan claro contigo.
- Cotización clara en 48 horas
- Equipo en Ecuador, atención en español
- Desde un MVP hasta un producto en producción