TDD y calidad de vida (la tuya)
Ya sea como empleados o freelance vendemos nuestro tiempo y trabajo a empresas y clientes. Una cosa que distingue nuestra profesión de otras es el hecho de que vendemos trabajo intelectual. A veces, incluso, trabajo intelectual de alto nivel.
Así que cuidar de nuestra mente e inteligencia parece ser una actividad razonable que deberíamos practicar con frecuencia.
Hay mucha gente en el mundo del desarrollo de software que piensa, o incluso afirma, que hacer testing es duro o caro. Y eso sin mencionar el Test Driven Development. Pero lo que queremos demostrar es que TDD es el camino más recomendable si quieres tener una vida más sana en el campo del desarrollo de software.
Pero primero, veamos un par de cuestiones acerca de cómo funciona nuestro cerebro.
Conocimiento en el mundo, conocimiento en la cabeza
Puertas
¿Sabes utilizar una puerta? ¿Segura? ¿Has visto alguna vez una puerta con manual de instrucciones? Yo sí. Montones de ellas en realidad: todas esas que tienen letreros indicando si se debe empujar o tirar. Me apuesto algo a que te has encontrado con más de una de esas.
¿Te has visto alguna vez ante una puerta cerrada sin saber cómo abrirla? Yo sí. De hecho las hay por docenas en el mundo, como puertas deslizantes con sensores que no están bien ajustados, o puertas que abren hacia adentro cuando todo indica que abren hacia fuera.
El caso es que una puerta debería ser algo fácil de usar y no siempre sucede. La forma en que se usa una puerta debería ser obvia, ¿no?
Interruptores
¿Y qué tal si hablamos de interruptores? Me refiero a esos paneles de interruptores cuya disposición no está relacionada con la de las luces que controlan. A veces están situados en lugares en donde no se pueden ver las lámparas y necesitas probar varias veces hasta encontrar la combinación secreta que enciende la lámpara que quieres.
La relación entre un interruptor y la lámpara que controla debería ser obvia, ¿no?
Hablemos de lo obvio
Cuando hablamos de algo obvio, nos referimos a conocimiento que no deberíamos tener que buscar en nuestra cabeza. El conocimiento está ahí, en el mundo. Solo tenemos que usarlo mientras hacemos otras cosas. Queremos poder abrir puertas y encender luces sin tener que pensar ni una fracción de segundo acerca de ello.
Por eso, cuando nos vemos obligadas a pensar en cosas que deberían ser obvias, estamos desperdiciando parte de nuestros recursos mentales, utilizando espacio en nuestra memoria de trabajo que preferiríamos, o incluso deberíamos, estar empleando en otros propósitos.
El conocimiento está en el mundo cuando todas las pistas que necesitamos para usar o interactuar con un objeto están presentes en el objeto mismo. Por eso no necesitamos preocuparnos, razonar o recordar cómo utilizarlos. Si necesitamos hacerlo, es decir cuando necesitamos razonar o recordar instrucciones, tenemos que poner el conocimiento en nuestra cabeza para poder alcanzar nuestro objetivo.
Por esto, si disponemos de más conocimiento en el mundo cuando ejecutamos una tarea, necesitamos menos conocimiento la cabeza, dejando espacio libre en ella que podemos usar para pensar mejor acerca de lo que estamos haciendo.
Cuanto menos tengamos que pensar en la forma de usar las herramientas, más podemos pensar en lo que estamos haciendo con ellas.
Pero, ¿de cuánto espacio disponemos en nuestra memoria de trabajo?
Bueno…, el caso es que no mucho.
La capacidad de nuestra memoria de trabajo
Disponemos de una capacidad de almacenamiento prácticamente infinita es nuestra memoria. Piensa en ella como un enorme e inteligente disco duro que puede guardar recuerdos y datos durante años. No se trata de un almacén pasivo. De hecho, reconstruye nuestra memoria constantemente para guardar y recuperar cosas. Esto es importante, porque cuando lo pensamos, necesitamos usar nuestra memoria de trabajo para mantener los datos que estamos usando. Muy parecido a un ordenador.
Sin embargo, nuestra memoria de trabajo es bastante diferente de nuestra memoria a largo plazo. Hay quien la llama “memoria a corto plazo” y hay quien “memoria de trabajo”. Creo que podemos verla como un procesador, con algunos registros que pueden almacenar una cantidad limitada de unidades de información llamadas chunks mientras trabaja. Los chunks pueden tener un tamaño variable, pero son unidades significativas.
Como nos dedicamos a la programación, una buena forma de entender de estos chunks es pensar que son punteros que indican posiciones de memoria. En esas posiciones puede haber estructuras de cualquier tamaño. A veces son muy pequeñas, como una letra o un número, y otras veces son enormes.
¿Puedes recordar un número de teléfono? Me apuesto a que has agrupado los dígitos de modo que solo tines dos o tres números que retener.
Esto es porque nuestro procesador puede manejar un número limitado de chunks. Este número es aproximadamente 7 (más o menos dos). Es algo que varía con la edad y entre los individuos, pero es una aproximación muy buena. Por tanto, intentamos ahorrar tantos registros como podemos, agrupando la información en chunks, y manteniendo alguno de los registros libre.
¿Qué sucede ti llenamos todos los registros? Pues la precisión y la velocidad al realizar la tarea disminuyen, aumentando los errores. En general, el desempeño es peor si tratamos de mantener muchas cosas en nuestra memoria de trabajo al mismo tiempo.
Por supuesto, se trata de una sobre simplificación. Sin embargo, creo que puedes hacerte una idea. Podemos reducir la sobrecarga si ponemos el conocimiento el mundo, en lugar de mantenerlo en nuestra cabeza, con lo que nuestro desempeño será mejor en cualquier tarea.
Puedes poner conocimiento fuera de la memoria de trabajo mediante la práctica. Eso es lo que ocurre cuando introducimos una nueva técnica, intentamos aplicar una novedad del lenguaje de programación o utilizar una nueva herramienta. Al principio vamos lentos y cometemos errores. Necesitamos tiempo para automatizar cosas en nuestra mente mientras ponemos conocimiento en el mundo.
Es hora de volver al objetivo principal de este artículo. Hablemos acerca de nuestra vida como desarrolladora.
Un día en la vida
Programando sin test
Analicemos por un momento qué sucede cuando programamos sin hacer tests.
De hecho, en realidad siempre hacemos tests, pero con frecuencia tendemos a que sean manuales. Lo llamamos “depurar”. Usamos un proceso de prueba y error: ¿Esto funciona? ¿No…? Prueba otra vez. ¿Sí…? Sigue adelante.
Intentamos escribir código y verificar que funciona al mismo tiempo que lo escribimos, hasta que nos parece que está terminado. Tras eso, intentamos verificar que el código funciona como un todo. Entonces descubrimos que habíamos olvidado algunos detalles… Tras eso, desplegamos y descubrimos nuevos detalles que no funcionan, así que necesitamos corregirlos.
Al final del día, nos encontramos con grandes dolores de cabeza y bajo la impresión de habernos perdido algo.
Esto sucede porque intentamos mantener toda la información en la cabeza al mismo tiempo (recuerda que tiene capacidad limitada). Nos sobrecargamos nosotras mismas. La mejor estrategia es hacer una lista de metas y tareas, y ayudarnos a mantener cierta organización y foco con estos soportes externos.
Por ejemplo: escribir un simple endpoint para una API necesita un montón de cosas:
1. Una acción en un controlador
2. Una ruta a ese controlador
3. Un caso de uso o comando que ejecute la acción
4. Probablemente una o más entidades de dominio y su repositorio
5. La definición en el contenedor de dependencias
6. Posiblemente algún servicio
7. La definición del mismo en el contenedor de dependencias
8. Un objeto de respuesta
9. etc.
Con esto nuestra memoria se sobrecarga, ya que superamos ampliamente los 7+/-2 items. Esto explica por qué nos sentimos cansadas y estresadas, con el sentimiento de que podríamos haber olvidado algo. E inseguras sobre los que estamos haciendo o si nos hemos dejado atrás algo importante.
Así que, echemos una mirada a cómo ejecutaríamos el mismo proceso, esta vez con testing al final.
Programando con tests al final
En realidad es casi lo mismo, pero ahora hay tests al final del proceso. La clase de tests que automatizamos.
El resultado final es mejor, porque ahora tenemos más confianza con el código. Pero seguimos teniendo ese mismo dolor de cabeza al final del día.
Sí. Hemos hecho la misma cantidad de trabajo, con la misma sobrecarga mental y con el añadido de tener que escribir una suite de tests, mientras nuestro cerebro nos grita: “¡Hey! ¡Pero si el trabajo está terminado! ¿Qué estás haciendo?”
En estas condiciones, puede que nuestros tests no sean los mejores tests del mundo. Ni la suite cubra todos los escenarios posibles.
De hecho, ya estamos cansadas cuando empezamos la fase de testing. Esto explica por qué mucha gente piensa que el testing es duro y hasta que es doloroso.
Así que los test mejoran nuestra confianza en el código, pero al coste de obligarnos a un montón de trabajo extra. Nuestra vida no es mejor con tests, incluso si dormimos mejor por la noche. Entonces, ¿qué es lo que está mal?
Para mejorar tu vida deberías probar una aproximación diferente. Debería probar Test Driven Development.
Programando con TDD
En esto consiste TDD: una cosa cada vez y posponer decisiones.
- Un test sencillo que falle: no escribas código mientras no tengas un test.
- Añade código que haga pasar el test: no escribas ni más ni menos de lo necesario.
- Revisa el código para mejorar cosas, pero no implementes nada nuevo y mantienes los tests existentes pasando.
Veamos el proceso desde el punto de vista de nuestro modelo de la memoria de trabajo. Cuando escribimos el primer test que falla estamos enfocándonos en este test. Por tanto, no tenemos que poner atención a nada más fuera de eso. Escribir el test también significa que ponemos el conocimiento que necesitamos en el mundo. Nuestra memoria está casi desocupada.
A continuación, nos enfocamos en escribir el código necesario para hacer que el test pase. El conocimiento que necesitamos está en el test, no en nuestra cabeza, y es lo que necesitamos para conseguir el objetivo inmediato.
Solo tenemos que pensar en una forma de hacer que el test pase. Si es el primer test, solo necesitamos escribir la implementación más obvia que sea posible. Incluso si esa implementación es tan simple como devolver la misma respuesta esperada por el test.
Y una vez que el test ha pasado, podemos echar un vistazo al código y ver si podemos hacer mejoras mediante refactoring. No tenemos que añadir prestaciones. Debemos mantener el test pasando mientras ordenamos las cosas, eliminando duplicación innecesaria, introduciendo mejores nombres, etc.
Repetiremos el ciclo hasta tener la funcionalidad completamente implementada. No necesitamos escribir tests extras, no tenemos el riesgo de haber olvidado nada. Nuestra cabeza no duele. Hemos usado el cerebro para pensar evitando la sobrecarga.
No es magia, es TDD. Por supuesto, para lograr esto se necesita entrenamiento. TDD es una herramienta intelectual, y el uso de una herramienta debe automatizarse. Por tanto, deberías hacer ejercicios, como las katas, tanto individualmente, como con la ayuda de colegas, en una comunidad de práctica, de la forma que mejor te convenga a ti o a tu equipo. Practicar, practicar y practicar. Una vez que seas capaz de proceder paso a paso, descubrirás que estás más feliz y menos estresada en el medio y largo plazo.
Un consejo final
Almacena la mayor cantidad de conocimiento que necesites en el mundo: usa un backlog, usa post-its, escribe una lista de tareas, dibujas esquemas, modelos, mapas conceptuales… Liberta tu cabeza y deja espacio para trabajar en una cosa cada vez.
TDD es más que escribir tests. Es poner el conocimiento que necesitas en el código y liberar tu mente. Es posponer decisiones hasta el momento en que estés lista para tomarlas.
De verdad, prueba TDD, tu vida como desarrolladora mejorará.