Viernes 18hs: la obsesión de entender qué pasa adentro
Viernes a las seis, casa en silencio. Un webhook duplicando registros. Casi 40 años de oficio para entender que la calle no cambió.
Daniel Alvarez
6 min lectura
Son las seis de la tarde de un viernes. La casa está en un silencio raro. Ruth salió hace un rato y las chicas están en la suya: Vicky metida en su pieza con los auriculares puestos, Meli preparándose algo para comer en la cocina. En el escritorio, tengo un mate frío al lado del teclado que hace un par de horas que me olvidé de tomar.
Frente a mí, la pantalla está dividida en cuatro espacios. En uno, una terminal escupe logs de Node en tiempo real. En el otro, la consola de MongoDB. Arriba a la derecha, un issue abierto en el repositorio, y abajo, una instancia de Claude Code corriendo un refactor menor en un branch totalmente aislado para no ensuciar el entorno.
Estoy debuggeando un problema en FirmaLaboral, nuestro sistema de gestión y firma electrónica. Llevamos unos 7 años desde el primer commit y más de 6 años operando este monstruo en producción. Tenemos más de 400 PyMEs en Argentina y Uruguay que dependen de esta infraestructura para procesos críticos de recursos humanos, así que no hay margen para que los datos sean inconsistentes.
El síntoma apareció en los logs hace un par de días: cada tanto, bajo ciertas condiciones de carga, un proceso de notificaciones atado al webhook de Mercado Pago está duplicando un registro en nuestra base. Aclaro algo fundamental antes de seguir: Mercado Pago está haciendo exactamente lo que tiene que hacer y no le estamos cobrando dos veces a ningún cliente. Lo que se está duplicando es exclusivamente nuestro registro interno de confirmación. Un problema silencioso que no rompe la interfaz hoy, pero que ensucia la base de datos.
La diferencia entre código que compila y código que resiste
El diagnóstico me llevó un buen rato de cruzar marcas de tiempo. La idempotencia del webhook (la capacidad de recibir el mismo evento dos veces y procesarlo una sola) estaba implementada a nivel de la aplicación con un chequeo lógico previo.
La secuencia de código era la clásica que te escribe cualquier tutorial o modelo generativo hoy en día: llega el evento, la aplicación consulta a Mongo si ese ID ya fue procesado. Si la respuesta es no, avanza, ejecuta la lógica del negocio y finalmente guarda el registro.
El problema es que ese chequeo no era atómico. Cuando hay un pico de red o un reintento de conexión por latencia, los eventos llegan casi en simultáneo. Dos hilos de ejecución independientes entran al mismo tiempo, los dos consultan la base de datos al mismo milisegundo, ninguno de los dos encuentra el registro, y los dos proceden a escribir el éxito de la operación. Una race condition (condición de carrera) de manual.
En producción, tarde o temprano, la aplicación no se porta bien. Podés meter mil validaciones lógicas en tu backend de Node, pero si dependés de que el flujo de eventos sea perfectamente ordenado y secuencial bajo estrés, vas a perder o duplicar datos.
La única garantía real va en la base. El arreglo correcto acá no es sumar otro bloque if en el código. Es mover la responsabilidad a MongoDB con un UNIQUE constraint compuesto en la colección, atando el provider (Mercado Pago), el event_id y el tenant_id. Es una operación atómica. A partir de ahí, la base de datos rechaza el segundo intento de inserción a nivel motor, sin importar qué tan confundida, rápida o asíncrona esté tu capa de aplicación.
Casi cuatro décadas frente a la misma pantalla
Mientras miraba en la terminal local cómo el constraint de Mongo frenaba en seco la carrera de los microservicios, la cabeza se me fue a otra época. Me acordé de mi pieza en 1989.
Tenía 15 años y una PC 386. Estaba en una casa en Mendoza, sin internet (porque no existía), tratando de que Turbo Pascal 7.0 hiciera lo que yo tenía en la cabeza. Arriba de la mesa tenía el manual oficial de Borland en papel, pesado, marcado y doblado en las páginas críticas.
Me acuerdo de mi vieja golpeando la puerta a las once de la noche: "Dani, apagá eso que mañana hay que levantarse temprano". Mi respuesta era automática y siempre la misma: "Sí, ma, ya termino".
Y me quedaba dos horas más.
Estaba solo, peleando con punteros que se iban a cualquier lado de la memoria, sobreescribían datos y me colgaban el sistema entero, obligándome a reiniciar la máquina con el botón físico. No había a quién preguntarle. No había foros. Si no leías el manual y no entendías qué estaba pasando adentro del procesador, el programa simplemente no andaba.
Tengo 52 años. Casi cuatro décadas después de esa PC 386, la situación de fondo sigue siendo exactamente la misma.
Cambió el entorno, por supuesto. En vez de lidiar con Turbo Pascal, peleo con el asincronismo en Node y las transacciones de MongoDB. En vez de disquetes de 3½ que se corrompían en el bolsillo, hoy reniego con branches de Git mal mergeados. En vez de hojear el manual de Borland, tengo diez pestañas de documentación abiertas en el monitor secundario.
Y, fundamentalmente, en vez de la soledad absoluta de esa época, hoy tengo a la inteligencia artificial. Si me trabo, le tiro un log de error incomprensible a un LLM y en 30 segundos me devuelve tres hipótesis razonables sobre dónde puede estar la falla.
Pero la cabeza es la misma. La necesidad física de entender qué está pasando realmente adentro de la máquina no cambió un milímetro. El "una línea más y cierro" sigue intacto, solo que ahora lo digo a las dos de la mañana, siendo el responsable de una infraestructura que sostiene empresas, y acompañado de un mate lavado.
El oficio de no mirar para otro lado
La IA hoy te da hipótesis al instante. Te escribe el andamiaje del código y te ahorra meses enteros de fricción inútil peleando con la sintaxis. Esa velocidad es real, es brutal, y negar su impacto es quedarse fuera del mercado.
Pero hay cosas que ningún modelo estadístico te va a dar, por más contexto que le inyectes.
No te da la decisión de quedarte un viernes a las seis de la tarde debuggeando un problema que a simple vista parecía inofensivo. No te da la incomodidad en la boca del estómago de ver algo que está "casi bien" y saber que bajo ninguna circunstancia podés dejarlo así. No te da la sospecha instintiva de que un bug chico hoy, tapado con un parche rápido a nivel aplicación, es un problema gigante mañana a la madrugada cuando el volumen de usuarios se multiplique por diez.
Eso es oficio. No es talento puro, ni es genialidad espontánea. Es simplemente obsesión técnica acumulada y repetida durante años. Es entender que la responsabilidad de que un sistema B2B funcione no se delega ni a un framework, ni a un proveedor de nube, ni a un prompt de catorce líneas.
Son las 19:42. El hilo técnico de hoy está cerrado: la causa raíz de la duplicación de notificaciones está identificada. El fix lógico con el constraint atómico ya está escrito, probado en mi entorno local y estresado con un script que simula concurrencia masiva.
Pero no voy a pushear un cambio de arquitectura de base de datos a producción un viernes a la noche. Modificar índices en colecciones multi-tenant con usuarios operando requiere respeto. Queda documentado, testeado y empaquetado para salir en el sprint del lunes a primera hora, con el equipo conectado y toda la calma del mundo.
Ahora sí, voy a calentar el agua para un mate nuevo.
¿Querés un par senior recurrente?
1 call semanal de 60 min + acceso async + code reviews. Un plan mensual para founders construyendo SaaS con IA que no quieren estar solos en cada decisión técnica.
- Duración
- 3 meses mín.
- Precio
- Desde USD 900/mes

Sobre el autor
Daniel Alvarez
CTO y cofundador de FirmaLaboral.net (SaaS HRTech con 400+ PyMEs en AR + UY). 30+ años escribiendo código, hoy ayudando founders a llevar su SaaS post-vibe-coding a producción real.
¿Te avisaré del próximo?
Publico 1-2 notas técnicas por mes
Pensadas para founders que están construyendo SaaS con IA y necesitan que llegue a producción sin romperse. Sin spam, sin promos, sin newsletter genérica. Tres formas de seguirlo:
Seguir leyendo
8 min
Migrar la DB en vivo: 5 etapas, cero downtime
Dual write, backfill, shadow reads, switchover, decommission. 5 etapas para migrar una DB en producción sin apagar el sistema.
7 min
5 AM en un yacimiento: offline-first en serio
5 AM en un yacimiento patagónico. Sin internet, con guantes, frío. SaaS B2B real, no MVP de fibra óptica. Arquitectura offline-first.