Este comienza a convertirse en uno de mis mantras favoritos: "refactorizar o morir".

Recientemente he vuelto a poner en práctica una de las mejores virtudes de realizar este tipo de trabajo continuamente. El resultado finalmente es muy satisfactorio después de muchos momentos tipo "esto está quedando fatal", "así sólo voy a llegar a una solución muy enmarañada", etc. El desánimo cunde rápido, sobre todo si se trata de un pequeño proyecto que haces a ratos por las noches o fines de semana.

No obstante, una mínima tenacidad (y seguramente tirarlo todo a la basura y volver a empezar en algún momento), te hace llegar a una buena solución que no sólo funciona, sino que además es ampliable y evolucionable con cierta facilidad.

Se habla mucho de productividad; para mí es muy sencillo describir qué es productivo y qué no en software: las soluciones fáciles son más productivas que las inextricables que sólo pueden entender sus autores, aquello que nos permite ganar velocidad de desarrollo es más productivo y con ello conseguiremos más con menos esfuerzo. Nada más. Así de contundente y simple.

Hay quienes se procupan de mejorar el código de una aplicación en alguno de sus aspectos en algún momento del trabajo: al final, cuando ya todo funciona, de vez en cuando... Sin embargo, las ventajas de incorporar estas tareas de mejora en todo momento no siempre se aprecian como productivas, mucho menos cuando nos acercamos peligrosamente a las fechas de entrega y comenzamos a dejar cabos sueltos (de los que nos acordaremos sin duda semanas o meses más tarde).

¿Por qué refactorizar cuando lo importante de una aplicación es que le funcione al cliente? Buena pregunta, y al mismo tiempo, ingenua. Quienes aún no ven claro las virtudes de invertir tiempo en este trabajo, deben saber que lo primero que se gana es velocidad de desarrollo, por tanto, productividad.

La velocidad con la que desarrollamos cualquier pieza de software viene a ser una medida de la productividad, sobre todo cuando ésta nos acerca al objetivo de entregar las funcionalidades que nos piden.

Tenemos que perseguir que el diseño o la estructura de la solución que vamos construyendo, con el tiempo nos permita avanzar más rápido y no lo contrario.

En esa aplicación en la que llevo trabajando un tiempo he ido aplicando continuamente todas las técnicas de mejora posibles: simplificaciones, abstracciones en clases de utilidad, mejora en la estructura de la solución, modularizaciones, etc, de modo que cada dos semanas he podido ver cómo el código de un commit de hace quince días no tenía nada que ver con el del último. Esto cobra especial relevancia cuando desde el principio no tenemos claro cómo vamos a implementar ciertos aspectos de la aplicación un poco complicados.

A medida que avanzamos en la solución y gracias a que aplicamos todo ese trabajo de mejora, vemos cómo poco a poco va surgiendo (emergiendo) un diseño cada vez mejor. "Mejor" es una palabra muy subjetiva y difícil de consensuar entre dos o más personas. Para mí, en software, algo es mejor si te permite desarrollar más rápido (invertir menos tiempo en la misma solución) sin perder calidad en todos los aspectos de esta.

Ese diseño elegante, sencillo, correcto, es el que nos permite a partir de un punto ganar velocidad. Llega un momento en que esto es así y es entonces cuando te das cuenta de que todo ese trabajo de mejora ha sido en realidad una buena inversión de tiempo que ahora se va a amortizar. Es como si tuviéramos una madeja de hijo que al principio cuesta mucho desenredar hasta que llega el momento en que es fácil y rápido sacar más y más hilo de ella.

En ese proyecto en concreto que estoy realizando, ha llegado un momento que he conseguido resolver todas esas partes más difíciles de manera tan sencilla y elegante que ahora lo que me queda es decorar el resto hasta finalizar la aplicación. Esto no habría sido posible si no me hubiera parado al principio con esa idea de mejorar la aplicación en todos sus aspectos.

Los comienzos de una aplicación en la que hay muchas dudas que resolver son duros, cuesta apreciar verdaderamente avances y tenemos la tentación de tirar por lo rápido y fácil, sin darnos cuenta que ese camino se volverá en nuestra contra con el tiempo.

Llega un momento en que esa mejor arquitectura y diseño para nuestro propósito es tan maduro que ya sólo nos queda seguir esa coherencia para terminar la aplicación con éxito.

Lo mejor, además, es que llegaremos al final con una solución limpia y fácil de mantener y evolucionar.

Algunas de las técnicas que he empleado hasta el momento son las siguientes:

  • A medida que el código crecía, he ido adaptando la estructura y distribución de este mucho mejor.
  • Clases largas las he ido abstrayendo en clases más concretas y sencillas con una relación lógica entre ellas (principio SRP).
  • Duplicidades las he aislado correctamente en sus servicios correspondientes (el frontend está basado en AngularJS).
  • He ido buscando continuamente todo aquello inyectable, es decir, todas aquellas dependencias que se podían sacar para implementar Inyección de Dependencias.
  • He simplificado muchos bloques de código similares con sus correspondientes funciones de utilidad.
  • Métodos de más de tres o cuatro parámetros los he ido simplificando.
  • y un largo etcétera.

