Agile se trata de saber cortar la torta

Las metodologías ágiles se venden solas. Ya desde el nombre, que comparado con otros, resulta simpático y agradable. Personalmente, «waterfall» siempre me sonó a que vamos a terminar en caída libre, «Modelo en V» suena a corte y confección, y siglas como UP o RUP dicen poco y nada.

Más allá de los nombres, hay que admitir que los principios ágiles son atractivos y tienen sentido. Es comprensible que todo el mundo quiera «ser ágil», que los clientes demanden utilizar estas metodologías, y que cualquier proyecto parezca más manejable utilizando esos principios.

Sin embargo, sucede que la mayoría de las veces, se proclama que se está trabajando con metodologías ágiles, cuando en realidad lo que se está haciendo es un «mini waterfall», dividido en iteraciones y bastante desorganizado. Esto lleva a que las funcionalidades parece que nunca están terminadas, que se entrega al usuario final basura inutilizable y que las integraciones resultan más dolorosas y complejas que mil proyectos waterfall juntos.

Cómo se resuelven la mayoría de estos problemas? Se resuelven cortando bien la torta…

No, no enloquecí. Tampoco voy a pasarte una receta para hacer bizcochuelo. Resulta que la metáfora de la torta es útil para graficar la forma de trabajar correctamente con user stories. Y sucede que las user stories son la clave de cualquier proyecto agile. Podemos estar cumpliendo con todos los principios y prácticas ágiles, y aun así no estar siendo verdaderamente ágiles, porque las user stories nos están llevando a trabajar en cascada.

Las stories son una forma de especificar requerimientos que parece bastante simple. Y la tentación de escribir una story por componente es bastante grande. Sin embargo, es lo mismo que cortar una torta horizontalmente.

Pensemos en una torta. Básicamente, está formada por capas. Y cada capa aporta su sabor para combinarse con las demás. Si cortáramos la torta en forma horizontal, tendríamos que a algunos les tocaría un pedazo de bizcocuelo, a otros el dulce de leche o la crema, y a otros los merengues. Ningún comensal podría probar todos los sabores combinados, y por consiguiente, ninguno podría decir que realmente probó la torta.

Lo mismo sucede con el enfoque de escribir una user story por componente. Puede funcionar todo bien separadamente, pero cuando intentamos integrar todo, encontramos miles de problemas.

Es por eso que se compara a las stories con una porción de torta (bien cortada). Esta porción debe abarcar desde la cobertura, hasta el piso inferior de bizcochuelo, para que se pueda probar («testear») todo junto.

Cada user story debería tomar una porción de la funcionalidad, de punta a punta. Las distintas stories, sumadas, terminan abarcando toda la torta, pero cada una de ellas debe entregar una funcionalidad que puede ser probada independientemente de las demás.

De esta manera, se facilita el trabajo de todo el equipo, y especialmente el de Testing, ya que al poder contar con la funcionalidad de punta a punta, estamos integrando desde la primera iteración, y por ende, minimizando riesgos.

La contra es que requiere de un análisis minucioso. No se trata de sentarse con el cliente y empezar a escribir stories a la vez que el cliente va diciendo lo que quiere. Es una tarea mucho más compleja y que requiere de un armado cuidadoso, ya que se necesita imaginar de punta a punta una funcionalidad que por lo general no existe.

Sin embargo, la tarea vale la pena. Nos va a garantizar que en cada iteración estaremos entregando funcionalidad útil al ciente, y por sobre todas las cosas, que con cada story estaremos probando el sistema de punta a punta, lo que significa detectar los problemas tempranamente y minimizar el riesgo. Por lo tanto, si bien estaremos invirtiendo más tiempo en el armado de las user stories, estaremos ganando tiempo en planificación y ejecución de las pruebas.

