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

Software Dev
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.

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.

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?

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.

Imagine that you’re a chef in a fancy restaurant. You’ve been in business for six months, and you’ve established some regular customers. Things have been pretty busy, however, and you let cleaning slip. The state of your kitchen and your pots and pans interferes with your ability to deliver good-tasting meals before your guests get impatient and leave.

After a few close calls and a visit from the health inspector, it’s time that you debugged the kitchen, as it were. But you can’t just shut down your restaurant for a few weeks while you get everything cleaned up, can you? Your regulars might tolerate it, but it’s not guaranteed and you’ll definitely lose a lot of business from casual passers-by.

The same applies to software development.

Shutting down operations, the creation of value with new or improved features, is unlikely to go well with your client, stakeholders, and managers.

Good restaurants don’t operate like this. They don’t let cleanliness issues go unchecked to the point where they need to shut down. Note that I said good restaurants. They make cleanliness and maintenance a regular part of their ongoing operations.

And, again, the same applies to software development.

Regular code refactoring is like cleaning your kitchen. It’s far more effective in keeping you able to respond to new requirements and delivering value to your users or customers.

When Not to Refactor?

There are times when you shouldn’t refactor.

Refactoring should never change the behavior of the code.

This means you need to be able to verify the behavior of your code. All of your code, not just the bits you’ve changed.

Therefore you can’t, and don’t even want to refactor, when:

¿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:

  • 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.

  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.

 

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());

}

}

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.

Signup

Signup for updates!

[wpforms id=»8824″]

As a software development manager you just inherited a large codebase and everyone is expecting you to come up with crucial new features.

Preferably yesterday.

You’d love to do it. But neither you nor your team understands the code. Could code refactoring help you? Code refactoring in agile is hot after all. You hear it’s what keeps teams able to respond to change quickly.

And that’s exactly what you need.

But neither you nor your team have experience with it. You’re looking to get a good start, to understand the fundamentals and receive guidance on how to practice it.

Well, you’re in the right place.

This article will equip you with the right basics, so you’ll soon be well on your way refactoring the existing code base and adding new features.

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

Solicite una demostración personalizada de SwiftEnterprise.