Mantenimiento del código: ¿mantener un mal patrón cuando se extiende un nuevo código para ser consistente o no?

45

Tengo que extender un módulo existente de un proyecto. No me gusta cómo se ha hecho (hay muchos antipatrones involucrados, como código copiado / pegado). No quiero realizar una refactorización completa por muchas razones.

Debería:

  • crear nuevos métodos usando la convención existente, incluso si me parece mal, para evitar confusiones para el próximo mantenedor y ser coherente con la base del código?

o

  • ¿Trato de usar lo que siento mejor incluso si está introduciendo otro patrón en el código?

Precisión editada después de las primeras respuestas:

El código existente no es un desastre. Es fácil de seguir y entender. PERO está introduciendo un montón de código repetitivo que se puede evitar con un buen diseño (el código resultante podría ser más difícil de seguir entonces). En mi caso actual, es un buen módulo DAO JDBC (plantilla interna de resorte), pero ya me he encontrado con este dilema y estoy buscando otros comentarios de desarrollo.

No quiero refactorizar porque no tengo tiempo. E incluso con el tiempo será difícil justificar que todo un módulo que funcione perfectamente necesita ser refactorizado. El costo de refactorización será más pesado que sus beneficios. Recuerde: el código no es desordenado ni demasiado complejo. No puedo extraer algunos métodos allí e introducir una clase abstracta aquí. Es más una falla en el diseño (creo que es el resultado del extremo 'Keep It Stupid Simple')

Entonces, la pregunta también se puede hacer así:
usted, como desarrollador, ¿prefiere mantener un código aburrido y estúpido fácil O tener algunos ayudantes que hagan el código aburrido estúpido en su lugar?

La desventaja de la última posibilidad es que tendrá que aprender algunas cosas y tal vez también tendrá que mantener el código aburrido y estúpido hasta que se realice una refactorización completa)

Guillaume
fuente
Pregunta muy interesante!
Philippe
1
Mantener un código estúpido aburrido y fácil es, por definición, fácil. Prefiero tener una vida fácil y salir temprano cada día.
rápidamente_ahora
Sí, pero soy demasiado vago para hacer un código estúpido y aburrido :) ¡Prefiero trabajar duro durante 2 o 3 días para construir algo que haga esta parte por mí!
Guillaume
1
¿Cuándo se confundieron lo estúpido o lo aburrido?
Erik Reppen
Hola Erik, a veces un código que podría haber sido factorizado es más simple de leer: es docena de veces lo mismo y el código es sencillo. Puede leerlo en una fila sin pensarlo y puede depurarlo de forma lineal. No hay complejidad oculta.
Guillaume

Respuestas:

42

La refactorización se realiza mejor en pequeños pasos, y preferiblemente solo si tiene pruebas unitarias para cubrir el código. (Por lo tanto, si aún no tiene pruebas, trate de escribirlas primero y, hasta entonces, siga las refactorizaciones más simples, más infalibles, preferiblemente automatizadas. Una gran ayuda en esto es Trabajar eficazmente con código heredado de Michael Feathers).

En general, intenta mejorar un poco el código cada vez que lo toques. Siga la Regla de Boy Scout ( acuñada por Robert C. Martin ) dejando el código más limpio de lo que lo encontró. Cuando agregue un nuevo código, intente mantenerlo separado del código incorrecto existente. Por ejemplo, no lo entierre en medio de un método largo, en su lugar, agregue una llamada a un método separado y coloque su nuevo código allí. De esta manera, creces islas gradualmente más grandes de código limpio (er) dentro de la base de código existente.

Actualizar

El costo de refactorización será más pesado que sus beneficios. [...] Usted, como desarrollador, ¿prefiere mantener un código aburrido y estúpido O tener algunos ayudantes que hagan el código aburrido estúpido en su lugar?

Hice hincapié en lo que creo que es el punto clave aquí. Siempre vale la pena evaluar los costos y beneficios de la refactorización antes de saltar a ella. Como en su caso, la mayoría de nosotros tenemos recursos limitados para refactorizar, por lo que debemos usarlos con prudencia. Dedique ese precioso tiempo a la refactorización, donde brinda los mayores beneficios con el menor esfuerzo.

Como mente creativa, por supuesto que preferiría producir un código perfecto, hermoso y elegante, y reescribir todo lo que no se parezca a mis ideales :-) Sin embargo, en realidad, me pagan para producir software que resuelva problemas reales para sus usuarios, así que debería pensar en producir el mayor valor por su dinero a largo plazo.

