Marcos es autónomo y lleva la facturación de su estudio de diseño con una app casera que hizo él mismo hace un par de años, para no pagar una suscripción a un gestor solo para sacar PDFs. La regla que le importa no es de programación, es de Hacienda: dentro de un ejercicio, los números de factura tienen que ser correlativos, sin huecos y sin duplicados. Si dos facturas comparten número, en una inspección eso se explica mal.
El contador que fallaba los sábados
La función «siguiente número de factura» era sencilla: leer el último número guardado, sumarle uno, guardarlo, devolverlo. Funcionó meses seguidos. El fallo apareció un sábado en que su pareja, que también usa la app para sus propias facturas desde la misma cuenta, tenía una pestaña abierta a la vez que él tenía otra. Los dos emitieron una factura casi en el mismo segundo. Las dos salieron con el número 14. Y el 15 no lo usó nadie.
No era un fallo raro de ver: dos lecturas del mismo contador antes de que ninguna de las dos escrituras llegara a guardarse. Una condición de carrera clásica, solo que aquí el resultado no es un contador desincronizado en un test, es una factura ilegal.
Reproducirlo sin exponer nada
Para depurarlo con la IA local de Elffuss Code, Marcos necesitaba datos realistas: nombres de clientes de verdad, NIFs de verdad, importes de verdad, porque el bug solo aparecía con el histórico real de facturas, no con datos inventados de prueba. Pegó los fixtures tal cual en el editor, sin pararse a pensar si debía anonimizarlos antes, porque el modelo corre en su propio navegador, sobre su GPU, y ese prompt no sale del portátil. Con un servicio en la nube esa duda sí habría tenido que parársela: son datos fiscales de terceros.
La IA local, con el contador y las dos pestañas delante, señaló el problema real: localStorage.getItem y localStorage.setItem no son atómicos entre pestañas. Propuso serializar la operación con la Web Locks API del navegador (navigator.locks.request), que deja pasar a una sola pestaña a la vez por una sección crítica aunque haya varias abiertas.
Leer, sumar y guardar dejaron de ser tres pasos sueltos y pasaron a ser una operación con candado: la segunda pestaña espera su turno en vez de leer el mismo número que la primera.
Con el candado puesto, Marcos abrió seis pestañas a la vez y disparó cincuenta facturas en bucle. Cada una salió con un número distinto, sin huecos.
Lo que se queda de este caso no es la Web Locks API, que es de manual. Es que depurar un bug de facturación real exige datos de facturación reales, y eso normalmente te hace dudar antes de pegarlo en un prompt. Aquí no hubo que dudar.
Si tienes un bug que solo se reproduce con tus datos de verdad, abre Elffuss Code: corre entero en tu navegador y ni el código ni el prompt suben a ningún sitio.
Abrir Code →