Cambiando software en código legado: Sprout Method y Sprout Class
Michael Feathers define de una manera resumida y directa en su libro ‘Working Effectively With Legacy Code’ que código legado es aquel que no tiene tests. Ya estemos en un proyecto cuyo código tiene mucho tiempo o un proyecto ‘greenfield’, si no tiene tests…es legacy. Existen varias técnicas para afrontar cambios en estos sistemas: Sprout method, Sprout Class, Wrap Method y Wrap Class. Hoy nos acercaremos a las dos primeras.
Razones para el cambio
Antes, haremos un pequeño inciso para revisar cuáles son las cuatro razones principales para cambiar el código de una aplicación:
- Añadir una característica
Es el tipo de cambio más sencillo. Se da cuando el software funciona de una manera y los usuarios necesitan que el sistema haga más cosas.
- Corregir un bug
Se da cuando tenemos que cambiar el código por errores cometidos en el propio desarrollo que nos dan resultados inesperados o incorrectos.
- Mejorar el diseño
Es un tipo diferente de cambio. Se da cuando queremos cambiar la estructura del software para hacerlo más mantenible conservando el comportamiento existente en el mismo. Cuando no somos capaces de mantener el comportamiento entonces estamos cayendo en un bug. El acto de mejorar el diseño del software sin cambiar el comportamiento es llamado refactoring (nota: ¿y cómo sabemos que no cambiamos el comportamiento?…por los tests. Debemos tener unos tests que nos aseguren que el comportamiento existente sigue siendo el mismo. De manera que, si no hay tests…no podemos decir estrictamente que estamos refactorizando…estaremos haciendo otra cosa y lo llamaremos como queramos, pero, desde luego, refactorizar código, no).
- Optimizar el uso de recursos
Es como el refactoring pero, los cambios en el software no los hacemos con el objetivo de hacerlo más mantenible sino con el objetivo de mejorar el uso de algún recurso que se utilice, normalmente uso del procesador, tiempo de ejecución o uso memoria.
Tests en código legado
Ya sabemos que lo correcto es escribir tests para nuestro código. Es la forma de asegurarnos que funciona y que además cambios futuros no introducen errores en el código existente.
Pero, cuando trabajamos con código legado para introducir cambios en él , nos podemos encontrar con que el código esté tan acoplado o tan mal escrito que sea complejo escribir tests en un tiempo razonable. Por esto, podemos admitir no escribirlos para el código antiguo y centrarnos en aislar el nuevo código para que al menos este si podamos testearlo de forma correcta.
El objetivo de implementar Sprout Method y Sprout Class es poder crear tests para nuestro nuevo código de una forma más sencilla y rápida cuando nos encontremos en un contexto y con un código legado en el que no nos sea posible escribir tests en un tiempo razonable.
Sprout Method
Se da cuando necesitamos añadir una característica nueva a un sistema y esta puede ser escrita completamente en un método nuevo (sprout method) dentro de una clase de forma aislada, que, posteriormente, será llamado desde aquellos lugares en los que se necesite. Para ello, debemos poder ver el nuevo desarrollo como una pieza distinta y aislada de código. Es posible que no seamos capaces de testear todos los puntos en los que realizamos la llamada pero al menos podremos escribir tests del código nuevo.
La mecánica o pasos para crear un sprout method son los siguientes:
- Identificar el lugar en el que se necesita el cambio
- Escribir una llamada al nuevo método y dejarla comentada. Nos servirá para tener el contexto del lugar en el que va a realizar la llamada.
- Determinar las variables locales son necesarias en el código original y convertirlas en argumentos de llamada para el método nuevo.
- Determinar si el ‘sprouted method’ necesitará retornar valores al código original. Si así fuera, cambiar la llamada para que el valor de retorno sea asignado a una variable.
- Desarrollar el sprout method usando preferiblemente TDD…o al menos testearlo después.
- Eliminar el comentario en el código original para activar la llamada.
La principal ventaja de usar Sprout Method es que estamos separando claramente el viejo código del nuevo lo cual nos lleva a diferenciar muy bien dónde estamos introduciendo los cambios.
Sprout Class
En este caso vamos un poco más allá que con un Sprout Method ya que el código que vamos a escribir lo vamos a extraer a otra nueva clase (Sprout Class). Tenemos dos motivos principales para crear una Sprout Class:
- Uno: nuestros cambios van dirigidos a añadir una nueva responsabilidad en una de nuestras clases y estos pueden ser aislados conceptualmente en una nueva clase que además nos facilitará la creación de tests.
- Dos: tenemos un trozo de código nuevo que podríamos escribir en una clase existente pero es muy costoso incluir a esta última en un arnés de tests en un tiempo razonable, por lo que es extraído a otra clase facilitando también la creación de tests.
La principal ventaja de implementar Sprout Class es que nos permite avanzar en el desarrollo con más confianza que si tuviéramos que realizar cambios de forma invasiva en el código existente. Una de las desventajas es que aumentamos la complejidad conceptual rompiendo abstracciones y hacer la mayor parte del trabajo en otras clases distintas a la original.
La mecánica para incluir una Sprout Class es similar a la descrita anteriormente para un Sprout Method.
Be careful, amigo
Aún así, hay que usar estas técnicas con cuidado ya que aunque estemos añadiendo código testeado en un nuevo método o clase, si realmente no podemos cubrir con tests el código que los llama no estaremos testeando su uso real. Así que precaución.
Chimpún!
Bibliografía
- Michael Feathers (2005). Prentice Hall, ed. Working Effectively with Legacy Code. ISBN 0–13–117705–2.