Los alumnos sobre Mob-Programming y TDD

En MeMo2 @ingenieriauba hacemos un primer TP grupal en el que los alumnos deben evolucionar una webapp existente siguiendo un proceso XP-like (iteraciones semanales, planning, review, retro, CI/CD, DDD, BDD/TDD, etc).

Entre las cosas que más énfasis hacemos y que más les cuesta (o que más resistencia le ponen) los alumnos está el desarrollar guiados por pruebas (bdd/tdd) y trabajar todo el tiempo haciendo Mob-Programming (o al menos pair-programming). También les pedimos que hagan Trunk-Based development.

TDD es algo que ya han visto en alguna materia anterior pero de una forma mucho menos profunda que lo que proponemos en MeMo2. En general los alumnos tiene una buena recepción porque nuestra propuesta de BDD+TDD ofrece una guía muy detallada de como transitar el proceso de construcción desde la intención del usuario hasta el código que implementa esa intención en un ambiente donde el usuario puede utilizar la pieza de software construida.

La propuesta de Mob/Pair-Programming les resulta más “rara”, sobre todo a los que ya vienen con experiencia laboral. Posiblemente varios de los que ya trabajan en desarrollo no tendrían el visto bueno de sus jefes si pretendieran trabajar todo el tiempo (o la mayor parte) haciendo Mob/Pair Programming. Por esto es que insistimos tanto en que hagan Mob-Programming, si no lo prueban en nuestra materia, es posible que tampoco lo puedan probar en sus trabajos.

Algo similar ocurre con el uso de Trunk-Based development. Muchos de los que ya trabajan suelen utilizar feature-branches (como creo que hace gran parte de los desarrolladores actualmente) y entonces “temen” trabajar todos simultáneamente sobre el mismo branch. La clave aquí de nuestra propuesta es que al trabajar haciendo mob-programming solo hay un único branch de desarrollo activo, con lo cual resulta natural hacer trunk-based development. Al mismo tiempo y más allá de hacer mob-programming, nosotros insistimos en trabajar en pequeños incrementos haciendo commits al trunk/master cada 20 o 30 minutos como máximo, con lo cual las chances y el tamaño de divergencia al trabajar se reduce mucho, llevando casi a cero el tiempo requerido en arreglar conflictos de merge.

El jueves pasado al finalizar la segunda iteración del trabajo grupal hicimos una actividad con los alumnos para relevar cuanto habían usando las técnicas recomendadas y cuán útiles les habían resultado.

Los siguientes gráficos muestran el resultado de la actividad.

Mi sensación es que la mayoría de los alumnos “compra” nuestra propuesta. Veremos que opinan al finalizar el siguiente TP que reviste una complejidad mucho mayor y donde creo que estas técnicas suman aún más valor.

Taller: Tddeando microservicios a kubenertes

Ayer completé el dictado de la primera edición de mi taller “TDD your Microservice from Git to Kubernetes“. El título está en inglés porque efectivamente fue en inglés. Asimismo, si bien en el título dice TDD, el taller es mucho más amplio. Incluye también varias prácticas relacionadas como Configuration Management, Continuous Integration, Design Patterns, Architecture guidance, etc.

Hace un tiempo decidí no dictar más cursos de TDD porque la experiencia me demostró que luego del curso, el salto que debían hacer los participantes entre la “simpleza didáctica” de los ejercicios del curso y su código de trabajo cotidiano era muy difícil de realizar. Luego de analizar el origen de esas dificultades decidí armar este curso que apunta precisamente a trabajar con ejercicios más “de mundo real”, lo cual implica lidiar con un proceso de desarrollo en equipo, versionado, integración con otros sistemas e infraestructura, entre otras cuestiones.

Obviamente cubrir todas estas cuestiones en un solo taller resulta muy desafiante, es por ello que el taller está estructurado en varios encuentros. Al mismo tiempo es un taller “avanzado”, en el sentido de que requiere que los participantes tengan:

  • experiencia en desarrollo de software en un contexto de industria
  • conocimiento de TDD (al menos conceptualmente)
  • práctica en la automatización de pruebas (al menos de tipo unitario)

Al mismo tiempo, para achicar más el salto entre el taller y los proyectos diarios de los participantes, este taller está basado en tecnologías concretas incorporando también patrones de uso común en dichas tecnologías. Esta versión en particular la hice utilizando C# (netCore),NUnit, Moq, Gitlab y Kubernetes.