Por eso es tan importante cómo se corte la torta. Como testers, deberíamos pedir que nos corten verdaderas porciones, y no un pedacito de bizcochuelo por acá, una cucharada de crema por allá, unas nueces por el otro lado… Si no probamos todos los sabores combinados, no podemos decir si la torta estaba rica o no.

 

Agilidad vs Calidad

Marcos es responsable de proyecto de una consultora muy exitosa. Cuando leyó por primera vez sobre metodologías ágiles, le pareció estar soñando: había encontrado la forma de librarse de todo lo que hacía engorrosos sus desarrollos de software: la documentación, los procesos, la “gente de QA”…

Apoyándose en la moda que comenzaba a imponerse, no le costó mucho convencer a su nuevo cliente, ni tampoco a sus jefes, de las ventajas de utilizar una metodología ágil: entregas frecuentes, equipo reducido y un rol preponderante del cliente.

Cuando le preguntaron sobre el equipo, respondió que no necesitaría Testers, con la misma sonrisa de triunfo con la que mandó a la papelera de reciclaje los templates de documentos funcionales que había utilizado en los anteriores proyectos.

La primera etapa del proyecto pareció muy exitosa: Marcos se sentaba con el cliente a escribir las user stories, de ahí derivaba los UAT que servían como base para el desarrollo. A cada desarrollador le exigió que automatizara una batería de pruebas unitarias y después, uno de ellos, automatizó una regresión completa basándose en los UAT. Las primeras entregas no sólo llegaron a tiempo, sino que además, el cliente declaró al verlo que el software era “justo lo que necesitaba”.

Claro que hasta ese momento, nadie lo había utilizado. Tuvieron que pasar varias entregas para que el software llegara a algunos de los usuarios finales, y comenzaran a utilizarlo intensivamente. Ahí surgieron los problemas.

¿Qué pasó?

El software satisfacía al cliente (que no era el usuario final, sino un gerente de área; el producto era utilizado por la gente que estaba dos niveles más abajo), cumplía con lo que se le había requerido, pero en cuanto se lo sometía al uso cotidiano (lo que implica concurrencia de usuarios, alta carga de uso, errores de operación, etc.), las fallas eran tremendas, en cantidad y en gravedad.

Ante las quejas de su cliente, Marcos se movió rápido. Le aseguró que era conciente de los problemas que tenía el producto, y que por eso ya estaba contratando a un especialista en calidad, que ayudaría a resolver los numerosos defectos del software. Comprendió que había zafado, había salvado el proyecto, y que debía resignarse a volver a caer en manos de un “árbitro de la calidad”…, ya se lo imaginaba: retrasaría las entregas, pondría el grito en el cielo por la falta de documentación, perseguiría a todos exigiendo el agregado de un acento o el cambio de color de un botón…

En fin, se dijo Marcos, yo intenté ser ágil, pero si el cliente lo que quiere es que el proyecto se demore hasta el infinito, allá él.

Se sorprendió un poco cuando su jefe le presentó a Julio, un Tester con experiencia en proyectos ágiles. A Marcos le pareció un poco contradictorio: ¿no era que las metodologías ágiles prescindían de Analistas Funcionales y Testers?

Julio fue rápido para el diagnóstico del problema. Las cosas no se habían hecho del todo mal, simplemente, se había dejado de lado una parte importante. Se habían detallado bien los user stories, se habían automatizado bien las pruebas unitarias y las regresiones, pero el problema era que con eso no alcanza: eran necesarios más niveles de prueba, para exponer al producto a mayor exigencia.

Julio explicó que con la automatización de pruebas que el equipo de desarrollo había armado, se habían constituido en responsables de la calidad del producto, lo cual era más que acertado, ya que para las metodologías ágiles, la calidad es responsabilidad de todos los miembros del equipo.

El nuevo integrante del equipo ayudó a diseñar otros niveles de pruebas, que sacaron a la luz numerosos defectos, que el equipo de desarrollo fue corrigiendo. Así, cada iteración se componía de una parte de agregado de funcionalidad, y una parte importante de corrección de defectos. Cuatro o cinco versiones más adelante, el producto mostró una solidez mucho mayor, resistiendo el uso intensivo de los usuarios finales, y cambiando su imagen ante los ojos del cliente.

