Refactorización en Ágil: Cómo realizar cambios
de diseño de forma segura y eficaz

Como director de desarrollo de software, usted acaba de heredar una gran base de código y todo el mundo espera que presente nuevas funciones cruciales. Preferentemente, para ayer.

Te encantaría hacerlo. Pero ni usted ni su equipo entienden el código. ¿Podría ayudarte la refactorización del código? La refactorización de código en Ágil está de moda, después de todo. Se dice que es lo que hace que los equipos puedan responder rápidamente a los cambios. Y eso es exactamente lo que necesitas.

Pero ni tú ni tu equipo tenéis experiencia en ello. Lo que buscas es un buen comienzo, entender los fundamentos y recibir orientación sobre cómo practicarlo. Pues estás en el lugar adecuado.

Este artículo te equipará con los fundamentos adecuados, de modo que pronto estarás bien encaminado para refactorizar la base de código existente y añadir nuevas características.

Breve reseña

Con el tiempo, el diseño del software tiende a degradarse y los sistemas se vuelven cada vez más difíciles de cambiar.

La refactorización del código tiene como objetivo evitar que el software se degrade o, cuando ya se ha degradado, mejorar su diseño para que sea más fácil de entender y cambiar.

La refactorización del código es importante para eliminar los defectos de diseño, lograr la mantenibilidad y la extensibilidad de un sistema de software.

Lo más importante es que la refactorización de código cambia el diseño del código, pero nunca el comportamiento del software. Nunca se mezclarán los dos.

Breve historia: El nacimiento de la refactorización

La refactorización no fue un accidente feliz.

Los programadores han limpiado, reorganizado y reestructurado su código instintivamente desde que se escribieron los primeros programas.

A finales de la década de 1980, dos estudiantes de posgrado de informática de la época (William Opdyke en la Universidad de Illinois en Urbana-Champaign y William Griswold en la Universidad de Washington), inventaron de forma independiente lo que ahora se llama refactorización de software.

Test Driven Development (TDD)

Breve reseña

Con el tiempo, el diseño del software tiende a degradarse y los sistemas se vuelven cada vez más difíciles de cambiar.

La refactorización del código tiene como objetivo evitar que el software se degrade o, cuando ya se ha degradado, mejorar su diseño para que sea más fácil de entender y cambiar.

La refactorización del código es importante para eliminar los defectos de diseño, lograr la mantenibilidad y la extensibilidad de un sistema de software.

Lo más importante es que la refactorización de código cambia el diseño del código, pero nunca el comportamiento del software. Nunca se mezclarán los dos.

Breve historia: El nacimiento de la refactorización

La refactorización no fue un accidente feliz.

Los programadores han limpiado, reorganizado y reestructurado su código instintivamente desde que se escribieron los primeros programas.

A finales de la década de 1980, dos estudiantes de posgrado de informática de la época (William Opdyke en la Universidad de Illinois en Urbana-Champaign y William Griswold en la Universidad de Washington), inventaron de forma independiente lo que ahora se llama refactorización de software.

He aquí una breve evolución de la refactorización:

Investigador

William Griswold

  • Joven estudiante de posgrado de Informática 
  • Relativamente inexperto en trabajar fuera del ámbito académico
  • 1985-86: Comienza el programa de doctorado en la Universidad de Washington y empieza a trabajar con David Notkin

William Opdyke

  • Estudiante de ciencias de la computación.
  • Experiencia en el desarrollo de software en los Laboratorios Bell con cientos o miles de empleados, con productos con una vida útil de 10 a 20 años o más. 
  • 1988: Comienza el programa de doctorado en la Universidad de Illinois en Urbana-Champaign
Enfoque de la investigación

Evolución del software

Una vez desplegadas las aplicaciones, ya no se pueden modificar.

Cambio de software

Incluye la ingeniería de software basada en el conocimiento, la transformación de programas y la evolución del esquema de la base de datos.

