Industria del software: ¿cómo trato el código heredado y el código limpio de manera efectiva?

Actualmente estoy trabajando con una empresa que ha producido una gran cantidad de código útil, pero antiguo. Muchos de mis compañeros y desarrolladores de software han dedicado mucho tiempo a crear esta biblioteca y la usan mucho. Es importante destacar que la biblioteca era útil y necesaria cuando C ++ aparentemente no estaba tan bien desarrollado, pero hoy en día está muy desactualizado y tiene un estilo C. Tengo muchas dificultades para progresar con la escritura de software que requiere tanto mantenimiento.

Por ejemplo, mis compañeros prefieren matrices sin procesar a vectores y su propia clase de cadena a cadenas estándar. Mis compañeros también lo saben, pero dado que gran parte del código base está construido con nuestras propias implementaciones de listas y contenedores, económicamente no es posible refactorizar nuestro código sin escribirlo desde cero.

¿Qué pasos puedo tomar para integrarme mejor en el uso de su código y hacer que mi código se adapte al código heredado, mientras sigo practicando los estándares de codificación modernos?

En este tipo de situaciones, te recomiendo escuchar antes de juzgar. ¿Por qué sus colegas prefieren su propia clase de cadena a las cadenas estándar, por ejemplo? ¿Por qué se decidió esto o fue una decisión formal? ¿Cuáles son los pros y los contras de cambiar a cadenas estándar? ¿Cuánto tardaría en cambiar? etc.
Hagas lo que hagas, no refactorices el código antiguo a menos que lo entiendas completamente Y tengas la tarea de refactorizarlo.
@jcmack Por lo que sé, las situaciones relacionadas con las clases personalizadas para las contrapartes de STL se crearon porque no existían o porque eran demasiado lentas en ese momento (construimos sistemas en tiempo real). Somos una empresa pequeña, por lo que cambiar de clases personalizadas simplemente no es realista; Supongo que más de un año para refactorizar todo. Me dijeron explícitamente que a la mayoría de nuestros desarrolladores no les gusta nuestra clase de cadena personalizada, pero persisten debido a problemas de compatibilidad y plazos. Mi dificultad es equilibrar el código heredado y el cpp moderno, que es subjetivamente más legible y mantenible.
@JonSG No estoy sugiriendo refactorizar el código antiguo. Me gustaría encontrar una manera de encontrar un equilibrio entre trabajar de manera más efectiva con código legible y mantener las funciones heredadas.
@MichaelChoi Lo entiendo totalmente. Es muy importante trabajar dentro de la estructura de gestión existente en su empresa para presentar una iniciativa como esta. Como gerente o líder sénior, definitivamente me gustaría conocer los plazos, la cantidad de personas necesarias, los pros y los contras y el plan de migración/implementación. Con un esfuerzo tan grande como un año, definitivamente recomendaría una estrategia incremental si es posible.
En casos como este, a veces puede determinar una mejor manera de avanzar. Mantiene el código de trabajo, pero los cambios futuros en un sistema aislado pueden renovarse. Por supuesto, esto requiere funciones de código personalizado similares a las del método predeterminado o mejorado, lo que no siempre es el caso si el código personalizado tiene problemas de cobertura de código.
"Tengo muchas dificultades para progresar con la escritura de software que requiere tanto mantenimiento". Desafortunadamente, esa oración es equivalente a decir "Tengo dificultades para programar". Para bien o para mal, solo está describiendo un día de trabajo absolutamente normal "como ingeniero de software".
Voto para cerrar esta pregunta como fuera de tema porque se trata de programar, no de navegar en el lugar de trabajo.

Respuestas (3)

El código heredado es parte del ciclo de vida del desarrollo de software. Trabajamos en un entorno que cambia rápidamente. Esta hermosa clase que estás escribiendo hoy podría considerarse fea y obsoleta en solo unos años. Así que mi primer consejo sería: no hagas juicios si no sabes con seguridad por qué exactamente se decidió en ese momento ir de esta manera en particular. Tal vez, no se conocían todas las opciones, y se ha elegido "la peor" no intencionalmente. Y ahora, como mencionaste, todos conocen mejores prácticas, pero prefieren apegarse a ese estilo por razones de consistencia. Y siendo pragmático, tiene sentido mantener todo de la misma manera, ¿no?

Dicho esto, recomendaría expresar sus ideas de mejora. Tal vez, nadie permitirá volver a escribir todo el asunto. Pero los estándares de codificación son objeto de mejoras continuas, y un equipo saludable lo entiende. ¿Qué tal hacer una presentación sobre por qué los vectores son mejores que las matrices? ¿O iniciar una discusión en un chat o correo electrónico del equipo sobre la idoneidad de las implementaciones personalizadas para objetos estándar? Por supuesto, sería bueno compartir un par de enlaces, digamos, a publicaciones de blogs de grandes nombres de la industria, para fortalecer su posición. Y cuando tenga algún tipo de consenso después de estas discusiones, sea proactivo y cree una encuesta sobre la aceptación de esta idea como estándar a partir de ahora. Aunque no sea aceptado, aprenderás algo que te es desconocido en este momento.