En los próximos meses estaré haciendo una edición en castellano. Los interesandos pueden contactarme aquí.

Estilos de TDD: un voto para London

Hace un tiempo escribí sobre los estilos de TDD e intenté hacerlo de forma objetiva. Ahora pretendo continuar con la cuestión pero dando mi opinión.

No creo que haya un estilo que sea universalmente mejor que otro (no silver bullet). Creo que un determinado contexto puede resultar más conveniente utilizar uno u otro estilo. Personalmente me ocurre que en los contextos en los que suelo trabajar encuentro más conveniente una utilizar una estrategia del estilo London, o para ser más preciso: la propuesta de Freeman & Pryce. Para que se entienda mi posición explico un poco las generalidades de los contextos en los que suelo trabajar.

En primer lugar suelo trabajar muy cerca del usuario y con una estrategia de entrega continua. Al mismo tiempo, en los equipos en los que trabajo suelo ocupar un rol tipo “XP Coach” con el foco en lograr que el equipo pueda entregar software de valor, de calidad, de manera sostenible y predecible. Generalmente los equipos con los que trabajo no tienen experiencia en TDD. Típicamente trabajo en aplicaciones comúnmente denominadas como de “tipo enterprise / sistemas de información” . Generalmente me ocurre que los principales desafíos que el equipo enfrenta no vienen dados por la lógica de negocio ni el modelado del dominio sino por cuestiones “accidentales” como procesos manuales, desconocimiento/(des)control de la infraestructura, trabajo desordenado, burocracia, falta de comunicación, etc.

Es en estos contextos donde encuentro especialmente útil la propuesta de Freeman & Pryce, concretamente cuando proponen:

  • Comenzar con un walking skeleton que nos permita establecer las bases de la arquitectura de la aplicación de punta a punta.
  • Que ese walking skeleton este cubierto por una prueba de aceptación end-to-end
  • Incluir como parte de ese walking skeleton el proceso de versionado, build, test y deploy a un ambiente simil producción en un esquema de integración continua

Esto en un punto excede el estilo de TDD y por ello es me parece más preciso hablar de la propuesta de Freeman & Pryce que del estilo London cuando me refiero a estas cuestiones.

Ahora bien, una vez completo el walking skeleton entonces sí podemos hablar del estilo de TDD. Ahí la propuesta de Freeman & Pryce es comenzar “desde afuera” trabajando en el doble ciclo TDD (estilo London). Esto requiere del uso de test-doubles, posiblemente el punto más cuestionado de este enfoque. Las críticas a esta cuestión se deben principalmente al hecho de que puede resultar costoso el mantenimiento de los test-doubles a medida que la aplicación (y el diseño) van evolucionando. Coincido en que esta cuestión es un riesgo, pero en mi caso lo suelo mitigar tratando al código de tests con el mismo cuidado con el que trato al código de la aplicación y utilizando todo un conjunto de técnicas para asegurar su mantenibilidad. Muchas de estas técnicas están descriptas en el mismo libro de Freeman y Pryce, pero también hay algunas otras que he encontrando muy útiles en los libros de Gojko Adzic (Fifty Quick Ideas To Improve Your Tests, Specificacion by Example) y Gerard Meszaros (xUnit Test Patterns: Refactoring Test Code).

Una cuestión que quiero destacar de este enfoque es que me resulta muy conveniente la idea de ir diseñando/desarrollando la aplicación desde afuera porque eso nos pone en una posición de “cliente/usuario” de nuestro propio código, evitando en cierto modo la creación de métodos/comportamientos/atributos/artefactos que “el cliente” no requiera, entiendo aquí como cliente a “la capa/el objeto” que consume nuestro código y que indirectamente termina expresando la necesidad del cliente/usuario persona.

Efectos colaterales de TDD

El uso de TDD (desarrollo guiado por la pruebas) tiene varios efectos colaterales, a mi criterio, la mayoría de ellos positivos. Algunos de ellos son evidentes y ampliamente conocidos, pero algunos otros suelen pasar desapercibidos.