Influencia temprana

1987:

  • Artículo de Tony Hoare y su colega: Leyes de la programación.
  • Principio clave: Transformar algebraicamente un diseño de un programa en cualquier otro diseño posible.

1988:

  • La propuesta de Notkin: Reestructurar los programas para mejorar su diseño.
  • Griswold amplió la propuesta de Notkin de que las reestructuraciones deben preservar el significado.

1986

  • Artículo de Fred Brooks – No Silver Bullet
  • Compartió la idea clave: «Grow-don’t build-software».
  • El curso de Ralph Johnson sobre programación orientada a objetos promueve la reutilización y la idea de mejorar el diseño del código. 
  • Otras influencias clave: un equipo de investigación en Tektronix que incluía a Kent Beck, Ward Cunningham y Rebecca Wirfs-Brock.

1990:

  • Opdyke y Johnson son autores de «Refactoring: An Aid in Designing Application Frameworks and Evaluating Object-Oriented Systems».
Conductor clave

¿Cómo construir una herramienta para apoyar la reestructuración que preserva el significado?

Cómo programar operaciones de reestructuración (refactorizaciones) que apoyen el diseño, la evolución y la reutilización de los marcos de aplicación orientados a objetos.
Contribuciones clave
  • 1991:
    • Griswold completó su disertación.
    • La herramienta de línea de comandos automatizó un modesto catálogo de transformaciones preservadoras de significado.
    • Demostró que se puede llevar a cabo una sofisticada rearquitectura de una aplicación utilizando sólo transformaciones que preservan el significado.
  • 1992:
    • Opdyke completó su disertación.
    • Qué refactorizaciones deberías aplicar en una situación determinada? 
    • Cómo, si es que puedes refactorizar con seguridad en una situación dada?
    • Demostró cómo se podían conseguir cambios funcionales significativos y mejoras de diseño a nivel de sistema componiendo una serie de cambios más primitivos.
    • Opdyke presentó a Martin Fowler su investigación sobre refactorización.

Cómo funciona

¿Qué es la refactorización?

Refactorizar el código es cambiar el código con dos restricciones cruciales:

  1. Los cambios hacen que el código sea más fácil de entender y, por tanto, más barato de modificar.
  2. Los cambios nunca modifican la funcionalidad, el comportamiento observable, del código.

Esta segunda restricción merece ser repetida: una refactorización nunca debe cambiar el comportamiento de un programa. Esto significa que si el programa se ejecuta antes y después de una refactorización con el mismo conjunto de entradas, el conjunto de valores de salida resultante será el mismo.

También oirás hablar de refactorización como sustantivo y como verbo. Y aquí tienes una definición rápida.

Refactoring (sustantivo): un cambio realizado en la estructura interna del software para hacerlo más fácil de entender y más barato de modificar sin cambiar su comportamiento observable.

Refactorizar (verbo): reestructurar el software aplicando una serie de refactorizaciones sin cambiar su comportamiento observable..

¿Por qué hay que refactorizar?

Si el código funciona, ¿la refactorización no es una operación de oro? ¿Una pérdida de tiempo? ¿Un ejercicio mental para seguir facturando por horas? ¿Un entretenimiento para intentar que su código sea el mejor desde un punto de vista purista, o hay un valor real en hacerlo?

code refactor

Resulta que la refactorización es la forma de mejorar el diseño y la calidad de tu código. Y si se deja de trabajar en el código en cuanto parece que funciona, es muy probable que no se adapte bien a futuros cambios. Y, por tanto, los cambios futuros serán más caros.

Sin el cuidado adecuado, el coste de cambiar una aplicación puede aumentar exponencialmente en proporción a su tamaño y complejidad. Con el tiempo, puede dejar de ser rentable seguir actualizando la aplicación.

