Este artículo puede incluir enlaces de afiliado. Si compras a través de ellos, me llevo una pequeña comisión sin coste extra para ti. Más información.
Hay una clase de bug que no aparece en tu máquina. Ni en la del compañero, ni en el entorno de integración, ni en el preproducción que montasteis con tanto cariño para que fuera «idéntico a producción». Funciona en todas partes menos en el único sitio que importa.
Y cuando te toca pelearte con eso unas cuantas veces, empiezas a desarrollar un sexto sentido que ningún tutorial te enseña. Ahí entra el debugging en producción.
Llevo años programando en Java y durante mucho tiempo creí que depurar era una cosa: poner un breakpoint, mirar variables, avanzar paso a paso. Eso sirve para los bugs domésticos. Los de producción son otra especie. No se cazan igual.
Por qué hacer debugging en producción no se parece en nada al entorno local
En local tú controlas todo. Los datos los metes tú, normalmente cuatro filas de prueba con nombres como «Pepe» y «test1». La concurrencia es inexistente porque eres una sola persona dándole a F5. La configuración la tienes en un application-dev.properties que conoces de memoria.
Producción es lo contrario de todo eso. Hay millones de filas, algunas con caracteres que no sabías que existían. Hay cien peticiones simultáneas peleándose por el mismo recurso.
Y hay una configuración que alguien tocó hace ocho meses en una variable de entorno que no está documentada en ningún sitio. Si no se hace debugging en producción, no eres capaz de verlo.
El bug no está en tu código. Está en la diferencia entre tu mundo y el real.
Lo primero que aprendí es a desconfiar de la frase «en mi máquina funciona». No es que sea falsa. Es que es irrelevante. Tu máquina es un caso particular muy poco representativo.

El volumen de datos lo cambia todo
Hay código que es correcto y a la vez una bomba de relojería. El ejemplo clásico que me he comido en producción más de una vez: cargar una colección entera en memoria para procesarla.
Con tus cuatro filas de prueba vuela. Con dos millones de registros te tumba el servicio con un OutOfMemoryError a las tres de la tarde de un martes.
Algo tan inocente como esto:
// Carga TODO en memoria. En local con 10 filas, perfecto.
// En producción con millones, OutOfMemoryError.
List<Pedido> pedidos = pedidoRepository.findAll();
return pedidos.stream()
.filter(p -> p.getEstado() == Estado.PENDIENTE)
.collect(Collectors.toList());
El filtro debería estar en la consulta, no en memoria. Pedirle a la base de datos solo lo que necesitas en lugar de traértelo todo y descartar el 99%. Es una diferencia que en local ni notas y en producción es la diferencia entre que el servicio viva o muera.
El instinto que desarrollas es preguntarte siempre «¿y esto con qué volumen se ejecuta de verdad?» antes de escribir un findAll.
El volumen también saca a la luz problemas de rendimiento que en local pasan desapercibidos. Una consulta dentro de un bucle que genera mil consultas más. El famoso N+1.
En local son cuatro consultas y ni te enteras. En producción son cuarenta mil y el monitor de la base de datos se pone rojo.
La concurrencia es donde se pierde la inocencia
Los bugs de concurrencia son los que de verdad te cambian la forma de pensar. Porque no se reproducen. Aparecen una vez cada diez mil ejecuciones, dejan un rastro imposible en los logs y cuando intentas reproducirlos en local no pasa nada. Nunca pasa nada cuando miras.
Me recuerdan a Oli. Cuando estás delante es el gato más tranquilo del mundo, una bola blanca y atigrada durmiendo. Te das la vuelta dos minutos y ha tirado un vaso de agua sobre el teclado.
El bug de concurrencia es igual: solo actúa cuando no lo observas. El acto de mirar cambia el comportamiento.
Aquí aprendes a leer logs como un detective. A pensar en términos de qué pasa si dos hilos entran a la vez en ese método. A sospechar de cualquier estado compartido que no esté protegido.
A entender que un HashMap compartido entre hilos no es un bug evidente, es una trampa que tarde o temprano salta.
«Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it.»
— Brian Kernighan
Kernighan lo dice medio en broma, pero en producción se vuelve literal. El código que te parecía ingenioso, ese stream de tres niveles encadenados que escribiste orgulloso, es justo el que no puedes entender a las dos de la mañana cuando todo está caído.
La humildad de escribir código aburrido y obvio se aprende a base de sufrir el código listo.
El logging deja de ser opcional
En local no necesitas logs, tienes el debugger. En producción el log es lo único que tienes. No puedes pausar el mundo, no puedes poner un breakpoint en un servicio que atiende a usuarios reales. Solo te quedan las migas de pan que dejaste tú mismo en el pasado.
Y ahí desarrollas otro instinto: loguear lo que de verdad importa. No el «entré aquí» y «salí de aquí» que no sirve para nada, sino el identificador de la operación, el estado de las variables clave, el contexto que te permitirá entender qué pasó cuando no estabas mirando.
Un buen log es una carta que te escribes a ti mismo para un futuro en el que estarás cansado y asustado.
El instinto que solo da la producción
Después de unos cuantos incendios, cambia algo en cómo programas. Empiezas a escribir el código pensando en cómo fallará, no solo en cómo funcionará. Te preguntas qué pasa con datos raros, con carga alta, con la red caída a mitad de una operación.
Esa paranoia productiva no te la enseña ningún entorno local porque en local nunca pasa nada malo de verdad.
También aprendes a no tocar nada con prisa. La tentación de meter un fix rápido en caliente es enorme cuando todo está caído. Pero un cambio sin entender la causa suele empeorar las cosas.
He aprendido a respirar, a leer, a entender antes de tocar.
Un poco como cuando entró Toñi en casa de acogida y todo el mundo quería ir a achucharla: lo que tocaba era observar primero, dejar que el sistema te enseñara cómo se comporta antes de meter mano.
Sobre esa sensación de dudar más cuanto más sabes tiene mucho que ver cuando tardé en entender un bug que tenía delante. La producción tiene mucho que ver con eso. Cuanto más la sufres, menos te fías de tus propias certezas.
Al final, depurar en producción no es una habilidad técnica más. Es una forma distinta de mirar tu propio trabajo. Dejas de escribir código que funciona en tu cabeza y empiezas a escribir código que sobrevive en un mundo que no controlas.
Y eso, aunque duela aprenderlo a base de avisos a las tres de la mañana, te hace mejor programador.
Foto: macbook pro on brown wooden table por Clint Patterson en Unsplash.
¿Quieres más?
Recibe los nuevos artículos sobre gatos, escritura y código. Sin frecuencia fija, sin paja de IA, sin spam.
Suscribirme