Uno de estos efectos colaterales las pruebas y el código de prueba pasan a ser ciudadanos de primera categoría. Esto es así porque el desarrollo arranca con las pruebas y eso hace que el código de las pruebas sea una herramienta central para pensar nuestro diseño. Antes de escribir una funcionalidad la vamos a estar describiendo en una prueba, eso concretamente implica que en la prueba escribiremos invocaciones a métodos y objetos que aún no hemos creado. Esto no ocurre cuando escribimos las pruebas a posteriori. El hecho de que las pruebas sean un ciudadano de primera categoría implica que el código de prueba debe ser tratado con el mismo cuidado que el código de producción, esto es: debe ser mantenible, cumplir las convenciones y sobre todo ser claro y evitar duplicaciones. Ojo, no es que al escribir las pruebas a posterior no debamos cumplir con esto, pero al hacer TDD estas propiedades del código de prueba toman mayor relevancia porque el código tiene un protagonismo distinto.

Al mismo tiempo, como no escribiremos código de producto sin tener primero una prueba, es común que la cantidad de pruebas sea mayor que si escribimos pruebas a posteriori (no tengo evidencias formales de esto, es más bien una sensación basada en lo que visto tanto con mis alumnos como con mis clientes). Cuantas más pruebas tenemos, más importante es la claridad y mantenibilidad del código de prueba. Curiosamente y un poco a contramano de esto, suele ocurrir que al agregar nuevos casos de prueba sobre una funcionalidad existente, muchas veces comenzamos “copy&pasteando” el código de prueba del caso anterior. Esto en principio genera código duplicado y ahí la importancia de hacer refactoring sobre el código de prueba. Personalmente me pasa que a partir de cierto punto, siento que hago mucho más refactoring sobre el código de prueba que sobre el código de producción. Más aún, cuando la aplicación alcanza cierto tamaño o grado de complejidad los refactorings sobre código de prueba llevan a generar clases/métodos “de soporte” por ejemplo para generar objetos/datos de prueba. En línea con esto me parece que no es casualidad que el libro de Gerard Meszaros tenga como subtítulo “Refactoring Test Code“. También los libros de Tarlinder y Freeman tienen capítulos dedicados al cuidado del código de prueba.

A propósito de este tema, el próximo jueves 12 de noviembre voy a estar dando una charla en la conferencia dotnetconf 2020 en la que compartiré algunas técnicas para mejorar la legibilidad de tests en C#: Enhancing Test Readability with Extension Methods and Fluent Interfaces.

No más cursos de TDD

Estaba escribiendo una respuesta a un tweet en un hilo sobre developer testing y espontáneamente tuve esta revelación: no dictar más cursos de Test-Driven Development.
Si bien no lo tenía listado en mi catálogo de cursos, tenía un curso de TDD que venia dictando en forma privada a pedido de algunas empresas. Pero ya no más. La cuestión es simple: TDD es muy difícil de aplicar en proyectos de complejidad no trivial para gente que recién empieza con TDD y no creo que un curso de 1 o 2 días pueda bastar para que alguien aprenda la técnica y esté en condiciones de aplicarla en un proyecto no trivial apenas terminado el curso. Pero ojo, esto no significa que vaya a dejar de enseñar TDD. No, de ninguna manera. Simplemente voy a cambiar de estrategia: voy compartir gratuitamente un par de videos explicando la técnica (o incluso tal vez recomiende cursos de colegas) y luego ofreceré a los interesados trabajar conjuntamente en su proyecto para aplicar TDD.

Si bien yo uso TDD en mis proyectos, me llevó años y mucha perseverancia poder hacerlo una forma fluida y eficiente. Pero como he dicho más de una vez no considero TDD como una práctica de “efectividad universal”, a mi me resulta eficiente programar haciendo TDD, pero no significa que vaya a resultarle igual a todo el mundo. A mis alumnos les exijo que hagan TDD cuando programan para la materia porque es parte del plan de estudio. A mis compañeros de trabajo les sugiero que hagan TDD si es lo que hemos acordado como equipo, pero dependiendo del contexto me parece completamente válido que agreguen los tests a posteriori si no gustan hacer TDD.

Dicho esto, si quieren aprender TDD conmigo, no me consulten por cursos, contáctenme en privado y coordinemos directamente para hacer un par de sesiones de TDD sobre su proyecto.

Sobre la secuencia de tests al hacer TDD

Este es uno de los temas clave al hacer TDD. La elección de la secuencia de test puede hacer que el ciclo de TDD resulte muy fluido o que sea una pesadilla. Más aún, tengo la sospecha que la elección inapropiada de la secuencia de tests debe ser una de las principales razones de abandono de TDD. Personalmente suelo aplicar la heurística conocida como “TDD Guided by Zombies” propuesta por James Grenning, pero incluso usando esa heurística hay situaciones en la que mi secuencia de tests no me resulta lo suficientemente fluida. Quiero compartir un caso concreto para repasar distintas heurísticas.

