¿Debería actualizarse el código anterior para usar construcciones de lenguaje más nuevas, o deberían quedar bloqueadas las construcciones obsoletas?


15

Quiero hacer algunas mejoras en un código aún funcional que se escribió hace mucho tiempo, antes de que el lenguaje de programación en el que está escrito creciera en características. En teoría, todo el proyecto utiliza la versión actualizada del lenguaje; sin embargo, este módulo en particular (y de hecho, muchos otros módulos) todavía están escritos en el dialecto anterior.

Debería:

  • ¿No tocar las partes del código que no tengo que tocar, pero escribir mi parche haciendo uso de las nuevas características del lenguaje que hacen que sea más fácil escribir el parche pero que no se usan en situaciones análogas en ningún otro lugar del módulo? (Esta es la solución que elegiría intuitivamente).
  • ¿Ignora el hecho de que han pasado años y refleja el estilo que se usa en el resto del código al escribir mi parche, comportándose efectivamente como si estuviera haciendo la misma tarea muchos años antes? (Esta solución la consideraría tontamente intuitivamente, pero dada la cantidad de alboroto que todos los que hablan sobre el "buen código" se preocupan por mantener la coherencia a toda costa, tal vez esto es lo que debería hacer).
  • ¿Actualizar todo el módulo para usar las nuevas construcciones y convenciones del lenguaje? (Esta es probablemente la mejor solución, pero puede requerir mucho tiempo y energía que podría gastarse mejor en una tarea diferente).

1
@JerryCoffin No voy a seguir discutiendo en los comentarios: publiqué en meta donde podemos tener una discusión adecuada.

Respuestas:


10

Es imposible dar una respuesta definitiva a esta pregunta, porque depende demasiado de los detalles de la situación.

La coherencia en el estilo de la base de código es importante porque ayuda a facilitar la comprensión del código, que es uno de los aspectos más importantes de la mantenibilidad.
No serías el primer programador que fue maldecido por hacer que el código fuera difícil de entender al mezclar diferentes estilos. Incluso podrías ser tú mismo quien maldiga en años.

Por otro lado, el uso de nuevas construcciones de lenguaje que no existían cuando se escribió el código por primera vez no implica automáticamente que esté reduciendo la capacidad de mantenimiento o incluso que esté rompiendo el estilo del código. Todo eso depende de las características del idioma en particular que desee usar y de cuán familiarizado esté el equipo con el estilo antiguo del código y las nuevas características.

Como ejemplo, generalmente no sería aconsejable comenzar a introducir conceptos de programación funcional como map / reduce a una base de código que esté completamente en estilo OO, pero podría funcionar agregar una función lambda aquí y allá a un programa que no utilizó algo así antes.


1
Estoy parcialmente en desacuerdo. Deberíamos seguir el principio de abrir y cerrar. Por lo tanto, debemos tratar de aislar el nuevo código tanto como podamos, y al mismo tiempo escribir el nuevo código con la última construcción de lenguaje posible.
Anand Vaidya

33
@AnandVaidya: esa es, en mi humilde opinión, una expectativa poco realista, ya que supone que el código anterior sigue el OCP o se puede cambiar fácilmente para seguir el OCP, que rara vez es el caso o simplemente no vale la pena el esfuerzo.
Doc Brown

1
Cuando dije ocp, no me refiero a oops ocp, sino a ocp en general. Básicamente, trate de no modificar el código existente tanto como sea posible y tenga su propio código aislado. Eso es incluso posible con el antiguo código de procedimiento. Entiendo que los códigos de espagueti no se pueden modificar de esa manera, pero para un código bien diseñado en cualquier paradigma, abrir y cerrar debería ser fácilmente aplicable. Puede ser oops o de procedimiento o funcional.
Anand Vaidya

2
@AnandVaidya: cuando no te refieres a OCP, no deberías llamarlo así. Supongo que lo que realmente quiere decir es lo que Feather llama escribir nuevo código en un método de brote , por lo que uno puede restringir el nuevo estilo de código a un nuevo método separado. Cuando esto es posible y sensato, estás en lo correcto, está bien. Lamentablemente, este enfoque no siempre es aplicable, al menos no si desea mantener seco el código antiguo.
Doc Brown

@DocBrown: No creo que el principio abierto / cerrado esté restringido a la programación orientada a objetos. Puede tener módulos donde agregue nuevos procedimientos pero no modifique los existentes, es decir, mantenga intacta la semántica del código existente y, si necesita una nueva funcionalidad, escriba un código nuevo.
Giorgio

5

En gran medida, el código y cómo se ve es irrelevante . Lo que el código hace es lo que importa.

Si puede garantizar que cambiar / reescribir el código no cambiará lo que hace el código, entonces puede continuar y refactorizar el código al contenido de su corazón. Esa "garantía" es un conjunto exhaustivo de pruebas unitarias que puede ejecutar antes y después de sus cambios, sin una diferencia perceptible.

Si no puede garantizar esa estabilidad (no tiene esas pruebas), entonces déjelo bien solo.

Nadie le agradecerá por "romper" una pieza de software crítico para el negocio, incluso si estaba tratando de hacerlo "mejor". "Trabajar" triunfa "mejor" cada vez.