Pero si lo es, verá cuánto puede hacer incluso si el código base es viejo y grande y parece imposible arreglarlo todo. Comience con pequeños pasos: haga que esta regla funcione solo para un nuevo código, incluso si para implementar una nueva función necesita tocar partes antiguas del sistema donde muchos métodos todavía son "feos". A continuación, aplica la regla de los boy scouts.: si ve "el estilo antiguo" en algún lugar del camino y no lleva más de una hora arreglarlo, reescriba. Luego, si corresponde, una verificación automática podría ayudar a detectar todos los lugares que necesitan atención y estimar el esfuerzo real para deshacerse de esto por completo (por cierto, podría ser sorprendentemente pequeño). Y si agrega esta verificación a una canalización, obligará a todos a arreglar esas cosas incluso si su tarea no estaba relacionada con ese método en particular con un "estilo antiguo". Con todo esto algún día desaparecerá, lo prometo.

Y una situación más para demostrar que los cambios son más posibles de lo que piensas. En el mundo de los negocios, necesitamos hablar en términos comerciales. La refactorización por el hecho de refactorizar en sí agrega valor cero, pero cuando pueda demostrar cómo este cambio afectará los resultados, es probable que obtenga luz verde para cualquier solicitud. A veces, el mantenimiento de una parte particular del código es doloroso, mientras que el esfuerzo razonable de reelaboración lo acelerará drásticamente. Te animo a que hables en lugar de luchar con eso. Obtenga algunos números, haga estimaciones aproximadas sobre cuánto tiempo llevará la mejora de funciones con y sin refactorización. Venda este trabajo a su jefe de tecnología/gerente de producto/otra persona influyente relevante según sea necesario, y la elección les parecerá obvia. Después de este éxito, "de repente", tú' Te encontrarás liderando un proyecto para hacer de esta parte de la base de código un lugar donde los estándares de codificación modernos entren en juego. ¡A por ello! ¡Solo asegúrese de tener pruebas para todas las funciones críticas incluso antes de comenzar! ;)

Buena suerte, Miguel.

Una excelente respuesta (+1)! Además, tenga en cuenta el libro canónico Trabajar de manera efectiva con el código heredado : es una lectura obligada para todos en la posición de OP.
@vladimino Agradezco la respuesta pensada. Creo que intentaré ponerlo en práctica. De hecho, recién estoy comenzando mi primer año profesionalmente, por lo que definitivamente soy un novato y definitivamente no entiendo completamente las aplicaciones de las clases personalizadas de nuestra compañía. Habiendo dicho eso por la experiencia que tengo, puedo decir qué clases están mostrando edad y creo que seguiré su consejo sobre cambios incrementales muy pequeños.

C++ es algo así como un lenguaje inusual. Las bibliotecas tienden a no verificar errores o límites. Muchos asumen ciertas condiciones para funcionar y depende del desarrollador verificar esas condiciones antes de pasar a la función. Un buen ejemplo es la función strlen que puede causar un desbordamiento de búfer ya que asume un terminador nulo.

Esto provoca un comportamiento inesperado. He visto casos en los que un desbordamiento de búfer provocó la eliminación de un archivo aleatorio en la máquina. Ocurre aparentemente al azar y en momentos y condiciones extraños nunca consistentes. La depuración se volvió casi imposible ya que parece que funcionó. Fue solo después de un accidente que se encontró el error y las condiciones nunca volvieron a ocurrir.

Luego está toda la recolección de basura, punteros colgantes, etc. Existen muchas cosas peligrosas en C++. Su biblioteca puede verificar esas condiciones o realizar un seguimiento de un puntero automático incorporado/recolección de basura.

El punto es que su compañero de trabajo puede haber escrito bibliotecas que verifican las condiciones y los límites. Primero preguntaría directamente por qué no usar la función de biblioteca X en lugar de los métodos internos. Mira lo que dicen.

Sí, C++ siempre fue peligroso, ya que asumía que los programadores sabían lo que estaban haciendo. C aún más.

Además de la excelente respuesta de vladimino, podría sugerirle a su jefe que haga algunas evaluaciones (tal vez medio día por semana).

Las métricas pueden ser:

  • rendimiento (tiempo) con implementación propia y con contenedores STL para algunos ejemplos
  • Tiempo dedicado a corregir errores relacionados con matrices, etc., por ejemplo, errores fuera de los límites (a menudo difíciles de encontrar)

Entonces, podría construir un caso con números si una refactorización agregaría valor comercial. O tal vez no.

Otra idea sería extender los propios contenedores para hacerlos funcionar con algoritmos STL, es decir, dotarlos de los métodos necesarios.