Ayer por la tarde estábamos con otros 3 colegas en un sesión de mobbing trabajando sobre una funcionalidad que consistía en filtrar una lista de objetos Cuenta en base a 4 condiciones. En caso de no encontrar ningún objeto que cumpla con las 4 condiciones en forma simultánea debíamos generar un error pues eso representaba una situación excepcional de negocio.

Un colega propuso comenzar por los casos de excepción y luego los casos positivos. Lo que me gusta de esta heurística es que inicialmente permite avanzar rápido, el código del método generado comienza con una secuencia de validaciones que van respondiendo a los distintos casos de excepción. Lo que no gusta de esta secuencia es que durante varios ciclos el código no resuelve el problema para nadie, o sea, el método en cuestión solo valida y lanza excepciones pero no genera una lista filtrada. Hay que esperar casi hasta el final de la secuencia para obtener una lista filtrada. Una posible secuencia de tests con esta estrategia sería:

DeberiaLanzarExcepcionSiNoTieneCuentasEnPesos
DeberiaLanzarExcepcionSiNoTieneCuentasConTarjetaAsociada
DeberiaLanzarExcepcionSiNoTieneCuentasTitular
DeberiaLanzarExcepcionSiNoTieneCuentasActivas
DeberiaFiltrarLasCuentasEnPesosConTarjetaTitularidadActivas
...(otros casos positivos)...

Personalmente yo prefiero comenzar con casos positivos y dejar las situaciones de excepción para el final. En este sentido un camino posible sería arrancar literalmente al revés del ejemplo anterior, comenzando con el caso positivo que contempla todas las condiciones. El problema de esta estrategia es que genera un paso muy grande, o sea, implica escribir en un solo paso el algoritmo completo que considera las 4 condiciones.

Otra opción en línea con la idea de transitar el camino de casos positivos y siguiendo la heurística de Zombies, es arrancar con una condición positiva a la vez. De esta forma, ya de entrada estamos generando una lista filtrada, que al menos para algunos casos será una lista completamente válida. A partir de eso podemos ir generar nuevos casos positivos refinando el filtro y agregando los casos de excepción luego de tener todo el filtro completo. De esta forma cada paso del ciclo se mantiene pequeño y el algoritmo de filtrado se construye incrementalmente. La secuencia resultante podría ser:

DeberiaFiltrarSoloCuentasEnPesos
DeberiaFiltrarSoloCuentasConTarjeAsociada
DeberiaFiltrarSoloCuentasTitular
DeberiaFiltrarSoloCuentasActivas
.... (otros casos incluyendo los de excepcion)....

Obviamente que también está la posibilidad de tomar una secuencia que alterne casos positivos y negativos. De hecho si uno siguiera a raja tabla la heurística Zombie el primer caso (caso Zero) sería negativo y los siguientes positivos (caso One y casos Many).

Bueno, esto esto por ahora pero creo que este tema de la elección de la secuencia de tests aún da para mucha charla.

Materiales de la sesión TDD al banquillo

El viernes pasado estrené esta charla en el meetup online de Software Crafters Chile. La sesión duró poco más de 1 hora y yo quedé muy conforme. Creo que la exposición estuvo bien y la gente se mostró muy participativa. A partir de algunas preguntas/comentarios de los participantes se me ocurrieron algunos updates a la charla para una futura presentación.

Los slides que utilicé están disponibles aquí, allí mismo están las referencias a los papers formales sobre TDD. Como mencioné en la sesión, el paper que me resulta más relevante es el de Fucci, Erdogmus y colegas: “A Dissection of the Test-Driven Development Process: Does It Really Matter to Test-First or to Test-Last?

Para quienes quieran estudiar TDD mis recomendaciones son:

Agradezco a @JosephCastroR y demás organizadores del meetup por la invitación.

TDDeando la arquitectura

El viernes pasado estuve dando una charla titulada así en el contexto del meetup online de ArqConf.

Había más de 370 registrados pero como suele ocurrir con los meetups gratuitos, rara vez se llega al 50 %. Cuando comencé a hablar había alrededor de 120 personas y me consta que luego se fueron sumando más pero no tengo presente cuántos.

Dado que la charla trataría sobre TDD, comencé haciendo una breve encuesta a los presentes sobre su conocimiento de TDD. Si bien más del 90 % conocía TDD, tan solo el ~17 % dijo utilizarlo a diario. Personalmente no me sorprende, porque según estudios formales que hemos realizado en los últimos años, el uso consistente de TDD ronda el 20%.

