He tenido la suerte de poder participar en el code retreat que se celebro el Sábado en Donosti. ¿Que es un code retreat? Pues es una reunión de programadores que ejercitan sus conocimientos y aprenden y mejoran con ese ejercicio y con su puesta en común entre todos. Una buena forma de aprendizaje a través de la practica y la colaboración. Pero no solo esto, otro de los objetivos es replantearse los propios conocimientos y practicas y para ello la dinámica estaba orientada:
- Los ejercicios se llevan a cabo en parejas, para potenciar la programación colaborativa.
- El problema a resolver o representar en código es el mismo para todos y cada intento de hacerlo duraba estrictamente 45 minutos (osease un pomodoro). En nuestro caso el problema a resolver fue el juego de la vida de Conway. Aunque el objetivo real no era resolver el problema por supuesto.
- Después de cada sesión había que borrar todo el código escrito antes de compartir entre todos la experiencia.
- Como principio general se estableció el desarrollo guiado por las pruebas.
- Aparte se pusieron restricciones sorpresa para evitar entrar de ninguna manera en una rutina.
Todo ello para conseguir una nueva visión de la programación y para romper los esquemas, el bagaje que muchas veces te hace ser mas rígido y te impide aprender y avanzar. Y para conseguirlo de forma radical es necesario medidas radicales.
Por supuesto contar con un maestro de ceremonias adecuado es casi imprescindible y Enrique lo hizo a la perfección compartiendo sus valiosos conocimientos de forma totalmente desinteresada. El acto no pudo haber existido sin el tiempo y el esfuerzo de los organizadores y de los patrocinadores. Desde aquí mi reconocimiento a su generosidad. Finalmente pero no menos importante también hay que reconocer el papel de todos los asistentes al acto ya que entre todos ayudamos un poco a que fuera un éxito.
Nunca había practicado ninguna de las técnicas que he descrito y solo tenia cierto conocimiento teórico de lo que había podido leer en blogs y artículos. Un conocimiento totalmente inútil y que intente olvidar para limpiar la mente de prejuicios.El objetivo claro fue hacernos mejores programadores o sea que nuestro código fuera mas elegante, mas simple, mas fiable y mantenible. La prueba desde ese punto de vista pierde su valor como elemento externo al programa, muchas veces escrito a posteriori y se convierte en el medio para alejarnos del código real y mirarlo desde fuera incluso cuando aun esta en nuestra mente luchando por salir y apoderarse de nuestros dedos. La prueba nos aleja de el para mirarlo con ojos nuevos y sospechosos.
Sí, nuestro código siempre es sospechoso.
A la vez la prueba nos hace seguir pegados al objetivo y al propósito del código todavía no escrito y no nos separa totalmente de lo que es la programación ya que es en si misma código. Esa es su ventaja y su inconveniente ya que hay otras maneras de alejarnos del código: haciendo dibujos o simplemente no haciendo otra cosa que pensar en el. La desventaja de la prueba es que sigue siendo código en el que podemos regodearnos, dándole mas importancia de la debida y que puede acabar siendo tan imperfecto o mas que el código real que todavía no hemos escrito (y que puede acabar siendo empobrecido por la prueba y no al revés).
Sí, se corre el riesgo de escribir dos veces un código mal hecho. Nadie dijo que la tdd fuera mágica.
Aprendizajes y collejas varias
Para evitarlo Enrique nos daba collejas virtuales, tanto en mitad de los ejercicios como en las retrospectivas posteriores para sacarnos de nuestros raíles, viejos o los que íbamos creando en el momento. Borrar el código antes de hablar de el, tener que consensuar los nombres y el siguiente paso a seguir, cortar el ejercicio justo cuando la cosa empezaba a calentarse. Todo orientado a alejarnos del código y volverlo a mirar con otros ojos.
La prueba bien hecha no se deja arrastrar por el código todavía no escrito que pulula en nuestra cabeza, lo domina y lo encierra y le obliga a seguirla:
- Le debe obligar a ser mas simple: para ello Enrique nos obligo a escribir todo el código necesario dentro de la prueba y solo darle entidad propia en cuanto detectaramos repeticiones reales en las pruebas. En otro momento nos obligo a que las funciones o métodos no fueran de mas de cuatro lineas.
- Debe usar los nombres mas "adecuados" (totalmente autodescriptivos,no ambiguos, lo mas cortos posible) a la lógica de negocio y al problema que va a resolver. En cada retrospectiva Enrique incidía y cuestionaba los bautizos que hacíamos.
- Le debe dar justo la responsabilidad que necesita para hacer bien su trabajo sin dependencias innecesarias y lo mas alejadas de la implementaciones concretas. El mismo hecho de hacer la prueba te conduce a ello ya que probar un fragmento de código es mas difícil y costoso cuanto mas dependencias externas tienes que manejar en la prueba (uso de mocks e inicializaciones costosas). Enrique nos prevenía de la implementación concreta que luchaba por salir de nuestros dedos y que amenazaba con encerrar y anclar nuestro diseño antes de tiempo.
- La prueba debe contagiar al código de su carácter declarativo, para lo que obviamente debe ser ella misma lo mas declarativa posible. Enrique nos prohibió usar números en las pruebas para intentar hacernos ver que la mejor forma de que la prueba nos dijera lo que quería es darle un nombre que lo expresara de forma totalmente descriptiva. No lo consiguió y tuvo que borrar nuestro código (otra colleja) y señalarnos con el dedo (y aun asi nos costo asumirlo). Cuando vimos: public class TestCelulaCuandoEstaViva { public void muereCuandoTieneMenosDeDosVecinos () {assert(true)}} el código lleno de ifs, operadores de comparación y números que habíamos guardado celosamente en nuestras cabezas y que guiaba secretamente nuestras pruebas murió un poco mas. Tell, dont ask, my friend.
- Debemos poder hacer las pruebas tanto desde arriba hacia abajo como al revés y elegir el camino adecuado en cada momento (o combinarlos, primero hacia abajo y luego de vuelta como nos mostró Enrique). La elección debe ser cuidadosa y debe tener en cuenta el dominio, las herramientas, el lenguaje con el contamos, etc.
Un tesoro nos llevamos de Donosti, seguro que hubo mas enseñanzas que se me han olvidado o que ni siquiera capte pero estoy contento. Pero, espera un momento, yo ya conocía todos esos preciosos principios y buenas practicas en teoría e incluso intento con mayor o menor éxito aplicarlos en mi código. Geniales programadores los llevan a la practica todos los días sin hacer un test. Se alejaran del código y como paso previo pensaran en el teniendo en cuenta todas estos principios y algunos mas. Incluso estoy seguro que el primer código que piense gente como Enrique ya sera cristalino (al menos comparado con el mio).
Como otra opción para los programadores de a pie queda el hacer prototipos del código y repensarlo y reescribirlo las veces que haga falta. Equivocarse una y otra vez y aprender de los errores.
Entonces, ¿para que las pruebas?
Buena pregunta ya que, en mi opinión, no hay que aceptar las metodologías sin mas ya que también son sospechosas (igual que lo es el código). Igual me perdí algo en el code retreat que eliminara todo mi escepticismo. Dejare a mis comentaristas la oportunidad de contestar aunque puedo adelantar que en primer lugar aparece el valor original de la prueba que hasta ahora habíamos obviado: monitorizar el código de forma automática y aumentar nuestra confianza en el, describir y documentar los casos de uso y la lógica de negocio. El hacer la prueba primero tiene la consecuencia obvia de que seguro que la haces. Por otro lado te obliga de forma metódica a pensar en el código antes de escribirlo (cosa que no suele hacerse siempre a no ser que tengas una gran disciplina) Seguro que se os ocurren mas ventajas.
Clojure y el TDD
Por supuesto fui con mis paréntesis a la sesión con un poco de miedo que provocaran urticaria y dispuesto a amoldarme a cualquier lenguaje sobre todo desconocido para mi como oportunidad para aprender. Debí haber previsto que todos venían dispuestos a aprender sin prejuicios y no les importo o incluso estuvieron encantados (¡cuando @jmbeas empezó a recordar sus tiempos de emacs y common lisp tuve que seguirle yo a el!) de usar clojure para los ejercicios, @kinisoftware también acepto sin problema hacerlo en clojure a la vez que me guiaba por mis primeros balbuceos en los tests. Eso tal vez sesgo un poco el objetivo principal pero creo que pude mostrar unas pinceladas del lenguaje sin olvidarnos del todo de el.
Hay gente que cuestiona la pertinencia de la TDD en un lenguaje funcional como Clojure, pero dentro de la comunidad también hay opiniones contrarias y al menos el testing tiene su lugar ya en el mismo núcleo del lenguaje. Aparte hay varias librerías magnificas para ponerlo en practica. Personalmente creo que aunque no tiene el mismo significado o importancia en un lenguaje orientado a objetos y dinámico (en el que casi es una locura programar sin hacer tdd) que en uno funcional y que promociona la inmutabilidad y evita el estado, si que cumple su papel. No hay mejor referencia que los posts que ha escrito @unclebob sobre clojure y tdd o los de @marick.
Fue curioso como la orientación de objetos fue, en mi opinión, mas un estorbo que una ayuda para las sesiones. Los objetos (todos con mayúsculas Célula, Tablero, Juego,Dios) que modelaban el problema, sus relaciones y sus responsabilidades rondaban la cabeza de los asistentes y creo que tiraban de los tests anclandolos y no dejandolos fluir. En mi caso era las estructura de datos simples la que me rondaban y las funciones para transformarlas (vectores, mapas) pero creo que esa diferencia iba a mi favor pues con elementos tan simples y genéricos (que hay mas abstracto,generico, componible y simple que una funcion o un mapa) no necesitas crear abstracciones a posteriori ni mocks para aislar los tests del diseño e implementación concreto. ¡¡¡Corregirme si me equivoco!!!
Fue curioso como la orientación de objetos fue, en mi opinión, mas un estorbo que una ayuda para las sesiones. Los objetos (todos con mayúsculas Célula, Tablero, Juego,Dios) que modelaban el problema, sus relaciones y sus responsabilidades rondaban la cabeza de los asistentes y creo que tiraban de los tests anclandolos y no dejandolos fluir. En mi caso era las estructura de datos simples la que me rondaban y las funciones para transformarlas (vectores, mapas) pero creo que esa diferencia iba a mi favor pues con elementos tan simples y genéricos (que hay mas abstracto,generico, componible y simple que una funcion o un mapa) no necesitas crear abstracciones a posteriori ni mocks para aislar los tests del diseño e implementación concreto. ¡¡¡Corregirme si me equivoco!!!