El beneficio de la refactorización solo aparece si hay ahorros suficientes en tiempo y esfuerzos para comprender, mantener, corregir y extender el código a largo plazo . Entonces, si un fragmento de código, por feo que sea, rara vez o nunca se toca, no hay errores conocidos en él y no conozco ninguna característica próxima en el futuro previsible que requiera que lo toque, prefiero dejarlo en paz

Péter Török
fuente
77
Es tan fácil caer en la trampa de 'el código ya apesta, también podría seguir funcionando ...' Los buenos programadores no lo hacen. Buena respuesta.
sevenseacat
Aunque es muy probable que sea el enfoque más seguro (se garantiza que el código funcionará gracias a las pruebas), aún resulta en el problema mencionado en la pregunta. Se introduce un patrón diferente en el código. En general, creo que debería tratar de tomarse su tiempo para refactorizar todo de una vez (al hacer TDD, mejor con pasos intermedios) para no tener ese estado inconsistente intermedio. Cuando termine, confirme su código para el control de origen.
Steven Jeuris
2
@ Steven, está bien refactorizar todo de una vez si puedes permitírtelo . Sin embargo, en la mayoría de los proyectos de la vida real, no puedes detener todo y pasar varios días o semanas seguidas solo para refactorizar. Incluso si tiene la suerte de tener un gerente que lo respalde, el proyecto aún debe continuar.
Péter Török
1
@ Steven, depende; vea mi actualización más arriba.
Péter Török
1
@ Péter Török: ¡Buena actualización! Descripción perfecta de los factores del mundo real a tener en cuenta.
Steven Jeuris
4

Como no tiene tiempo para refactorizar y el código se puede mantener, manténgalo consistente. La próxima vez, asegúrese de incluir la refactorización en la estimación.

kirk.burleson
fuente
3

Ser coherente solo ayuda al próximo desarrollador cuando el código circundante está en buena forma. Piense en cuánto tiempo le llevó comprender el código en cuestión y encontrar los "puntos de extensión" correctos para agregar sus cambios. Si sigues la tendencia actual, el siguiente individuo debe comprender el código existente, así como tu nuevo código cuando tenga que hacer cambios.

Si refactoriza el código en funciones significativas, el siguiente individuo tendrá más facilidad para comprender lo que está sucediendo y necesitará menos esfuerzo para agregar sus cambios. Piénsalo de esta manera. Asume que el próximo chico eres tú. ¿Qué preferiría ver cuando vuelva a visitar este bloque de código, el mismo desastre que está viendo ahora o algo más construido lógicamente?

Michael Brown
fuente
3

La consistencia tiene una alta prioridad. Obviamente, todos en su sano juicio preferirían una solución DRY elegante en todas partes en lugar de una placa de copia pegada en todas partes, pero ¿realmente preferirían dos enfoques diferentes en la misma base de código que tener uno aplicado consistentemente? ¿Qué pasa si alguien inventa una solución aún más inteligente y también la aplica de manera inconsistente? Entonces tienes tres formas diferentes de hacer lo mismo.

He visto mucho código desordenado como resultado de que los desarrolladores encuentren una "mejor manera" de hacer algo, pero no lo apliquen consistentemente sobre una base de código. Si es parte de una estrategia planificada y coordinada aplicar un nuevo patrón gradualmente con el tiempo, puede funcionar, pero cada patrón implementado de manera inconsistente tiene el riesgo de empeorar el sistema en general.

Tal vez podría considerar la refactorización en pasos más pequeños, de modo que cada paso se pueda aplicar sobre toda la base de código de una sola vez. P.ej. extrayendo una parte más pequeña de la repetitiva a una función auxiliar en cada iteración. Con el tiempo terminas con todas las repeticiones en una clase auxiliar. Puede parecer realmente feo porque no fue diseñado sino desarrollado, pero puede solucionarlo ahora porque tiene todo el código en un solo lugar.

JacquesB
fuente
+1 "Entonces tienes tres formas diferentes de hacer lo mismo". Lo he visto en todos los proyectos empresariales en los que he trabajado. Yo diría que ni siquiera intentes refactorizar hasta que hayas rastreado el código para asegurarte de que ya no haya otra pieza de código que esté tratando de encargarse del código feo que decidiste refactorizar. A veces es mejor seguir la convención convencional, al menos hasta que haya leído el código detenidamente.
TK-421
1

Cualquier refactorización que elimine el código duplicado es buena refactorización, y no debe posponerse a menos que sea inminente una fecha límite. El tiempo dedicado a refactorizar una vez se compensa fácilmente por el tiempo ganado en futuras implementaciones. Gracias a la refactorización, el funcionamiento interno ya no es visible, por lo que debería ser más fácil de entender, no más difícil de seguir.