La presentación fue grabada y está disponible en YouTube y los slides están disponibles aquí.

Estos son algunos de los libros en los que me basé para armar la charla:

  • Growing object-oriented software guided by tests, de Freeman
  • Designing Software Architectures: A Practical Approach, de Cervantes y Kazman
  • Just Enough Software Architecture: A Risk-driven Approach, de Fairbanks
  • Building Evolutionary Architectures: Support Constant Change, de Ford
  • Specification by example, de Adzic

Agradezco a Gus Brey y demás organizadores por la invitación.

Próximas charlas sobre TDD

En las próxima semanas voy a estar participando de eventos virtuales con dos nuevas charlas sobre TDD que vengo trabajando desde hace un tiempo. Aún no tengo confirmadas la fechas (en cuanto las tenga las compartiré en redes) pero igualmente comparto la temática de las mismas.

TDD al banquillo

Test-Driven Development (TDD) es una práctica creada por Kent Beck a finales de los 90. En 20 años ha cosechado varios practicantes, algunos “fanáticos” y algunos detractores. Definitivamente es una práctica muy conocida pero…¿cuan utilizada es? ¿cuanta gente usa TDD?.

Al mismo tiempo los practicantes de TDD hablan de un conjunto de beneficios que ellos mismo han obtenido, pero en términos formales/científicos ¿cuales son la mejoras que nos trae el uso de TDD?

TDD claramente impacta en la forma en que trabajamos, pero ¿tiene algún impacto en el resultado de nuestro trabajo? ¿el software resultante es “mayor calidad”?

TDD surgió en el contexto de Extreme Programming junto con otras prácticas como Integración Continua, Pair-Programming y Collective Ownership ¿es posible utilizar TDD en forma “aislada” sin incorporar estas otras prácticas? ¿Es igual de efectivo?¿Cuales serían los riesgos?

En la charla planeo recorrer todos estos interrogantes e intentaré darles respuestas desde la formalidad de varios estudios, libros y obviamente también con un condimento de experiencia personal. No es una charla introductoria a TDD en el sentido que no voy a detenerme a explicar en detalle cómo es la dinámica de TDD, creo que ya hay bastante de eso. La charla está dirigida a gente que ya conoce la propuesta de TDD independientemente de que la este usando o no.

TDDeando la arquitectura

Los cimientos de TDD se establecieron a finales de los años 90 en el contexto de Extreme Programming pero podríamos decir que, recién en 2002, Kent Beck formalizo la técnica en su libro Test-Driven Development by Example. En ese libro Kent explica la técnica y muestra ejemplos trabajando a nivel de prueba unitaria, haciendo TDD sobre clases. Sin embargo el mindset de TDD puede aplicarse a otros niveles de abstracción como el mismo Beck lo ha admitido. En esta charla veremos cómo utilizar TDD para guiar la construcción de una arquitectura en forma emergente. Esta charla no es charla introductoria a TDD, voy explicar muy brevemente la dinámica propuesta por TDD pero no será ese el foco. La charla está dirigida a gente que ya conoce la propuesta de TDD independientemente de que la este usando o no.

Investigación formal sobre TDD ¿queréis participar?

Hace unos dos meses comenzamos, con el grupo de Prácticas y Procesos de UNTreF, a trabajar en un estudio sobre Test-Driven Development (TDD).

TDD es un de las prácticas ágiles más populares pero comparativamente con otras prácticas ágiles es muy poco utilizada. Cuando digo que es popular me refiero a que es muy conocida (se sabe de que se trata) pero curiosamente hay varios estudios que muestran que su uso es mucho menor al de otras prácticas ágiles como las Restrospectivas e Integración continua. Dos fuentes que confirman esto son el reporte anual de VersionOne y nuestro propio estudio sobre Prácticas Agiles en Latam.

Con mi colega @dfontde tenemos la sospecha de que esta baja adopción de TDD puede deberse a issues de usabilidad de la práctica. Esto es precisamente lo que vamos a intentar probar (o descartar) en este estudio. Para ello utilizaremos el Modelo de Usabilidad de Práctica y Procesos que @dfontde está desarrollando como parte de su doctorado.

Para realizar esta investigación vamos a necesitar la colaboración de practicantes con al menos 5 años de experiencia en el uso de TDD. Los interesados pueden contactarme dejando un mensaje aquí.