La refactorización ayuda a garantizar que el código liberado sea fácil de mantener. Como dice el refrán: «Una puntada a tiempo ahorra nueve», la refactorización a tiempo facilita y abarata la incorporación de nuevas funcionalidades.

¿Cuándo hay que refactorizar?

Si su sistema tiene mucha deuda técnica, ¿debería dejar de trabajar en nuevas funcionalidades y dedicar unas semanas a la refactorización? A veces esto puede tener sentido, pero hay ciertos problemas con esta estrategia.

Ilustremos esto con una analogía:

when to refactor

Imagine que es un chef en un restaurante de lujo. Usted ha estado en el negocio durante seis meses, y ha establecido algunos clientes regulares. Sin embargo, ha estado muy ocupado y ha dejado de lado la limpieza. El estado de su cocina y de sus ollas y sartenes interfiere en su capacidad para ofrecer comidas de buen sabor antes de que sus clientes se impacienten y se marchen.

Después de unos cuantos sustos y una visita del inspector de sanidad, es hora de que depure la cocina, por así decirlo. Pero no puede cerrar su restaurante durante unas semanas mientras lo limpia todo, ¿verdad? Puede que sus clientes habituales lo toleren, pero no está garantizado y seguro que perderá muchos negocios de los transeúntes ocasionales.

Lo mismo ocurre con el desarrollo de software.

Detener las operaciones, la creación de valor con características nuevas o mejoradas, es poco probable que vaya bien con su cliente, las partes interesadas y los gerentes.

Los buenos restaurantes no funcionan así. No dejan que los problemas de limpieza se queden sin revisar hasta el punto de tener que cerrar. Nótese que he dicho buenos restaurantes. Hacen de la limpieza y el mantenimiento una parte habitual de sus operaciones.

Y, de nuevo, lo mismo se aplica al desarrollo de software.

La refactorización regular del código es como la limpieza de la cocina. Es mucho más eficaz para mantener la capacidad de responder a nuevos requisitos y ofrecer valor a sus usuarios o clientes.

¿Cuándo no hay que refactorizar?

Hay momentos en los que no se debe refactorizar.

La refactorización nunca debe cambiar el comportamiento del código.

Esto significa que debe ser capaz de verificar el comportamiento de tu código. Todo su código, no sólo las partes que ha cambiado.

Por lo tanto, no puede, y ni siquiera quiere refactorizar, cuando:

When Not to Refactor

  • Usted está en medio de un cambio funcional. Menos aún, cuando estás luchando para que ese cambio funcione. Frecuentemente resulta en bajar a una madriguera de conejo. O dos, o tres. Lo que usted quiere hacer es conseguir el programa de nuevo en un buen estado conocido. Luego refactorizarlo para que su cambio funcional sea más fácil de hacer. Y sólo entonces volver al cambio funcional.
  • Usted tiene una aplicación con una deuda técnica masiva y pocas, si alguna, pruebas automatizadas para verificar su comportamiento.
  • Lo que usted quiere hacer en este caso es aplicar una técnica llamada Golden Master. Consiste en tener un gran conjunto de entradas para ejecutar a través de la aplicación y comparar la salida antes y después de un cambio de código. De esta manera se pueden verificar bases de código que de otro modo serían «no comprobables» y se puede encontrar la manera de hacerlas más comprobables mediante la refactorización.
    Otra forma de proceder es declarar la bancarrota de la deuda técnica y crear una aplicación Strangler Fig que estrangule y sustituya a la actual.
  • Tiene que cumplir un plazo inmediato. Lo mejor es aplazar la refactorización hasta que se haya cumplido el plazo.
    Sin embargo, cuando se adquiere una deuda técnica a sabiendas, hay que pagarla justo después de la fecha límite. Eso es porque el camino más rápido a la bancarrota técnica es siempre poner la fecha límite, el corto plazo, antes de pagar la deuda, el largo plazo.

El ABC de los olores del código

