Hace ya varios años que tomé la decisión de descartar cualquier temática no técnica para los blogs y dejar a un lado las ideas o pensamientos que pudiera tener, inclusive aquellos relacionados con las propias materias técnicas. Sin embargo, hay ocasiones que a veces la parte técnica no es mostrar qué fragmento de código, qué framework o qué tecnología he usado para resolver un problema. Una idea, un matiz o una sugerencia pueden dar con la pista y abrir un nuevo mundo de posibilidades.
Ayer en La 2 (sí, veo La 2, ¿qué pasa?) emitieron un documental sobre la construcción del Rover Curiosity que desde el punto de vista ingenieril es muy pero que muy interesante:
Considero que la formación recibida como ingeniero informático ha sido satisfactoria; aunque como todo en la vida, puede ser mejorable en algún campo más que en otro. Recuerdo todavía casi como si fuera ayer, las clases en las que nos enseñaban metodologías de gestión de proyecto y un poquito de tareas más o menos relacionadas pero sin entrar en profundizar (algo de pruebas, algo de gestión de personal, algo de estimación, algo de arquitectura, etc) lo cual me debería llevar a tener una cierta visión del Mundo Real para poder sobrevivir en él.
Sin embargo, tengo muchísimas lagunas, de las que poco a poco (bien aprendiendo despacito o generalmente mediante palos) voy consiguiendo rellenar. Hoy voy a hablar de pruebas. En el documental podrás ver algunas de las pruebas a las que someten al Rover Curiosity y a algunos de sus elementos. En la carrera me enseñaron poco de pruebas y es algo que debería ser desde mi punto de vista algo fundamental en cualquier ingeniería.
P & P: Hacer Pruebas es de Pobres
Probar debería ser lo más normal del mundo. Metodologías como TDD y BDD se centran precisamente en probar lo que hace desde distintos puntos de vista. ¿Quién no ha oído hablar de TDD? ¿Quién no ha visto una charla/evento sobre TDD? ¿Quién aplica TDD? Ah amigo…
La ingeniería del software es una (¿ingeniería?) muy reciente. Los fundamentos matemáticos de la computación moderna (sin desmerecer a pioneros como Ada, Pascal, Baggage e incluso el misterioso mecanismo de Anticitera) nace a partir de 1920-30, de la mano de científicos como Von Neumann o Turing. A partir de ahí, el ritmo de desarrollo de la ciencia de la computación va cobrando un camino vertiginoso hasta nuestros días en los cuales la tecnología avanza a un ritmo que difícilmente podemos seguir. Nos hemos acostumbrado a servicios como Facebook, Youtube, Gmail/Hotmail, la nube… todo el mundo los conoce y parece que siempre han estado ahí. Parece, porque realmente alguno ni siquiera tiene 10 años.
Este rápido avance provoca que las disciplinas tengan que adaptarse casi a paso cambiado. Otras ingenierías como la civil, industrial o química llevan décadas, siglos e incluso milenios de desarrollo con lo cual sus procesos están asentados, depurados y aceptados. Nosotros todavía estamos discutiendo qué metodología es mejor o más adecuada porque, sencillamente es un mundo cambiante al cual tenemos que adaptarnos y proponer constantemente alternativas y soluciones para poder sobrevivir.
Todo lo que usamos a diario ha sido sometido a multitud de pruebas. Desde nuestra casa, el coche (con sus famosas pruebas de airbag), carreteras, puentes… Hasta el teclado sobre el que escribo este texto ha sido sometido a durísimas pruebas para estimar cuánto tiempo debe durar y proporcionar un funcionamiento correcto bajo una serie de condiciones.
¿Ocurre lo mismo con el software? Desgraciadamente, no. Las excusas son variopintas y todos, YO el primero, las hemos pronunciado alguna vez:
-
No tengo tiempo para hacer pruebas.
-
Hacer pruebas no sirve para nada.
-
No sé cómo probar esto.
-
Paso de hacer pruebas; esto funciona de lujo.
-
Hay pruebas, pero no funcionan.
-
etc…
Justificadas o injustificadas, son excusas. Y son excusas porque hay solución a cada una de ellas:
-
“No hay tiempo para hacer pruebas” : El 95% de los clientes van a preferir una entrega que les funcione en un alto porcentaje de la funcionalidad contratada antes que una bomba nuclear inestable en sus manos.
-
“No sirve para nada” : Respirar tampoco, fíjate.
-
“No sé como probar esto”: A esta la respondo más abajo.
-
“Paso de hacer pruebas; esto funciona de lujo” : Explotará. Y lo sabes. ¿El motivo? Salvo sorpresa, somos humanos, no máquinas autómatas y cometemos errores. Es normal, cotidiano y predecible. Pero podemos intentar que sea menos cotidiano cada vez.
-
“Hay pruebas, pero no funcionan” : Simple. Si no sirven, se quitan. Y si no, se adaptan. Respondo más abajo también.
Podemos agrupar las excusas en tres tipos:
-
Timing: No se puede dedicar tiempo pese al riesgo que conlleva. Y si conocemos y aceptamos los riesgos, adelante.
-
Pereza: Poco podemos hacer aquí…
-
Conocimientos: Esta es la buena y la más importante.
TDD es una metodología con una curva de aprendizaje inicial muy dura, porque exige un cambio de paradigma mental a algo que no estamos acostumbrados. Normalmente estamos (mal) acostumbrados a programar, arrancar y probar. Si falla, vemos qué falla y vamos a corregirlo hasta que quede listo. Sin embargo no hace falta aplicar TDD/BDD/{ponga usted su favorita} para hacer pruebas. ¿Cuánto tiempo se pierde en parar, cambiar y arrancar? ¿Cuántos pasos hay que dar para volver a ejecutar la prueba? ¡Automaticemos todo esto en la medida de posible!
Probar antes ejecutar la aplicación te puede ayudar con las siguientes capacidades:
-
Adquirir un mayor conocimiento de lo que estás haciendo.
-
Te permite centrarte en una sola cosa y descartar el resto de tareas relacionadas.
-
Te proporciona una visión muy clara de la funcionalidad.
-
Averiguar, probar y acotar comportamientos que no estaban contemplados en un principio.
-
Mejorar el código descubriendo las dependencias, acoplamientos, etc que estaban ocultos.
-
Descubrir funciones o métodos que deberían/no deberían existir
-
etc…
Precisamente una de las bazas de hacer pruebas es el refactor una vez se ha probado para que cada vez el código sea más claro, limpio y conciso respetando la funcionalidad. Y bueno, eso es simplemente (simplificando mucho, claro) teniendo una prueba que represente la validación de un requisito. Podemos cambiar el código, o lo que haga pero sabemos que el test va a recibir una entrada X y devolverá una salida Y. Si eso no ocurre, es que algo hemos roto. Se trata de seguridad. De tener la seguridad empírica, con el paso del tiempo, que el desarrollo y su avance no rompe nada de lo que funcionaba anteriormente.
Be water, my friend
Nadie nace sabiendo y parece que lo olvidamos demasiado a menudo. Reconocer la ignorancia de algo es la primera piedra de conocimiento de ese algo, porque es el momento en que nos planteamos en el subconsciente una necesidad de aprendizaje que anteriormente no se nos presentó.
Probar es algo que supone esfuerzo, especialmente al principio. Y al final también. Las primeras pruebas son feas, mal hechas, desorganizadas, acopladas… y poco a poco, a medida que se van haciendo se mejora. Porque el único método de aprender a hacer pruebas es haciéndolas. Podrás leer muchos libros, consultar o tener mucha referencia a recursos, pero hasta que no te sientas frente al lienzo en blanco no aprenderás a hacer pruebas. Y con la experiencia y la multitud de recursos disponibles (libros, github, blogs) se irá refinando el proceso cada vez.
En un campo en que la tecnología cambia cada 6-18 meses y los requisitos también, no nos queda otra que adaptarnos y cambiar continuamente. Si cambian los requisitos, cambian los tests que deben validarlos. Es una exigencia continua de un flujo continuo…
Para la mayoría de proyectos en los que he trabajado en mi vida profesional, me tomo los despliegues y entregas con demasiado estrés. Intento que todo lo que pueda hacer en función de mis conocimientos esté bien: build etiquetada, todos los tests de todos los tipos pasando, despliegue controlado por si hay que hacer rollback en cualquier momento, bakcups de todo… Son momentos críticos para cualquier sistema porque implica que durante ese tiempo, lo que sea que se esté desplegando no está disponible y hay que asegurar que el despliegue no rompa nada de la versión antigua. Porque en última instancia, podemos hacer rollback y estaremos en una situación de estrés por solventar la incidencia en el menor tiempo posible… pero al menos efectivamente, se puede solucionar.
A veces pienso en la cantidad de pruebas que hacen los ingenieros de NASA, ESA, EADS como el del Rover Curiosity y la presión que deben sentir (proyectos de 2.000 millones de € que dependen de un cable de 8€, por ejemplo) para que todas las pruebas y toda la casuística posible quede demostrada y ejecutada, intentando no dejar nada al azar. Porque una vez el robot ha sido lanzado y está en Marte, no hay vuelta atrás ni solución si ocurre algún problema*. Y eso, como ingeniero no hace más que producirme la más absoluta admiración.
Hay casos en los que por mucho que se sigan las mejores prácticas, por mucho que queramos habrá una parte en la que no tenemos control sobre lo que ocurre. Son cosas inevitables. Pero todo aquello que podamos hacer a nivel de pruebas para garantizar la estabilidad, seguridad y calidad de nuestro trabajo, hagámoslo. Afortunadamente disponemos de la tecnología para que en la mayoría de casos podamos replicar los entornos de producción/despliegue de forma bastante fidedigna, lo que permitiría reducir el riesgo de un despliegue. Este tipo de capacidades no las tiene la industria aeroespacial ni muchas otras, por lo que deberíamos usar las herramientas disponibles para minimizar riesgos y asegurarnos los menores problemas posibles. Porque, a fin de cuentas es una inversión a largo plazo que siempre sale muy, pero que muy rentable.
* Algunos gazapos de la industria aeroespacial derivados de NO hacer las pruebas suficientes:
-
Ariane 5: No se probó el sistema de cálculo de la aceleración hasta el día del lanzamiento. Este sistema venía derivado del Ariane 4 y funcionaba perfectamente, pero no se probó sobre un Ariane 5. Resultado: el piloto automático no interpretó los datos correctamente ya que se produjo una excepción de punto flotante, obligando a los ingenieros de control a autodestruir el cohete ya que el desvío de trayectoria podría haber provocado una auténtica catástrofe.
-
Mars Climate Orbiter: Parte del software de tierra hablaba en millas mientras que otra parte en metros. Resultado: se enviaron datos incorrectos a la nave, no ajustó el ángulo y distancia correcta para situarse en la órbita de Marte. Quedó carbonizada al tomar contacto con la atmósfera marciana.