legacy - Estar presionado para GOTO el lado oscuro
(10)
Tenemos una situación en el trabajo donde los desarrolladores que trabajan en un sistema heredado (núcleo) están siendo presionados a usar declaraciones GOTO cuando agregan nuevas características al código existente que ya está infectado con el código spaghetti.
Ahora, entiendo que puede haber argumentos para usar ''solo un pequeño GOTO'' en lugar de gastar el tiempo en refactorizar a una solución más fácil de mantener. El problema es que este aislado ''solo un pequeño GOTO'' no está tan aislado. Al menos una vez cada semana más o menos hay un nuevo ''un pequeño GOTO'' para agregar. Esta base de código ya es un horror para trabajar debido a que el código que data de 1984 o antes estaba plagado de GOTOs que harían que muchos Pastafarians creyeran que estaba inspirado en el Monstruo de Espagueti Volador mismo.
Desafortunadamente, el lenguaje en el que está escrito no tiene herramientas de refactorización listas, por lo que es más difícil presionar para que el ''Refactor aumente la productividad más adelante'', porque las ganancias a corto plazo son las únicas ganancias que se han prestado atención aquí ...
¿Alguien más ha experimentado este problema por el cual todos están de acuerdo en que no podemos agregar nuevos GOTO para pasar 2000 líneas a una sección aleatoria, pero los Anaylsts insisten en hacerlo constantemente esta vez y que la administración lo apruebe?
tldr;
¿Cómo se puede abordar el problema de que los desarrolladores sean presionados (forzados) a agregar continuamente sentencias GOTO (agregando, me refiero a agregar para saltar a secciones aleatorias a muchas líneas de distancia) porque ''obtiene esa característica más rápido''?
Estoy empezando a temer que podamos perder desarrolladores valiosos para las aves de presa por esto ...
Aclaración:
Ir a here
alsoThere:
No, estoy hablando del tipo de goto que salta 1000 líneas de una subrutina a otra a mitad de camino en un ciclo while. Ir a somewhereClose
Cerrar
there:
ni siquiera estaba hablando del tipo de cosas que puedes leer razonablemente y determinar qué estaba haciendo un programa. Goto alsoThere
somewhereClose:
Este es el tipo de código que hace que las albóndigas nextpoint
midpoint:
Si es la primera vez aquí nextpoint
detail:
(cada una es casi completamente diferente) Goto pointlessReturn
here:
en esta pregunta, no estaba hablando del uso ocasionalmente correcto de un goto. Ir there
tacoBell:
y acaba de volver a la mesa de dibujo. Goto Jail
elsewhere:
cuando a los analistas les toma semanas descifrar qué hace un programa cada vez que se toca, algo está muy mal con su base de código. De hecho, en realidad estoy hasta el hell:
si no estoy actualizado goto 4
rendición de la especificación goto detail
pointlessReturn:
tacoBell
pointlessReturn:
goto tacoBell
Jail:
realidad, solo una pequeña actualización con una pequeña victoria. Pasé 4 horas refacturando una parte de este programa en particular, una sola etiqueta a la vez, guardando cada iteración en svn a medida que avanzaba. Cada paso (alrededor de 20 de ellos) era pequeño, lógico y lo suficientemente fácil como para bypass
nextpoint:
salta espontáneamente de tu comida y en tu pantalla a través de un extraño tipo de magnetismo de espagueti y albóndiga. Goto elseWhere
bypass:
verifica razonablemente que no debe introducir ningún cambio lógico. Usando esta nueva versión más legible, me he sentado con el analista y he completado casi todo este cambio ahora. Goto end
4:
primero * si es la primera vez aquí goto hell
, no hay segundo si es la primera vez aquí goto hell
, no hay tercero si es la primera vez aquí goto hell
cuarto ahora actualizado goto hell
end:
Lamentablemente, el idioma en el que está escrito no tiene herramientas de refactorización listas para usar
¿No tienes editores con capacidades macro? ¿No tienes scripts de shell? He hecho toneladas de refactorización a lo largo de los años, muy poco con navegadores de refactorización.
¿Cuántos errores se han introducido debido a GOTO incorrectamente escritos? ¿Cuánto dinero le costó a la compañía? Convierta el problema en algo concreto, en lugar de "esto se siente mal". Una vez que las personas a cargo lo reconozcan como un problema, conviértalo en una política como, "no hay nuevos GOTO para nada excepto simplificar la lógica de salida para una función", o "no hay nuevos GOTO para funciones que no lo hacen". tener una cobertura de prueba unitaria del 100% ". Con el tiempo, apriete las políticas.
De regreso a los principios:
- ¿Es legible?
- ¿Funciona?
- ¿Es mantenible?
El problema subyacente aquí parece ser que tienes ''analistas'' que describen, presumiblemente necesarios, cambios funcionales en términos de agregar un goto a algún código. Y luego tienes ''programadores'' cuyo trabajo parece estar limitado a escribir ese cambio y luego quejarse sobre él.
Para hacer algo diferente, esa distribución disfuncional de responsabilidades entre esas personas debe cambiar. Hay muchas maneras diferentes de dividir el trabajo: la clásica y convencional (que es bastante probable la política oficial, pero ignorada en su trabajo) es que los analistas escriban un documento de especificación independiente de la implementación y los programadores lo implementen como manteniblemente como pueden.
El problema con ese enfoque teórico es que en realidad es poco realista en muchas situaciones comunes. En particular, hacerlo "correctamente" requiere que los empleados vistos como junior ganen una discusión con colegas que son mayores, tienen mejores conexiones sociales en la oficina y más conocedores del negocio. Si el argumento ''goto no es independiente de la implementación, así como un analista simplemente no puede decir que la palabra'' no vuela en su espacio de trabajo, entonces presumiblemente este es el caso.
Mucho mejor en muchas circunstancias son alternativas como:
- los analistas escriben código orientado al cliente y los desarrolladores escriben la infraestructura.
- Los analistas escriben especificaciones ejecutables que se usan como implementaciones de referencia para pruebas unitarias.
- Los desarrolladores crean un lenguaje específico de dominio en el que los analistas programan.
- Deja caer la distinción entre uno y otro, teniendo solo un híbrido.
Este es el clásico conflicto entre la "administración" y los "tecnólogos".
A pesar de estar en el equipo "techie", a lo largo de los años he establecido firmemente el lado de la "gestión" de este debate.
Hay un número de razones para esto:
¡Es muy posible tener programas bien escritos fáciles de leer y estructurados con gotos! Pregúntele a cualquier programador ensamblador; rama condicional y un bucle do primitivo son todo lo que tienen que trabajar. El hecho de que el "estilo" no es actual y no significa que no está bien escrito.
Si es un desastre, va a ser una verdadera molestia extraer las reglas de negocio que necesitará si va a reescribir por completo o, si está haciendo una refacturación técnica del programa, nunca estará seguro de que el comportamiento del programa refactorizado es "correcto", es decir, hace exactamente lo que hace el programa anterior.
Retorno de la inversión: mantener el estilo de programación original y realizar cambios mínimos requerirá un esfuerzo mínimo y rápidamente confirmará la solicitud de los clientes. Gastar mucho tiempo y esfuerzo en refactorizar será más costoso y tomará más tiempo.
Riesgo: las reescrituras y las refactorizaciones son difíciles de realizar, se requieren pruebas exhaustivas del código refactorizado y las cosas que parecen "errores" pueden haber sido "características" en lo que respecta al cliente. Un problema particular con la "mejora" del código heredado es que los usuarios empresariales pueden tener una solución de problemas bien establecida que depende de la presencia de un error, o explotar la existencia de un error para cambiar los procedimientos comerciales sin informar al departamento de TI.
Así que, en general, la administración se enfrenta a una decisión: "agregar un pequeño truco" para probar el cambio y ponerlo en producción en doble tiempo rápido con poco riesgo - o - realizar un gran esfuerzo de programación y hacer que el negocio grite a ellos cuando surge un nuevo conjunto de errores.
Y si decide refaccionarse dentro de cinco años, algún graduado de la universidad morbosa se quejará de que su programa refactorizado ya no es compatible con la palabra de moda y exige que se le permita pasar semanas reescribiéndolo.
Si no está roto, ¡no lo arregles!
PD: Incluso Joel piensa que esta es una mala idea: cosas que nunca debes hacer
¡Actualizar!-
Aceptar si desea refactorizar y mejorar el código que necesita para llevarlo a cabo correctamente.
El problema básico es que le está diciendo al cliente que "quiero pasar n semanas trabajando en este programa y, si todo va bien, hará exactamente lo que hace ahora". - esta es una venta difícil para decir lo menos.
Necesita recopilar datos a largo plazo sobre la cantidad de bloqueos e interrupciones, el tiempo empleado en analizar y programar cambios aparentemente pequeños, la cantidad de solicitudes de cambio que no se hicieron porque eran demasiado difíciles, las operaciones comerciales perdidas porque el sistema no podía cambiar rápidamente suficiente. Reúna también algunos datos de la competencia sobre los costos de mantener programas bien estructurados frente a dejar que se hunda.
A menos que tenga un caso hermético para presentar a los Beancounters, no obtendrá el presupuesto. Realmente tiene que vender esto a la gerencia de negocios, no a sus jefes inmediatos.
La realidad del desarrollo es que a pesar de todas las palabras floridas sobre hacerlo de la manera correcta, la mayoría de los clientes están más interesados en hacerlo de la manera más rápida. El concepto de una base de código que se mueve rápidamente hacia el punto de implosión y la resultante caída en sus negocios es algo que no pueden comprender porque eso significaría tener que pensar más allá del día de hoy.
Lo que tienes es solo un ejemplo. Cómo se para en esto dictará cómo se hace el desarrollo en el futuro. Creo que tienes 4 opciones:
Acepta la solicitud y acepta que siempre estarás haciendo esto.
Acepte la solicitud e inmediatamente empiece a buscar un nuevo trabajo.
Niégate a hacer y prepárate para luchar para arreglar el desastre.
Renunciar.
La opción que elijas va a depender de cuánto valores tu trabajo y tu autoestima.
Los GOTO no son buenos espaguetis de código, hay una multitud de otros factores involucrados. Esta discusión de la lista de correo de Linux puede ayudar a poner algunas cosas en perspectiva (comentarios de Linus Torvalds sobre la imagen más grande del uso de los gotos).
Tratar de instituir una política de "no goto" solo por el hecho de no tener "gotos" no logrará nada a largo plazo, y no hará que su código sea más fácil de mantener. Los cambios deberán ser más sutiles y centrarse en aumentar la calidad general del código, pensar en la utilización de las mejores prácticas para la plataforma / idioma, cobertura de pruebas unitarias, análisis estático, etc.
Recientemente tuve que trabajar en un código que no era legado per se , pero los hábitos de los desarrolladores anteriores ciertamente lo eran y, por lo tanto, los de GOTO estaban en todas partes. No me gusta GOTO; hacen un lío horrible de cosas y hacen que la depuración sea una pesadilla. Peor aún, reemplazarlos con código normal no siempre es sencillo.
SI no puede relajar su GOTO, ciertamente le recomiendo que ya no los use.
Si un cambio en el programa requiere "solo un pequeño goto", diría que el código es muy fácil de mantener.
Este es un problema común cuando se trata de código heredado. Siga el estilo original del programa o "modernice" el código. Para mí, la respuesta suele ser seguir con el estilo original a menos que tengas un cambio realmente grande que justifique una reescritura completa.
También tenga en cuenta que varias características de lenguaje "moderno" como la declaración "throw" de Java, o los SQL "ON ERROR" son realmente "IR A" disfrazados.
Tal vez puedas usar el patrón boyscout: deja el lugar un poco más limpio de lo que lo encontraste. Por lo tanto, para cada solicitud de características: no introduzca nuevos gotos, pero elimine uno.
Esto no pasará demasiado tiempo para mejoras, le daría más tiempo, para encontrar errores recién introducidos. Tal vez no le cueste mucho tiempo adicional, si elimina un goto de la pieza, lo que a pesar de que tuvo que pasar tiempo entendiéndolo, y presentando la nueva característica.
Discuten que una refacturación de 2 horas ahorrará 20 veces y 15 minutos en el futuro y permitirá mejoras más rápidas y profundas.