En mi opinión, es un error común pensar que el código refactorizado es más difícil de seguir. Este suele ser un argumento de personas que solo están familiarizadas con lo que se está refactorizando, y no con el resultado refactorizado. Para los recién llegados, el resultado refactorizado será más claro.

Cualquier error / función se puede corregir / implementar en un lugar central en un momento posterior, y está automáticamente disponible en todas partes en su aplicación. Esta es una gran ganancia que generalmente se subestima mucho. En general, una refactorización total de un componente no lleva tanto tiempo. (días en lugar de meses) Al comparar esto con el tiempo perdido debido a errores, al tener que comprender el código que podría haber sido refactorizado, el costo de la refactorización se vuelve mínimo.

Actualización relacionada con la respuesta de Péter Török: ¿No es al posponer la refactorización "porque lleva tiempo", aumenta el tiempo para refactorizarla más adelante? La refactorización no debería llevar mucho tiempo, cuando se realiza con más frecuencia.

Cómo explicarle a su jefe que pasará unos días escribiendo código que no agrega nada al producto, es difícil y es una pregunta completamente diferente. :)

Steven Jeuris
fuente
1
"Cómo explicarle a su jefe que pasará unos días escribiendo código que no agrega nada al producto, es difícil y es una pregunta completamente diferente". Buen intento ;-) Desafortunadamente en la vida real también necesitamos resolver esto. Por eso es más práctico dar pequeños pasos. Se puede realizar media hora de refactorización como parte de una tarea de dos horas, sin que su gerente necesite saberlo. OTOH dos días completos dedicados a la refactorización rara vez pasan desapercibidos.
Péter Török
@ Péter Török: Actualicé un poco la publicación. Por supuesto, todavía tiene situaciones del mundo real donde no se le permite tomarse el tiempo para refactorizar. Pero creo firmemente que, en teoría , siempre es tiempo ganado. (a menos que sepa que el código en el que está trabajando ya no será compatible en el futuro cercano)
Steven Jeuris
Como dicen, en teoría no hay mucha diferencia entre práctica y teoría. Sin embargo, en la práctica ... :-) En la vida real, no siempre puede recuperar el costo gastado en la refactorización. Extendí mi respuesta sobre esto.
Péter Török
1
La refactorización que elimina el código duplicado no siempre es buena; Es posible que dos métodos sean idénticos bajo las reglas de negocios de hoy, pero eso no será el de mañana. Por ejemplo, AcmeLockerBankuno podría tener un getRow(int itemIndex)método que devuelve index % 5y un AcmeButtonPanelmétodo idéntico porque tanto el casillero como los tableros de botones numeran las cosas de la misma manera; si ninguno de los bancos de casilleros de hoy en día tiene elementos con un índice superior a 1000 pero algunos paneles de botones sí, y los casilleros futuros usan un espaciado de fila diferente para los índices en el rango 1000-1999 ...
supercat
1
... agregar el comportamiento adicional a los bancos de casilleros será fácil si los bancos de casilleros y los bancos de botones tienen getRowmétodos distintos , pero puede ser más difícil si las funciones se han fusionado con un método común.
supercat
0

¿No puede crear una nueva Interfaz / Clases que funcione como una Fachada sobre el código anterior, mientras introduce su nuevo código en su propio estilo que puede ampliarse fácilmente?

Darren Young
fuente
0

Hazlo bien en el futuro. Use funciones en lugar de cortar y pegar. Eso no requiere refactorización, pero le da el comienzo de una base para una refactorización futura.

Andy Lester
fuente
0

Usted, como desarrollador, ¿prefiere mantener un código aburrido y estúpido o tener algunos ayudantes que hagan el código aburrido estúpido en su lugar?

No entiendo la pregunta revisada. Creo que la mayoría de la gente, como yo, preferiría no mantener un "código estúpido y aburrido". No sé a qué te refieres con ayudante, lo siento.

El cartel respondió a su propia pregunta al no hacer grandes cambios en este momento. Buena elección. Tengo problemas con la arrogancia de un desarrollador de que saben lo que es mejor para el negocio. Una gran refactorización a escondidas ... porque sabes más que tu jefe? Cualquier gerente capaz puede sopesar los beneficios.

Si la refactorización no es una opción, la pregunta se convierte más en una discusión sobre la hora, el lugar, etc. correctos para hacerlo. La consistencia es muy importante. Sin embargo, el código de larga duración a menudo se beneficia de la "renovación". Otros han discutido esto ...


fuente