Por supuesto, no hay nada que lo detenga para crear un conjunto de tales pruebas en preparación para tal ejercicio ...


4

Casi cualquier cosa puede analizarse en términos de costos y beneficios, y creo que esto se aplica aquí.

Los beneficios obvios de la primera opción son que minimiza el trabajo a corto plazo y minimiza las posibilidades de romper algo al reescribir el código de trabajo. El costo obvio es que introduce inconsistencia en la base del código. Cuando realiza alguna operación X, se realiza de una manera en algunas partes del código, y de una manera diferente en una parte diferente del código.

Para el segundo enfoque, ya ha notado el beneficio obvio: consistencia. El costo obvio es que tiene que inclinarse para trabajar de maneras que de otro modo hubiera abandonado hace años, y el código sigue siendo ilegible constantemente.

Para el tercer enfoque, el costo obvio es simplemente tener que hacer mucho más trabajo. Un costo menos obvio es que puede romper cosas que estaban funcionando. Esto es particularmente probable si (como suele ser el caso) el código anterior tiene pruebas inadecuadas para asegurar que continúe funcionando correctamente. El beneficio obvio es que (suponiendo que lo haga con éxito) tiene un nuevo código agradable y brillante. Junto con el uso de nuevas construcciones de lenguaje, tiene la oportunidad de refactorizar la base del código, lo que casi siempre proporcionará mejoras en sí mismo, incluso si todavía usó el lenguaje exactamente como existía cuando se escribió: agregue nuevas construcciones que hagan que el trabajo más fácil, y bien puede ser una gran victoria.

Sin embargo, otro punto importante: en este momento, parece que este módulo ha tenido un mantenimiento mínimo durante mucho tiempo (a pesar de que el proyecto en su conjunto se mantiene). Eso tiende a indicar que está bastante bien escrito y relativamente libre de errores; de lo contrario, probablemente habría sufrido más mantenimiento mientras tanto.

Eso lleva a otra pregunta: ¿cuál es la fuente del cambio que está haciendo ahora? Si está arreglando un pequeño error en un módulo que en general aún cumple con sus requisitos, eso indicaría que el tiempo y el esfuerzo para refactorizar todo el módulo probablemente se desperdicien en gran medida, para cuando alguien necesite meterse con nuevamente, es muy posible que estén en la misma posición que usted ahora, manteniendo un código que no cumple con las expectativas "modernas".

Sin embargo, también es posible que los requisitos hayan cambiado, y usted está trabajando en el código para cumplir con esos nuevos requisitos. En este caso, es muy probable que sus primeros intentos no cumplan con los requisitos actuales. También hay una posibilidad sustancialmente mayor de que los requisitos se sometan a algunas rondas de revisión antes de que se estabilicen nuevamente. Eso significa que es mucho más probable que esté haciendo un trabajo significativo en este módulo a corto plazo (relativamente), y es mucho más probable que realice cambios en el resto del módulo, no solo en el área que conoce bien ahora. En este caso, es mucho más probable que la refactorización de todo el módulo tenga beneficios tangibles a corto plazo que justifiquen el trabajo adicional.

Si los requisitos han cambiado, es posible que también necesite ver qué tipo de cambio está involucrado y qué está impulsando ese cambio. Por ejemplo, supongamos que estaba modificando Git para reemplazar su uso de SHA-1 con SHA-256. Este es un cambio en los requisitos, pero el alcance está claramente definido y es bastante limitado. Una vez que haya hecho que se almacene y use SHA-256 correctamente, es poco probable que encuentre otros cambios que afecten al resto de la base de código.

En la otra dirección, incluso cuando comienza pequeño y discreto, un cambio en una interfaz de usuario tiende a aumentar, por lo que lo que comenzó como "agregar una nueva casilla de verificación a esta pantalla" termina más como: "cambie esta IU fija para admitir plantillas definidas por el usuario, campos personalizados, esquemas de colores personalizados, etc. "

Para el primer ejemplo, probablemente tenga más sentido minimizar los cambios y errar por el lado de la coherencia. Para este último, la refactorización completa es mucho más probable que valga la pena.


1

Iría por tu primera opción. Cuando codifico, sigo la regla para dejarlo en un mejor estado de lo que era.

Entonces, el nuevo código sigue las mejores prácticas, pero no tocaré código que no esté relacionado con los problemas en los que estoy trabajando. Si lo hace bien, su nuevo código debería ser más fácil de leer y mantener que el código anterior. Descubrí que la mantenibilidad total mejora, porque si sigues así, el código que debes tocar para tu trabajo es cada vez más el código "mejor". Lo que lleva a una resolución de problemas más rápida.


1

La respuesta, como tantas otras cosas, es que depende . Si existen ventajas importantes para actualizar las construcciones más antiguas, como una capacidad de mantenimiento a largo plazo enormemente mejorada (evitando el infierno de devolución de llamadas, por ejemplo) que lo haga. Si no hay una gran ventaja, la consistencia es probablemente tu amigo

Además, también querrá evitar incrustar dos estilos en la misma función más de lo que desearía evitarlo dentro de dos funciones separadas.

En resumen: su decisión final debe basarse en un análisis de "costo" / "beneficio" de su caso particular.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.