refactoring - software - refactorizacion java
Mejorando sistemas realmente malos (9)
- Apaga los fuegos Si hay problemas de prioridad crítica, sean lo que sean, primero debe manejarlos. Instálalo si debes, con una base de código maloliente está bien. Sabes que lo mejorarás en el futuro. Esta es su técnica de ventas dirigida a quien informa.
- Escoja alguna fruta baja. Supongo que es relativamente nuevo en este software en particular y que se le volvió a asignar la tarea de manejarlo. Encuentre algunos problemas aparentemente fáciles en un subsistema relacionado del código que no debería tomar más de un día o dos para resolver cada uno y solucionarlos. Esto puede implicar refactorización, o puede que no. El objetivo es familiarizarse con el sistema y con el estilo del autor original. Puede que no sea realmente afortunado (uno de los dos incompetentes que trabajaba en mi sistema antes que yo siempre publicaba sus comentarios con cuatro signos de puntuación en lugar de uno, lo que hacía muy fácil distinguir quién escribía el segmento particular de código). pero desarrollarás una idea de las debilidades del autor para que sepas qué buscar. Acoplamiento extenso y estricto con el estado global frente a la escasa comprensión de las herramientas de lenguaje, por ejemplo.
- Establecer un gran objetivo. Si su experiencia es paralela a la mía, se encontrará en un trozo particular de código de spaghetti cada vez más a medida que realiza el paso anterior. Este es el primer nudo que necesitas desenredar. Con la experiencia adquirida entendiendo el componente y el conocimiento sobre lo que el autor original probablemente hizo mal (y por lo tanto, sobre lo que debe tener cuidado), puede comenzar a imaginar un modelo mejor para este subconjunto del sistema. No se preocupe si aún tiene que mantener algunas interfaces desordenadas para mantener la funcionalidad, simplemente siga paso a paso.
Enjabona, enjuaga, repite! :)
Con tiempo, considere agregar pruebas unitarias para su nuevo modelo un nivel debajo de sus interfaces con el resto del sistema. No grabe las interfaces malas en el código a través de pruebas que las usan, las cambiará en una iteración futura.
Abordando los problemas particulares que menciona:
Cuando se encuentre con una situación en la que los usuarios estén trabajando de forma manual, hable con los usuarios para cambiarla. Verifique que aceptarán el cambio si lo proporciona antes de dedicarle tiempo. Si no quieren el cambio, su trabajo es mantener el comportamiento roto.
Cuando se encuentra con un componente defectuoso que muchos otros componentes han funcionado, sugiero una técnica de componentes paralelos. Crea un contador que funcione como debería funcionar el existente. Proporcione una interfaz similar (o, si es práctico, idéntica) y deslice el nuevo componente en la base de código. Cuando toca componentes externos que funcionan alrededor del roto, intente reemplazar el componente anterior por el nuevo. Interfaces similares facilitan la transferencia del código, y el componente anterior todavía está presente si falla el nuevo. No elimine el componente viejo hasta que pueda.
¿Cómo comenzarías a mejorar en un sistema realmente malo?
Permítanme explicar a qué me refiero antes de recomendar la creación de pruebas unitarias y la refactorización. Podría usar esas técnicas, pero eso sería inútil en este caso.
En realidad, el sistema está tan roto que no hace lo que tiene que hacer.
Por ejemplo, el sistema debe contar cuántos mensajes envía. Funciona principalmente, pero en algunos casos "olvida" aumentar el valor del contador de mensajes. El problema es que tantos otros módulos con sus propias soluciones se basan en este contador que si corrijo el contador, el sistema en su conjunto empeoraría de lo que es actualmente. La solución podría ser modificar todos los módulos y eliminar sus propias correcciones, pero con más de 150 módulos que requerirían tanta coordinación que no puedo pagarlos.
Peor aún, hay algunos problemas que tienen soluciones no en el sistema en sí, sino en la cabeza de la gente. Por ejemplo, el sistema no puede representar más de cuatro mensajes relacionados en un grupo de mensajes. Algunos servicios requerirían cinco mensajes agrupados. El departamento de contabilidad conoce esta limitación y cada vez que cuentan los mensajes para estos servicios, cuentan los grupos de mensajes y los multiplican por 5/4 para obtener la cantidad correcta de mensajes. No hay absolutamente ninguna documentación sobre estas desviaciones y nadie sabe cuántas cosas están presentes en el sistema ahora.
Entonces, ¿cómo comenzarías a trabajar para mejorar este sistema? ¿Qué estrategia seguirías?
Algunas cosas adicionales: soy un ejército de hombres trabajando en esto, así que no es una respuesta aceptable contratar suficientes hombres y rediseñar / refactorizar el sistema. Y en unas pocas semanas o meses, realmente debería mostrar una progresión visible, por lo que tampoco es una opción hacer la refactorización en un par de años.
Algunos detalles técnicos: el sistema está escrito en Java y PHP, pero no creo que eso realmente importe. Hay dos bases de datos detrás, una de Oracle y una de PostgreSQL. Además de los defectos mencionados antes, el código también huele, está mal escrito y documentado.
Información adicional:
El problema del contador no es un problema de sincronización. Las instrucciones de contador ++ se agregan a algunos módulos y no se agregan a otros módulos. Una solución rápida y sucia es agregarlos donde faltan. La solución larga es hacer que sea un aspecto de los módulos que lo necesitan, lo que hace imposible olvidarlo más adelante. No tengo problemas para arreglar cosas como esta, pero si hiciera este cambio rompería otros 10 módulos.
Actualizar:
Acepté la respuesta de Greg D''s. Incluso si me gusta más Adam Bellaire, no me ayudaría a saber qué sería ideal saber. Gracias por todas las respuestas.
¿Qué se te pide ahora? ¿Le piden que implemente la funcionalidad o corrija errores? ¿Saben siquiera lo que quieren que hagas?
Si no tiene la mano de obra, el tiempo o los recursos para "arreglar" el sistema como un todo, entonces todo lo que puede hacer es sacar agua. Estás diciendo que deberías poder hacer un "progreso visible" dentro de unos meses. Bueno, con el sistema siendo tan malo como lo describiste, en realidad puedes empeorar el sistema. Presionado para hacer algo notable, simplemente agregará código y hará que el sistema sea aún más intrincado.
Necesitas refactorizar, eventualmente. No hay manera de evitarlo. Si puede encontrar una forma de refactorización que sea visible para sus usuarios finales, sería ideal , incluso si toma entre 6 y 9 meses o un año en lugar de "unos pocos meses". Pero si no puedes, entonces tienes que elegir:
- Refactor, y arriesgarse a ser visto como "no lograr nada" a pesar de sus esfuerzos
- No refactorice, logre objetivos "visibles" y haga que el sistema sea más intrincado y más difícil de refactorizar algún día. (Tal vez después de encontrar un trabajo mejor, y esperar que el próximo desarrollador que venga nunca pueda descubrir dónde vives).
Cuál es el más beneficioso para usted personalmente depende de la cultura de su empresa. ¿Algún día decidirán contratar más desarrolladores o reemplazarán este sistema por completo con algún otro producto?
Por el contrario, si tus esfuerzos por "arreglar las cosas" realmente rompen otras cosas, ¿entenderán la monstruosidad que te piden que abordes por tu cuenta?
No hay respuestas fáciles aquí, lo siento. Debe evaluar en función de su situación individual e individual.
Abre el directorio que contiene este sistema con Windows Explorer. Luego, presione Ctrl-A, y luego Shift-Delete. Eso suena como una mejora en tu caso.
En serio, ese contador parece que tiene problemas de seguridad en el hilo. Pondría un candado alrededor de las funciones crecientes.
Y con respecto al resto del sistema, no puedes hacer lo imposible, así que intenta hacer lo posible. Debes atacar tu sistema desde dos frentes. Primero, ocúpese de los problemas más visiblemente problemáticos, para que pueda mostrar el progreso. Al mismo tiempo, debe lidiar con los problemas más infraestructurales, para que tenga la oportunidad de arreglar esto algún día.
Buena suerte, y que la fuente esté contigo.
Bueno, necesitas comenzar en algún lado, y parece que hay errores que deben corregirse. Trabajaría con esos errores, haré refactorizaciones rápidas y escribiré pruebas de unidades posibles en el camino. También usaría una herramienta como SourceMonitor para identificar algunas de las partes más complejas del código en el sistema y ver si podría simplificar su diseño de alguna manera. En última instancia, solo tienes que aceptar que será un proceso lento y dar pequeños pasos hacia un mejor sistema.
Escoge un área que sería de mediana dificultad para refactorizar. Cree un esqueleto del código original con solo las firmas de métodos de las existentes; tal vez usar una interfaz incluso. Entonces comienza a piratear. Incluso puede señalar los métodos "nuevos" a los antiguos hasta que llegue a ellos.
Luego, pruebas, pruebas, pruebas. Dado que no hay pruebas de unidades, ¿tal vez solo use pruebas antiguas (personas) activadas por voz? O escribe tus propias pruebas sobre la marcha.
Documente su progreso a medida que avanza en algún tipo de repositorio, incluyendo frustraciones y preguntas, para que cuando el próximo pobre idiota que obtiene este proyecto no esté donde está :).
Una vez que haya terminado la primera parte, pase a la siguiente. La clave es construir sobre el progreso incremental, es por eso que no debes comenzar con la parte más difícil primero; será demasiado fácil desmoralizarse.
Joel tiene un par de artículos sobre reescritura / refactorización:
Este es un libro completo que básicamente dirá unit test y refactor, pero con más consejos prácticos sobre cómo hacerlo
http://ecx.images-amazon.com/images/I/51RCXGPXQ8L._SL500_AA240_.jpg
http://www.amazon.com/Working-Effectively-Legacy-Robert-Martin/dp/0131177052
He estado trabajando con un sistema heredado con las mismas características durante casi tres años, y no hay atajos de los que tenga conocimiento.
Lo que más me molesta de nuestro sistema heredado es que no puedo corregir algunos errores, ya que muchas otras funciones podrían fallar si los soluciono. Esto requiere soluciones temporales feas o crear nuevas versiones de funciones antiguas. Las llamadas a las funciones anteriores pueden reemplazarse por las nuevas a la vez (durante la prueba).
No estoy seguro de cuál es el objetivo de su tarea, pero le recomiendo que toque la menor cantidad de código posible. Solo haz lo que necesites hacer.
Es posible que desee documentar todo lo posible entrevistando a personas. Esta es una gran tarea, ya que no sabe qué preguntas hacer, y la gente habrá olvidado muchos detalles.
Aparte de eso: asegúrese de que le paguen y suficiente apoyo moral. Habrá llanto y crujir de dientes ...
Intentaría elegir una parte del sistema que pudiera extraerse y reescribirse aisladamente con bastante rapidez. Incluso si no hace mucho, puede mostrar el progreso bastante rápido, y no tiene el problema de interactuar directamente con el código heredado.
Afortunadamente, si pudiera elegir algunas de esas tareas, lo verán avanzar visiblemente, y podría presentar un argumento para contratar a más personas para que reescriban los módulos más grandes. Cuando partes del sistema confían en un comportamiento defectuoso, no tienes más remedio que separarte antes de arreglar algo.
Con suerte, puedes construir gradualmente un equipo capaz de reescribir todo el lote.
Todo esto tendría que ir de la mano con un entrenamiento decente, de lo contrario los viejos hábitos de las personas se mantendrán, y su trabajo tendrá la culpa cuando las cosas no funcionen como se esperaba.
¡Buena suerte!
Olvida todo lo que existe actualmente que tiene problemas y escribe otros que funcionen correctamente. Documente todo lo que pueda sobre lo que cambiará y coloque grandes letreros rojos intermitentes por todo el lugar que apuntan a esta documentación.
Al hacerlo de esa manera, puede mantener sus errores existentes (los que están siendo compensados en otro lugar) sin ralentizar su progreso hacia la obtención de un sistema real de trabajo.