Al final, todo este tipo de trabajo constituye un entrenamiento por el que terminas haciéndolo de la manera más natural y ni te planteas conscientemente el realizar esas actividades como algo separado del trabajo de desarrollo, sino como algo consustancial al mismo.

En El Libro Negro del Programador se insiste mucho (como en cualquier otro libro que nos enseñe a ser mejores profesionales) que la refactorización de código no es sólo una herramienta que debemos usar sino que ésta es un hábito que emplear en el día a día y que distingue a los mejores desarrolladores de software.

No obstante, el concepto de refactorizar se suele entender exclusivamente como un trabajo de modificar líneas de código y mejorarlo sin afectar a su comportamiento externo; conseguimos, efectivamente, limpiar en cierta medida el código para que sea de mejor calidad, más legible y mantenible.

Siempre insisto en que esto no es algo que se hace o no, sino que forma parte del hecho de programar una solución, es algo consustancial a la misma.

Cuando terminamos de implementar una nueva funcionalidad, siempre nos debemos preguntar ¿podemos mejorar lo que hemos implementado en algún aspecto?, ¿se puede simplificar?, ¿se puede mejorar en relación al proyecto general como parte que se pueda reutilizar?, etc. Lo sorprendente es que cuando te creas el hábito de plantearte este tipo de cosas apenas termina haciendo falta hacerlo al final de implementar algo, ya que tienes el hábito de hacer las cosas limpias desde un principio de modo que al final, es esfuerzo o tiempo que empleas como puro trabajo de refactorizar es mínimo.

No obstante, se suele entender por refactorizar el trabajo de mejora del código, pero ¿qué ocurre con la misma estructura de un proyecto que va creciendo y creciendo con el tiempo? No hablo de diseño, sino de cómo están organizadas las carpetas, subproyectos, etc.

Cuando un proyecto crece en tamaño (en forma de librerías dispersas, multitud de sub-proyectos, demasiadas dependencias externas, incluso una estructura de los proyectos de pruebas compleja) se debe y tiene que refactorizar igualmente.

Recientemente hemos cerrado una nueva revisión de uno de los proyectos en los que trabajamos actualmente (la Plataforma de Telegestión y MDM IRIS para dispositivos de telemedida PRIME). Pues bien, he planteado unos días de trabajo para mejorar la estructura interna del mismo para evitar que sea cada vez más compleja (de gestionar y de trabajar sobre ella).

En concreto, algunas de las cosas que vamos a evaluar son:

  • Unificación de algunas librerías dispersas que podrían vivir en un único proyecto.
  • Integración de algunos subproyectos pequeños que podrían vivir coherentemente en otros.
  • Mejora de la distribución y estructura de las puebas de integración.
  • Plantear las bases para que el gestor de tareas, ahora muy ligado a la idiosincrasia de la solución, viva como un framework independiente del proyecto (y reutilizable por otros proyectos que pongamos en marcha).
  • etc.

¿Qué pretendo conseguir con esto?

Cuando un proyecto crece en complejidad, cuando hablamos de cientos de miles de líneas de código, aunque esté todo realizado con extremada limpieza y con una arquitectura general y microdiseños muy cuidados, la misma distribución de los artefactos del proyecto puede ser un problema que impida llegar a los sitios con sencillez y comodidad. La espaguetización del proyecto no sólo se puede producir en su código sino también en la estructura misma de sus módulos.

De este modo, al mejorar y simplificar cómo están distribuidas y organizadas las cosas, ganamos agilidad y comodidad a la hora de trabajar en una nueva revisión. Lo que no es fácil de ver es que esta agilidad y comodidad se traduce después en ahorro de muchas horas de trabajo. Coseguimos la máxima calidad en algo cuando tenemos un entorno cómodo de usar y de trabajar. La facilidad de mantenimiento también tiene mucho que ver con esto.

Detrás de cada acto de refactorización, del tipo que sea, se esconde un ahorro de tiempo futuro al dedicar menos esfuerzo y tiempo en evolucionar una aplicación bien hecha, o lo que es lo mismo, el tiempo invertido en refactorizar se traduce en una mejor productividad (= menos recursos económicos necesarios para hacer algo).

¿Por qué leer El Libro Negro del Programador?

Adquirir desde:
Amazon (kindle eBook / papel)
CreateSpace (papel)
PayHip (epub / mobi / pdf)

El libro negro del programador.com
Segunda Edición - 2017

Archivo

Trabajo en...

Mis novelas...