Si eres desarrollador habitualmente vas a trabajar con legacy code.
Normalmente a los programadores nos gusta más escribir código nuevo que modificar código existente por los riesgos que supone y porque vas a trabajar dentro del contexto en que se desarrolló el código a modificar.
Esto muchas veces implica desarrollar en tecnología antigua o incluso obsoleta, código que has heredado de otros desarrolladores y sin tests o con ellos pero malos o desactualizados.
Esto se acentúa todavía más si el código que tenemos que modificar se encuentra en mal estado, clases o métodos muy grandes, código muy acoplado etc..
Seguro que más de uno os sentís reflejados y habéis vivido alguna situación similar llegando a convertirse en una autentica pesadilla.
Existen técnicas y buenas prácticas para trabajar con legacy code que ayudan manejarte en este entorno.
La experiencia también juega un papel fundamental para evitar meterte en un pozo del que puede resultar imposible salir.
Este artículo pretende ser una introducción al concepto de legacy code y en futuros artículos veremos diferentes técnicas que podemos utilizar.
Legacy Code
¿Qué es código legado?, si hicieramos una encuesta entre diferentes desarrolladores esta definición seguro que variaría significativamente y no existe una única definición.
A continuación voy a enumerar algunas definiciones:
Legacy code o código heredado es código fuente relacionado con un sistema operativo o una tecnología de computación sin soporte técnico - Wikipedia
Michael Feathers lo define desde otro punto de vista:
Código sin tests es un mal código. No importa si esta bien escrito; no importa si es bonito, orientado a objetos o lo bien encapsulado que está. Con tests, podemos cambiar el comportamiento de nuestro código rápidamente y con seguridad. Sin ellos, realmente no sabemos si nuestro código está cambiando a mejor ó a peor. ― Michael C. Feathers, Working Effectively with Legacy Code.
También nos proporciona una definición más prágmatica:
Código legado es código sin tests - Michael C. Feathers, Working Effectively with Legacy Code
Más alla de las definiciones, creo que cualquier desarrollador estaría de acuerdo en reconocer el código legado como aquellas features que da miedo tocar, nadie suele querer realizar y que se evitan si es posible iteración tras iteración.
Motivos para cambiar código existente
Muy a menudo al trabajar con diferentes empresas, me encuentro con que a todo lo relacionado con tocar código legado se engloba dentro del concepto refactoring.
Sin embargo el refactoring es un concepto relacionado solo con uno de los motivos para modificar código legado.
Veamos qué motivos existen:
Añadir una característica nueva
Consiste es añadir una carácteristica nueva normalmente solicitada por el cliente. A veces lleva implicito modificar código existente pero no tiene porqué ser siempre así.
Corregir un error
Consiste en solucionar un error detectado por el cliente o por nosotros mismos.
Mejorar el diseño del código
Consiste en cambiar el diseño o la estructure del código, con el objetivo de hacerlo más mantenible, sin modificar el comportamiento. A este tipo de cambio se le conoce como refactorizar.
¿Cómo nos aseguramos de qué no modificamos comportamiento?, los tests automáticos son los que nos va a servir para saber si el comportamiento sigue siendo el mismo.
Por este motivo, cuando nos enfrentamos a un código legado sin tests una de las primeras acciones debe ser crear tests.
En función del estado del código, estos primeros tests pueden ser tests unitarios pero otras veces donde no es posible crear tests unitarios porque el código no es testable al estar muy acoplado, es necesario empezar por crear tests end to end.
Recuerda que los test forman una parte fundamental del refactoring porque nos van a servir para verificar que el comportamiento no ha sido modificado.
Modificar la estructura del código sin tests no se puede llamar refactorizar.
Optimizar el uso de recursos
Similar al refactoring, en cuanto a que el comportamiento no debe variar, pero el objetivo no es hacer más mantenible el código, sino mejorar otros aspectos como el rendimiento, el uso de la memoria o reducir las peticiones de red, añadiendo una cache, en caso de ser una aplicación móvil etc...
Por dónde empezar a refactorizar
Siempre es buena idea aplicar la regla del boy scout, aunque ojo, el sentido común es importante porque puede ser un arma de doble filo.
Cuando queremos plafinicar una estrategia de refactor un poco más amplia existen una serie herramientas y estrategias que tenemos a nuestro alcance:
-
Aislar el dominio, orientar los refactors a aislar el dominio siempre es una buena inversión. Una de las carencias más habituales al trabajar con código legado es no tener bien identificado y aislado el dominio.
Poco a poco ir mutando hacia Clean Architecture a una arquitectura similar nos va a permitir ir desacoplando toda esa lógica de negocio e ir creando tests para los más valioso del proyecto como es el la lógica de dominio. Esta estrategía también nos va a permitir tener un código más escalable y tolerante a cambios al estar cada vez el dominio menos acoplado por ejemplo a la capa de infraestructura, capa de red, interfaz de usuario etc... -
Módulos o partes de nuestra aplicación con más bugs, parece obvio pero tener una herramienta donde gestionar errores encontrados por el cliente nos puede ayudar a enfocar futuros refactors.
Además de los errores que pueda encontrar nuestro cliente, deberíamos tener un sistema de reporting de errores que nos esté aportando feedback de nuestra aplicación en producción.
Un número elevado de bugs en una parte del software es síntoma de falta de tests automáticos o demasiado acoplamiento. -
Clases grandes, analizar los ficheros más grandes del proyecto nos puede llevar a aquellas clases que se encargan de realizar demasiadas cosas y donde deberíamos de empezar a aplicar principios SOLID.
-
Analizar el repositorio de código fuente, analizar el repositorio de código fuente puede ayudarnos a descubrir acoplamientos entre diferentes partes del código. Normalmente este acoplamiento emerge como ficheros que siempre van juntos en los commits.
Una herramienta que podemos utilizar es Code Maat. -
Análisis estático del código, existen herramientas que abarcan diferentes leguajes y que analizan el código proporcionandonos recomendaciones:
Referencias
Existen algunos libros que son una referencia en este tema:
-
Refactoring: Improving the Design of Existing Code (Addison-wesley Signature Series) - Segunda edición (2019) de Martin Fowler.
-
Working Effectively with Legacy Code de Michael Feathers,el cual pertece a la serie de Robert C. Martin.
-
Your Code as a Crime Scene: Use Forensic Techniques to Arrest Defects, Bottlenecks, and Bad Design in Your Programs de Adam Tornhill.
Curso y artículos relacionados
- Curso código legado y refactoring
- Trabajando con código legado: Sprout Method y Sprout Class
- Trabajando con código legado: Wrap Method y Wrap Class
Conclusiones
En este artículo hemos introducido el concepto de legacy code.
Hemos visto algunas definiciones y hemos enumerado algunos motivos para cambiar código existente.
También hemos repasado algunas estrategias y herramientas para saber por donde empezar a refactorizar.
En futuros artículos veremos algunas técnicas que se pueden utilizar al trabajar con código legado.