Kent Beck acuñó el término code smells en la década de 1990. Los olores de código son síntomas, o señales, en el código fuente de un programa que indican un problema potencialmente más profundo.

En su libro seminal «Refactoring», Martin Fowler da una definición similar: «Un olor a código es una señal superficial que suele corresponder a un problema más profundo en el sistema».

Los olores de código no son errores.

El código es correcto y el programa funciona como debería.

En cambio, muestran debilidades en el diseño del código que pueden ralentizar el desarrollo o aumentar el riesgo de errores o fallos en el futuro.

Un ejemplo de olor a código es el «Código duplicado»: el mismo código copiado y pegado en varios lugares. Es sólo cuestión de tiempo que alguien se olvide de cambiar una de esas copias junto con sus hermanos.

La refactorización que hay que aplicar para desodorizar este olor es el «Método de Extracción»: fusionar las copias en una función o un método de la clase.

Debido a que los olores de código son «problemas esperando a ocurrir» es bueno esforzarse por conseguir cero olores de código.

El proceso de refactorización

Para minimizar la probabilidad de que introduzca accidentalmente errores como parte de su refactorización, debe seguir un proceso estricto.

refactoring process

  1. Asegúrese de que puede retroceder – restaurar a una versión que se ha demostrado que funciona correctamente. Asegúrate de que has confirmado todos tus cambios y que todas las pruebas contra el código confirmado tienen éxito. De esta manera, puedes restaurar a este punto si tu refactorización no va como habías previsto.
  2. Identifica qué quieres refactorizar y cómo – qué refactorizaciones utilizar.
  3. Si tienes múltiples refactorizaciones que ejecutar para lograr una reestructuración mayor, puedes opcionalmente, seleccionar un subconjunto de pruebas automatizadas para verificar el comportamiento sin cambios después de cada refactorización individual.
  4. Iterativamente: aplique una refactorización y verifique que el comportamiento no ha cambiado. Si las pruebas muestran que sí has cambiado el comportamiento, cambia el código, nunca las pruebas.
  5. Si has utilizado un subconjunto de pruebas automatizadas durante el proceso de refactorización, ejecuta todas las pruebas para verificar que el comportamiento no ha cambiado en toda la aplicación.
  6. De nuevo, si algo se rompe, cambia el código para que las pruebas pasen, no cambies las pruebas.
  7. Si te das cuenta de que no vas a ser capaz de conseguir que las pruebas pasen de nuevo en un tiempo razonable, restaura el código de trabajo que tenías antes del proceso de refactorización.
  8. Evalúa el efecto de las refactorizaciones en las características de calidad del software (por ejemplo, complejidad, comprensibilidad, mantenibilidad) o del proceso (por ejemplo, productividad, coste, esfuerzo).
  9. Si no son satisfactorios y no se pueden mejorar fácilmente, restablece el código de trabajo que tenías antes del proceso de refactorización.

 

¿Por qué se considera peligrosa la refactorización?

No lo es. Nunca he oído que sea peligroso.

Refactoring dangers

Sin embargo, hay algunos errores que podrías cometer:

  • No usa herramientas de refactorización automatizadas y no tiene suficiente cobertura de pruebas. Puede que no termine una refactorización correctamente.
  • Puede que no utilice la técnica Golden Master. El código que no tiene pruebas a menudo no está diseñado para ser comprobable. Para hacer que el código sea comprobable, tienes que refactorizarlo. ¿Se da cuenta de que esto parece una tarea imposible? Y la solución: La prueba Golden Master allana el camino para poder empezar a refactorizar con seguridad.
  • Puede que no sea consciente del «acceso reflexivo» a algunas de las cosas (supuestamente) privadas que has cambiado. Algún otro código podría estar saltándose los controles de acceso.
  • Usted va más allá de una simple refactorización y termina en el territorio del rediseño. Cuando se confunden las dos actividades, lo más probable es que se cometan errores en ambas.
  • Usted «refactoriza» una API pública publicada – excepto que no completó la tarea porque no tiene acceso a los clientes que llaman. Sólo se rompen.

