técnicas sinonimo refactory refactorizar refactorización refactorizacion ejemplos codigo book refactoring project-management legacy

refactoring - sinonimo - ¿Cómo abordar un gran proyecto de refactorización?



refactory (3)

Estoy a punto de comenzar a planificar una refactorización importante de nuestro código base, y me gustaría obtener algunas opiniones y respuestas a algunas preguntas (he visto bastantes discusiones sobre temas similares, como https://stackoverflow.com/questions/108141/how-do-i-work-effectively-with-very-messy-legacy-code , Estrategia para refactorización a gran escala , pero tengo algunas preguntas específicas (en la parte inferior):

Desarrollamos una aplicación compleja. Hay unos 25 desarrolladores trabajando el código base. El total de años de trabajo del producto hasta la fecha es de aproximadamente 150. El código base actual es un proyecto único, construido con ant. El objetivo de alto nivel del proyecto en el que me estoy embarcando es modular la base de código en sus diversas infraestructuras y componentes aplicativos. Actualmente no hay una buena separación entre los diversos componentes lógicos, por lo que está claro que cualquier esfuerzo de modularización deberá incluir algunas definiciones de API y un desentrañamiento serio para permitir la separación. Los estándares de calidad son bajos: casi no hay pruebas y, definitivamente, no se ejecutan pruebas como parte del proceso de compilación.

Otro punto muy importante es que este proyecto debe realizarse en paralelo al desarrollo activo del producto y las versiones que se envían a los clientes.

Objetivos del proyecto:

  • Permite la reutilización de componentes en diferentes proyectos.
  • aplicación separada de la infraestructura, y les permite evolucionar de forma independiente
  • mejorar la capacidad de prueba (mediante la creación de API)
  • simplificar el env de los desarrolladores (menos el código extraído y compilado)

Mis pensamientos y preguntas:

  1. ¿Qué piensas sobre los objetivos del proyecto? ¿Algo que cambiarías?
  2. ¿Tienes experiencia con este tipo de proyectos? ¿Cuáles serían algunas recomendaciones?
  3. Estoy muy preocupado por la falta de pruebas; por lo tanto, no tengo control para saber que el proceso de refactorización no está rompiendo nada a medida que avanzo. Este es un problema 22, porque uno de los objetivos de este proyecto es hacer que nuestro código sea más comprobable ...
  4. Estaba muy influenciado por Michael Feathers '' Working Effectively With Legacy Code . De acuerdo con esto, un enfoque de abajo hacia arriba es la manera de resolver mi problema: no me lance de cabeza a la base de código y trate de solucionarlo, sino que comience poco a poco agregando pruebas unitarias al nuevo código durante varios meses y vea cómo funciona. el código (y el equipo) se vuelven mucho mejores, en la medida en que surgirán abstracciones, surgirán las API, etc., y esencialmente, la modularización comenzará a ocurrir por sí misma. ¿Alguien tiene experiencia con tal dirección? Como se ve en muchas otras preguntas sobre este tema, el principal problema aquí es la incredulidad gerencial. "¿De qué manera las pruebas clase por clase (y dedicar mucho tiempo a hacerlo) nos llevarán a un sistema estable? Es una buena teoría que no funciona en la vida real". ¿Algún consejo para vender esto?

Bueno, supongo que es mejor ahora que después, pero definitivamente tienes una tarea por delante. Una vez formé parte de un equipo de tres responsables de refactorizar un producto de tamaño similar. Era un código de procedimiento, pero describiré algunos de los problemas que tuvimos que se aplicarán de manera similar.

Comenzamos por la parte inferior y comenzamos a incorporarnos seleccionando funciones que deberían haber sido altamente reutilizables pero que no lo eran. Escribiríamos un montón de pruebas unitarias en el código existente (¡ninguna existía en absoluto!), Pero en poco tiempo enfrentamos nuestro primer gran problema: el código existente tenía errores que habían estado inactivos.

¿Los arreglamos? Si lo hacemos, entonces hemos ido más allá de una refactorización. Así que registramos un problema con el código existente con la esperanza de obtener una base de código fija y recién probada, pero, por supuesto, la gerencia decidió que había prioridades más importantes que la solución de errores que nunca habían aparecido. Comprensible.

Así que pensamos que intentaríamos corregir los errores en nuestro nuevo código. Luego descubrimos que estos errores en el código original hicieron que otros códigos funcionaran, por lo que realmente eran "errores conceptuales" en lugar de "errores funcionales". Bien quizás. Ocasionalmente hubo espasmos intermitentes en el software original que nunca se habían rastreado.

Entonces cambiamos de rumbo y decidimos mantener los errores en su lugar, como debería hacer una verdadera refactorización. Es fácil introducir errores sin querer, ¡es mucho más difícil hacerlo intencionalmente!

El siguiente problema fue que el código estaba en un desorden que las pruebas unitarias iniciales que escribimos tuvieron que cambiar sustancialmente para atender la refactorización. En otras palabras, dos blancos en movimiento. No está bien. El mero hecho de escribir las pruebas demoraba años y nos hacía creer en el valor del proyecto. Realmente era algo de lo que solo querías alejarte.

Al final descubrimos que realmente teníamos que reducir el alcance de la refactorización si íbamos a terminar este milenio, lo que significaba que la base de código que soñábamos no se lograría. Declaramos que la solución más factible era simplemente limpiar y recortar el código y al menos hacerlo conceptualmente más fácil de entender para que los futuros desarrolladores lo modifiquen.

La reducción de los beneficios de la refactorización limitada no fue considerada como un esfuerzo valioso por parte de la administración, y dado que se encontraron problemas similares de confiabilidad en la plataforma de hardware (proyecto integrado), la compañía decidió que era su oportunidad de renovar todo el producto, con el software Escrito desde cero, nuevo lenguaje, objetos. Solo las amplias especificaciones de prueba del sistema implementadas desde el producto original hicieron que esto tuviera una oportunidad.


Claramente, la ausencia de pruebas pondrá a las personas nerviosas cuando intentes refactorizar el código. ¿Dónde obtendrá alguien fe en que su refactorización no rompa la aplicación? Creo que la mayoría de las respuestas que obtendrás serán "esto va a ser muy difícil y no muy exitoso", y esto se debe principalmente a que estás enfrentando una gran tarea manual y no confías en la respuesta.

Solo hay dos salidas.

  • Construye un montón de pruebas. Desafortunadamente, esto costará mucho tiempo y la mayoría de los gerentes no ven ningún valor; después de todo, te has llevado bien sin ellos hasta ahora. Señalar de nuevo la pregunta de la fe no ayudará; Todavía estás usando mucho tiempo antes de que suceda algo útil. Si le permiten crear pruebas, tendrá el problema de evolucionar las pruebas a medida que se refactoriza; es posible que no cambien la funcionalidad en un bit, pero a medida que desarrolle nuevas API, las pruebas tendrán que cambiar para que coincidan con las nuevas API. Eso es un trabajo adicional más allá de refactorizar el código base.

  • Automatizar el proceso de refactorización. Si aplica transformaciones automatizadas confiables, puede argumentar (a menudo sin éxito) que el código refactorizado conserva la función original del sistema. La forma de vencer el argumento no exitoso es escribir esas pruebas (ver primer método) y aplicar el proceso de refactorización a la aplicación y las pruebas; A medida que la aplicación cambia de estructura, las pruebas también tienen que cambiar. Pero son solo códigos de aplicación desde el punto de vista de la maquinaria automatizada.

No mucha gente hace esto último; ¿De dónde sacas las herramientas que pueden hacer tales cosas?

De hecho, tales herramientas existen. Se denominan herramientas de transformación de programas y se utilizan para llevar a cabo transformaciones masivas en el código. Piensa en esto como herramientas para refactorizar literalmente lo grande; Debido a la escala, tienden a no ser interactivos.

Se requiere esfuerzo para configurarlos para la tarea en cuestión; Tienes que escribir reglas personalizadas para lograr el resultado deseado personalizado. Es probable que no pueda hacer esto en una semana, pero es mucho menos trabajo que modificar manualmente un sistema grande. Y debe considerar que tiene 150 años-hombre invertidos en el software existente; tomó tanto tiempo para hacer el lío. Parece razonable que "algún" pequeño esfuerzo en comparación debería estar bien.

Solo conozco 3 de estas herramientas que tienen la posibilidad de trabajar en código real: TXL , Stratego / XT , y nuestra herramienta, el kit de herramientas de reingeniería de software DMS . Los dos primeros son productos académicos (aunque TXL se ha utilizado para actividades comerciales en el pasado); DMS es comercial.

DMS se ha utilizado para una amplia variedad de análisis de software a gran escala y tareas de transformación masiva. Una tarea fue la traducción automática entre idiomas para el B-2 Stealth Bomber . Otro, mucho más cercano a su problema de refactorización, fue la arquitectura automatizada de un sistema a gran escala basado en componentes C ++ para componentes, desde un RTOS heredado con sus reglas idiosincrásicas sobre cómo se organizan los componentes, hasta CORBA / RT en el que las API de componentes tenían para ser cambiado de estructuras ad hoc a facetas de estilo CORBA y interfaces de receptáculos , así como el uso de los servicios CORBA / RT en lugar de los servicios heredados de RTOS. (Estas tareas se realizaron con 1 o 2 años hombre de esfuerzo real, por parte de tipos muy inteligentes y expertos en DMS).

Todavía está el problema de la construcción de la prueba (estos dos ejemplos anteriores ya tenían excelentes pruebas del sistema). Aquí voy a arriesgarme. Creo que hay esperanza en obtener tales herramientas para automatizar la generación de pruebas mediante la instrumentación de un código de ejecución para recopilar los resultados de entrada-salida de la función. Hemos creado todo tipo de instrumentación para el código fuente (obviamente, hay que compilarlo después de la instrumentación) y creemos que sabemos cómo hacerlo. YMMV.

Hay algo que haces que es considerablemente menos ambicioso: identificar las partes reutilizables del código, descubriendo qué se ha reutilizado en el código. La mayoría de los sistemas de software contienen una gran cantidad de código clonado (nuestra experiencia es del 10-20% [y me sorprende el informe de PHP de números más pequeños en otra respuesta; sospecho que están usando un detector de clon débil). El código clonado es un indicio de falta de abstracción en el software de la aplicación. Si puede encontrar los clones y ver cómo varían, puede ver fácilmente cómo abstraerlos en funciones (o lo que sea) para hacerlos explícitos y reutilizables.

Salion Inc. hizo la detección de clones y la abstracción . El papel no explora la actividad de abstracción; Lo que realmente hizo Salion fue una revisión periódica de los clones detectados, y la remediación manual de los atroces o los que tenían sentido en los métodos (a menudo de biblioteca). El resultado neto fue que el código base en realidad se redujo de tamaño y los programadores se volvieron más efectivos porque tenían bibliotecas mejores ("más reutilizables").

Utilizaron nuestro CloneDR , una herramienta para encontrar clones usando la sintaxis del programa como guía. CloneDR encuentra clones exactos y casi fallos (reemplazo de identificadores o declaraciones) y proporciona una lista específica de lugares de clonación y paramerizaciones de clonación, independientemente del diseño y los comentarios. Puede ver los informes de clonación para varios idiomas en el enlace. (Soy el autor y autor de CloneDR entre mis muchos sombreros).

Con respecto al "porcentaje de clones pequeños" para el proyecto PHP discutido en otra respuesta: No sé qué se estaba usando para un detector de clones. El único detector de clones centrado en PHP que conozco es PHPCPD , que en mi humilde opinión es un terrible detector de clones; solo encuentra clones exactos si entiendo la implementación reclamada. Vea el ejemplo de PHP en nuestro sitio para propósitos comparativos.


Esto es exactamente lo que hemos estado haciendo para web2project durante los últimos dos años. Bifurcamos de un sistema existente (dotproject) que tenía métricas terribles como alta complejidad ciclomática (bajo: 17, promedio: 27, alto: 195M), mucho de código duplicado (8% del código general) y cero pruebas.

Desde la división, hemos reducido el código duplicado (2.1% en total), reducido el código total (200kloc a 155kloc), agregamos casi 500 pruebas de unidad y mejoramos la complejidad ciclomática (bajo: 1, promedio: 11, alto: 145M). Sí, todavía tenemos mucho camino por recorrer.

Nuestra estrategia se detalla en mis diapositivas aquí: http://caseysoftware.com/blog/phpbenelux-2011-recap - Proyecto Triage & Recovery; y aquí: http://www.phparch.com/2010/11/codeworks-2010-slides/ - Unit Testing Strategies; y en varias publicaciones como esta: http://caseysoftware.com/blog/technical-debt-doesn039t-disappear

Y solo para advertirte ... no es divertido al principio. Puede ser divertido y satisfactorio una vez que tus métricas comiencen a mejorar, pero eso toma un tiempo.

Buena suerte.