Debuggear* es todo un arte.
De hecho casi todo en el desarrollo de software y sistemas es un arte. Al menos así lo vemos aquí.
El arte detrás de esto está en la parte creativa, en la parte donde creas algo que al usuario le resulta útil. E implica muchísimas habilidades que caen en el rango de 'artes'. Es un arte poder entender una necesidad y luego plasmarla en un software (empezando por la interfaz y terminando por la arquitectura, o al revés). Es un arte diseñarlo. Es un arte realizarlo.
Y también es un arte depurarlo.
El arte detrás del proceso de debugging puede parecer muy sutil, pero ahí está.
Imaginemos que vamos al médico. Nos hemos sentido mal últimamente y queremos curarnos. ¿Qué hace el médico? Analiza síntomas. Revisa un historial médico. Hace preguntas. Solicita análisis. Y con todo recaba información que le permite diagnosticarnos un problema.
O que vamos al mecánico. El carro ha estado haciendo un ruido raro y más vale revisarlo antes de que nos bote cuando menos lo esperamos. ¿Qué haría el mecánico? Primero intenta replicar el problema. Si lo logra, analiza el dichoso ruido. Se pone a observar componentes. Hace pruebas y revisa los resultados de las mismas. Y con esa información, diagnostica un problema.
Pues lo mismo cuando depuramos. El arte detrás del debuggeo es precisamente el que implica ser un buen diagnosta. ¡Como House, pero en sistemas!
Desde que recuerdo, el debugging siempre me ha parecido un juego de detectives. Hay un crimen perpetrado, y el inspector debe analizar las pruebas dejadas por el suceso, para encontrar al culpable. En el caso de sistemas, el chiste es encontrar la causa del problema, y resolverla.
¡Y es un juego realmente divertido!
Como buen inspector, hay que saber en dónde buscar respuestas. En principio, como el mecánico, hay que replicar el error. ¡Pero es que hay muchos errores que eluden todo tipo de detección, no se diga de replicación! Entonces hay que buscar las respuestas en el momento en que ocurren los problemas. Y para eso hay muchos recursos. Por mencionar algunos:
- Podríamos esperar a que el error suceda, el usuario desesperado grite, y nosotros estemos ahí, con la esperanza de entender el problema. Es una técnica válida (en algunos casos), aunque si fuéramos inspectores sería como esperar a que la víctima sea asesinada y nosotros estemos justo ahí, como testigos.
- Podríamos ejecutar el programa, paso a pasito, hasta encontrar la o las líneas emproblemadas. Igualmente es una técnica válida, ¡y muy útil! El problema es que no siempre podemos ejecutar el programa para que falle justo cuando vamos inspeccionándolo. El equivalente sería tener cámaras en todos lados esperando que el crimen ocurriera justo cuando lo estemos monitoreando.
- Y por último, podemos simplemente buscar las pistas que nos deje el programa, para después inspeccionarlas para encontrar el problema. Justo como lo que, comúnmente, deben de hacer los inspectores.
La cosa es que el programa no deja pistas por que sí, en espera de que el inpector (el programador) las encuentre. Es decir, a veces podrá dejarlas, pero entre más pistas nos deje, mejor ¿no? Sería como pedirle a las posibles víctimas que no sólo se dejen asesinar y ya, sino que usen herramientas, como geologalizaciones en vivo, o avisar a dónde van, para que el inspector la tenga fácil.
En nuestro caso tenemos la ventaja de que es el mismo programador (el inspector) el que debe dejar las pistas necesarias, donde crea o considere que es apropiado que el programa grite que hay un problema, una situación anómala o incluso que va ejecutándose normalmente, sin mayor novedad.
Y para esto último precisamente existe el logging. El pariente feo del mismo es usar sentencias print** por todos lados, dejando las pistas necesarias para dilucidar el misterio.
La cosa es que con print, siempre que se ejecute el programa se va a pintar todo lo que programemos. Incluso si se trata de logs que sólo usamos cuando desarrollamos, o cuando estábamos armando ese algoritmo un poco retorcido, además de las situaciones críticas por supuesto.
En realidad los distintos lenguajes cuentan con sus propias herramientas, comúnmente librerías de funcionalidades que incluimos en nuestros programas cuando deseamos hacer logging.
Una buena herramienta para hacer logging nos va a permitir pintar en algún lugar que el programador o administrador de sistemas pueda consultar posteriormente, incluso si el error tiene horas, o días o lo que fuera, de haber sucedido. Incluso si el programa murió irremediablemente por el error. O el sistema junto con él.
Y por supuesto, debe permitir ajustar el grado con el que va a pintar la información, dejando el ruido a un lado:
- Si se trata de información para desarrolladores, normalmente querremos que no se pinte esa información, salvo cuando desarrollamos.
- Si se trata de información rutinaria, que la pinte moderadamente.
- Y si se trata de un error crítico, que grite justo en el momento, lo más fuerte y desesperadamente posible. Como dirían: "when failing, fail early, fail loudly".
Si eres un programador, y no haces logging. ¿Qué esperas? Busca las herramientas adecuadas según tu lenguaje, y tus preferencias, ¡y comienza a hacerlo!
Por cierto, no necesita ser el mismo programador el que realice después la labor de debuggeo. Las pistas que dejemos pueden ser útiles a alguien más. A un sysadmin, a el programador que ahora le da mantenimiento al software que tú creaste hace años (o a ti mismo luego de meses de haberlo olvidado porque ya funciona "perfecto" en producción), o incluso a desarrolladores del otro lado del mundo que usen tu framework luego de que se hizo popular.
* El término 'debuggear' es un anglicismo, que puede traducirse como depurar (un programa) (se entiende, depurar de errores, es decir bugs).
** print es el nombre genérico, en realidad cada lenguaje tendrá sus propios equivalentes.