La refactorización no es más peligrosa que cualquier otra práctica de codificación, en realidad. Hay que ser consciente de lo que puede salir mal y tomar medidas para evitarlo.

Un ejemplo rápido: Método de extracción

Problema

Tiene un fragmento de código que se puede agrupar.

————————————————–

void PrintOwing()

{

  this.PrintBanner();

  // Print details.

  Console.WriteLine(“name: ” + this.name);

Console.WriteLine(“amount:”+ this.GetOutstanding());

}

————————————————–

Solución

Mueva este código a un nuevo método (o función) independiente y sustituya el código antiguo por una llamada al método.

————————————————–

void PrintOwing()

{

this.PrintBanner();

this.PrintDetails();

}

void PrintDetails()

{

Console.WriteLine(“name: ” + this.name);

Console.WriteLine(“amount: ” + this.GetOutstanding());

}

————————————————–

¿Por qué refactorizar?

  • Cuantas más líneas se encuentren en un método, más difícil será averiguar qué hace el método. Esta es la razón principal de esta refactorización.
  • Además de eliminar las asperezas de su código, la extracción de métodos es también un paso en muchos otros enfoques de refactorización.

Beneficios

  • ¡Código más legible! Asegúrese de dar al nuevo método un nombre que describa el propósito del método: createOrder(), renderCustomerInfo(), etc.
  • Mismo nivel de abstracción en ambos métodos, lo que ayuda a la comprensión del código.
  • Menos duplicación de código. A menudo el código que se encuentra en un método puede ser reutilizado en otros lugares de tu programa. Así que puedes reemplazar los duplicados con llamadas a tu nuevo método.
  • Aísla partes independientes del código, lo que significa que los errores son menos probables (como si se modifica la variable equivocada).

Más información

  1. W.F. Opdyke y R.E. Johnson, «Refactoring: An Aid in Designing Application Frameworks and Evolving Object-Oriented Systems», Proc. 1990 Symp. Object-Oriented Programming Emphasizing Practical Applications (SOOPPA 90), 1990, pp. 274-282.
  2. W.G. Griswold y D. Notkin, «Automated Assistance for Program Restructuring», ACM Trans. Software Eng. Methodology, vol. 2, no. 3, 1993, pp. 228-269.
  3. W.G. Griswold y D. Notkin, «Architectural Tradeoffs for a Meaning-Preserving Program Restructuring Tool», IEEE Trans. Software Eng., vol. 21, no. 4, 1995, pp. 275-287.
  4. W.F. Opdyke, «Refactoring Object-Oriented Frameworks», tesis doctoral, Departamento de Informática, Universidad de Illinois en Urbana-Champaign, 1992.
  5. M. Fowler y otros, Refactoring: Improving the Design of Existing Code, Addison-Wesley Longman, 1999.
  6. K. Beck, Extreme Programming Explained: Embrace Change, Addison-Wesley Longman, 1999.

Iniciar la refactorización

¿Recuerda ese código base que heredó? ¿La que ni usted ni su equipo entienden? ¿Que le hizo preguntarse cómo cumplir con las expectativas de todos en cuanto a nuevas características?

Ahora ya sabe cómo abordar esa base de código con confianza.

Si hay pocas o ninguna prueba, el primer paso es crear un Golden Master.

Cuando tenga ese Golden Master, o si tiene la suerte de que el código base tenga una buena cobertura de pruebas, puede proceder a la refactorización para facilitar la adición de nuevas características, y a la adición de esas nuevas características. Todo ello sin cerrar el negocio.

Así que, adelante, empieza a refactorizar y a añadir las características que todo el mundo está esperando.

Refactorización en Ágil

¡La vida es buena cuando sus equipos ágiles están sincronizados!

Solicite una demostración personalizada de SwiftEnterprise.