Una de las cosas que más sorprendió a Marcos fue que Julio ejecutaba pruebas todo el tiempo. Nunca se quedaba esperando a que le entregaran una funcionalidad completa, sino que estaba constantemente probando. De esa manera, logró detectar defectos muy tempranamente.

El producto siguió su evolución y el proyecto terminó siendo un rotundo éxito. El cliente obtuvo el producto que necesitaba, en tiempo y forma, con una alta percepción de calidad. Marcos estaba gratamente sorprendido y feliz con esta nueva experiencia. Pese a haberse equivocado en un comienzo, había tenido la flexibilidad para cambiar su enfoque y aprender de los errores cometidos. Y sobre todo, había cambiado radicalmente su forma de ver a los testers: no concebía un equipo sin que alguno de sus integrantes fuera un especialista en calidad.

Los datos de prueba

Sorprendentemente, todavía escuchamos a responsables de proyectos que creen que armar los datos para una prueba consiste simplemente en “traerse un set de datos de producción”, o en todo caso, que el tester, al momento de ejecutar los casos, complete los formularios con lo primero que se le venga a la cabeza.

Parecen no comprender que es imposible realizar una buena prueba si el tester no controla los datos que utilizará en ella. Controlar los datos significa pensar de antemano con qué datos se ejecutará cada caso, escribirlos todos, incorporarlos al sistema en el momento oportuno y de la manera más eficiente, y tener conciencia en todo momento de qué datos tenemos, qué modificaciones deberían sufrir, y en qué estado deberían encontrarse al finalizar la ejecución de las pruebas.

Es cierto que en algunos casos necesitaremos contar con datos de producción, ya sea porque necesitamos fidelidad con las condiciones reales de operación del sistema, o porque es demasiado complejo generar los datos desde cero. Sin embargo, en la mayoría de los casos, los datos de producción son insuficientes, sobre todo para poner a prueba el sistema en condiciones diferentes del “camino correcto”, cuando necesitamos ver cómo reacciona ante una inconsistencia, ante errores de operación o ante ataques externos.

En este tipo de situaciones, necesitamos contar con datos especialmente diseñados, que nos garanticen poder crear las condiciones que requiere el caso de prueba. Estos datos deben ser generados por el tester, y controlados en todo momento. Hay que tener en cuenta que los datos pueden mejorar o empeorar un caso de prueba (1). Es decir, que el mismo caso de prueba, ejecutado con diferentes datos, puede arrojar resultados diferentes, y por consiguiente, un buen caso de prueba, ejecutado con los datos incorrectos, puede arruinarse y no cumplir su cometido.

Cuando hablamos de controlar los datos, también nos referimos a que el tester debe llevar un registro de qué datos utilizó en la ejecución de cada caso de prueba. De esta manera, ante la aparición de un defecto, podrá exhibir los datos con los que el defecto se detectó, a los efectos de facilitar su reproducción posterior. No se debe olvidar que muchos defectos sólo aparecen con determinados datos, o combinaciones de datos; si los datos se cambian, desaparece el defecto. Así, evitaremos que muchos defectos vuelvan de Desarrollo con el antipático comentario de “no se pudo reproducir”.

En suma, creemos que deberia dedicarse el mismo esfuerzo a diseñar los datos de prueba, que el que se dedica a diseñar los casos: los datos deben ser pensados y diseñados especialmente para cada caso de prueba, a continuación ser generados cuidadosamente, e incorporados al sistema en el momento oportuno, monitoréandolos en todo momento para verificar su estado. Una prueba donde el tester no controle los datos será una prueba insuficiente e incompleta.

(1) decimos que un caso de prueba es mejor cuantas más posibilidades tiene de sacar a la luz un defecto.