anti-patterns - diseño - refactoring antipatterns
¿Cuál es el programa más incorrecto que has tenido que mantener? (14)
El que acabo de comenzar.
- Sin control de fuente
- Toda la fuente se edita en vivo. Para detener errores, hay archivos de respaldo como db-access.php.070821 que ensucian el árbol de fuentes.
- El código es excepcionalmente frágil: hay muy poco en el camino de la comprobación de errores y absolutamente no retrocede si lo hace.
Periódicamente se me pide que haga trabajos de mantenimiento en un sistema que fue construido por un verdadero cirujano espacial. Hay tanto problema con eso que es difícil saber por dónde empezar.
No, espera, comenzaré por el principio: en los primeros días del proyecto, al diseñador se le dijo que el sistema necesitaría escalar, y había leído que una fuente de problemas de escalabilidad era el tráfico entre la aplicación y la base de datos. servidores, por lo que se aseguró de minimizar este tráfico. ¿Cómo? Al poner toda la lógica de la aplicación en los procedimientos almacenados de SQL Server.
Seriamente. La mayor parte de las funciones de la aplicación se realiza mediante la interfaz HTML que formula los mensajes XML. Cuando el nivel medio recibe un mensaje XML, utiliza el nombre de etiqueta del elemento del documento como el nombre del procedimiento almacenado al que debe llamar, y llama al SP, pasándole todo el mensaje XML como parámetro. Toma el mensaje XML que devuelve el SP y lo devuelve directamente al frente. No hay otra lógica en el nivel de aplicación.
(Había algún código en el nivel intermedio para validar los mensajes XML entrantes contra una biblioteca de esquemas. Pero lo eliminé, después de comprobar que 1) solo un pequeño puñado de mensajes tenía esquemas correspondientes, 2) los mensajes no se ajustaban a estos esquemas, y 3) después de validar los mensajes, si se encontraron errores, el método los descartó. "¡Esta caja de fusibles ahorra mucho tiempo, viene de fábrica con una moneda preinstalada!")
He visto software que hace algo mal antes. Montones. He escrito bastante. Pero nunca he visto algo como la determinación de ojos acerados de hacer lo incorrecto, en cada giro posible , que está incorporado en el diseño y la programación de este sistema.
Bueno, al menos se fue con lo que sabía, ¿verdad? Um. Aparentemente, lo que sabía era Access. Y realmente no entendía el acceso. O bases de datos.
Aquí hay un patrón común en este código:
SELECT @TestCodeID FROM TestCode WHERE TestCode = @TestCode SELECT @CountryID FROM Country WHERE CountryAbbr = @CountryAbbr SELECT Invoice.*, TestCode.*, Country.* FROM Invoice JOIN TestCode ON Invoice.TestCodeID = TestCode.ID JOIN Country ON Invoice.CountryID = Country.ID WHERE Invoice.TestCodeID = @TestCodeID AND Invoice.CountryID = @CountryID
Bien vale. Tampoco confías en el optimizador de consultas. Pero ¿qué tal esto? (Originalmente, iba a publicar esto en ¿Cuál es el mejor comentario en el código fuente que he encontrado alguna vez? Pero me di cuenta de que había mucho más para escribir que solo este comentario, y las cosas se salieron de control.) En Al final de muchos de los procedimientos almacenados de la utilidad, verá un código similar al siguiente:
-- Fix NULLs SET @TargetValue = ISNULL(@TargetValue, -9999)
Sí, ese código está haciendo exactamente lo que no puedes permitirte creer que está haciendo para que no te vuelvas loco. Si la variable contiene un NULL, está alertando a la persona que llama al cambiar su valor a -9999. Así es como se usa comúnmente este número:
-- Get target value EXEC ap_GetTargetValue @Param1, @Param2, OUTPUT @TargetValue -- Check target value for NULL value IF @TargetValue = -9999 ...
De Verdad.
Para otra dimensión de este sistema, vea el artículo en thedailywtf.com titulado I Think I''ll Call Them "Transactions" . No estoy haciendo nada de esto. Lo juro.
Cuando trabajo en este sistema, a menudo me recuerda la famosa respuesta de Wolfgang Pauli a un alumno: "Eso no está bien. Ni siquiera está mal".
Este no puede ser realmente el peor programa de todos. Es definitivamente el peor en el que he trabajado en toda mi carrera de 30 años. Pero no he visto todo. ¿Que has visto?
¿Cuál es el programa más incorrecto que has tenido que mantener?
¡Todo lo que he escrito!
Seriamente. Cuanto más leo blogs, escucho podcasts y sigo sitios como este, más aprendo todos los días. Y cada día, básicamente, me doy cuenta de que todo lo que escribí ayer está mal de alguna manera. Siento por los pobres que mantienen las cosas que escribí al principio de mi carrera.
Estoy manteniendo una aplicación web de programación que usamos en nuestra intranet. Cuando me preguntaron si podía eliminar un agente del programador, pensé, seguro por qué no. Cuando eché un vistazo al código fuente, descubrí que cada hora del día de este agente estaba codificada por separado. Así que todos los días de su semana. Y así fue cada semana de cada agente de esta región. Y así fue cada región de alrededor de 5 regiones. Html fies que contienen código asp por todo el lugar.
Un día me tomé un tiempo para adivinar cuántas líneas de código hay en estos varios archivos y estimé aproximadamente 300000. Trescientas mil líneas de código de una vez escritas a mano y luego copias y pegadas.
Pero este número convenció a mi gerente con bastante rapidez de que necesitaríamos una nueva aplicación de programación muy rápidamente.
Solía ser un programador de COBOL (estremecimiento). Todo nuestro código cayó en la categoría "incorrecto". En COBOL, no tiene espacios de nombres, todas las variables son globales y hay una gran cantidad de duplicaciones obligatorias de nombres de archivos y otros recursos. Para invocar un procedimiento, establece variables globales, llama al procedimiento y luego inspecciona los contenidos de esas variables globales (u otras que puedan establecerse).
Lo peor, sin embargo, fue mantener un programa COBOL escrito antes de que yo naciera (nací en 1967) y su método exclusivo de control de flujo fue el GOTO. Fue un desastre absoluto e imposible de seguir. Los cambios menores en un tipo de variable pueden tardar días en resolverse. No hubo pruebas automáticas y los planes de prueba manuales nunca se guardaron, por lo que cada cambio requería que se escribiera un nuevo plan de prueba manual, seguido exhaustivamente y entregado con el código.
Irónicamente, esto es lo que hace que COBOL tenga tanto éxito. COBOL a menudo se ejecuta mediante Job Control Language (JCL). Como COBOL es tan débil, los programas no hacen mucho, por lo que JCL asignaría un poco de espacio en el disco (a menudo hasta el nivel del cilindro), y ejecutaría un pequeño programa COBOL para leer datos y luego escribir solo los datos que necesita. Entonces JCL podría llamar a un programa de clasificación para ordenar el archivo resultante. Luego, se llamaría a otro programa COBOL para que lea el archivo ordenado, resuma los datos y tal vez vuelva a extraer los resultados necesarios. Y quizás JCL se usaría nuevamente para mover el archivo a otro lugar, y otro programa COBOL sería llamado para leer los resultados y almacenarlos en una base de datos, y así sucesivamente. Cada programa de COBOL tendía a hacer solo una cosa y se creaba una versión primitiva del modelo de oleoducto de Unix, todo porque COBOL es demasiado difícil de mantener o se puede complicar. Tuvimos un acoplamiento débil y una estrecha cohesión (entre programas, no en ellos) porque era casi imposible escribir COBOL de otra manera.
Todo lo que he escrito originalmente se supone que es un prototipo rápido y termina quedándose por un tiempo. Mi dominio de problema requiere una gran cantidad de prototipos desechables por su naturaleza. Para estos prototipos, a veces es razonable violar todas las mejores prácticas y las reglas del buen estilo, solo hazlo y limpia después si el prototipo termina siendo útil. Sin embargo, ocasionalmente estos prototipos terminan siendo muy difíciles de funcionar correctamente, pero luego terminan siendo guardianes. En estos casos, suelo posponer la refacturación / reescritura indefinida porque me temo que nunca volveré a ponerla en funcionamiento. Disminuyendo aún más mi motivación es que mi jefe es un experto en el dominio que no programa en absoluto.
El intérprete para un lenguaje de procesamiento de geometría CAD / CAM (P1 = 10,10; P2 = 20,20; L1 = P1, P2; - ese tipo de cosas), escrito en el Sistema de Desarrollo Profesional Básico (PDS) de Microsoft, con una longitud mínima nombres variables (se agotó rápidamente, por lo que pasó a letras dobles PP, PQ, PR, ¿alguien?). Y, para ser justos, algunos comentarios. En italiano.
Curiosamente, realmente funcionó, y pude agregarle algunas funcionalidades, pero fue como una odontología amateur: dolorosa, y ciertamente no recomendada ...
Al salir de la escuela de postgrado en Lucent, me dieron un compilador e intérprete para mantener, escrito en PL / I. El lenguaje que se compilaba describía un conjunto complejo de restricciones de integridad, y el intérprete las aplicaba a un gran conjunto de datos que luego se formaría en la base de datos inicial que controla el conmutador 4ESS. El 4ESS era, y sigue siendo, un interruptor de circuito para el tráfico de voz de larga distancia.
El código era un revoltijo. Había una etiqueta para ramificar llamada "NORTH40". Le pregunté al desarrollador original qué significaba.
"Ahí es donde se realizan los controles de rango, ya sabes, los controles para asegurarse de que cada campo tenga un valor correcto".
"¿Pero por qué ''NORTH40''?"
"Ya sabes, ''Hogar, hogar en el campo''".
"¿Huh?"
Resultó que ''NORTH40'' significaba una granja de 40 acres en el norte, que en su mente criada en la ciudad tenía una conexión oscura con un rancho de ganado.
Otro módulo tenía dos matrices paralelas llamadas TORY y DIREC, que se actualizaron en paralelo, por lo que fue un intento obviamente equivocado de modelar una única matriz que contiene pares de datos. No pude descifrar los nombres y le pregunté al desarrollador. Resultó que estaban destinados a leerse juntos: "directivo". Estupendo.
Los pobres que tenían que escribir las restricciones de integridad no tenían un manual de usuario que los guiara, solo una tradición oral más notas escritas a mano. Peor aún, el compilador no realizaba la comprobación de la sintaxis, y muy a menudo terminaban especificando una restricción que se asignaba silenciosamente a la lógica incorrecta, a menudo con muy malas consecuencias.
Lo peor estaba por venir. Mientras profundizaba en las entrañas del intérprete, descubrí que se había construido en torno a un proceso de clasificación gigante. Antes, el género era un proceso de entrada que generaba los datos de entrada de clasificación de los datos sin procesar y las restricciones de integridad. Por lo tanto, si tuviera una tabla con 5.000 definiciones de troncales y cada registro de troncal tuviera tres valores de campo únicos en todo el conjunto de datos de entrada, el proceso de entrada crearía 3 * 5.000 = 15.000 registros de entrada de clasificación, cada uno en bruto registro de datos prefijado por el número de restricción de integridad y una copia del valor de campo para ordenar. En lugar de hacer tres mil 5,000 géneros de registro, hizo una clasificación de 15,000 registros. En el momento en que incluyó cientos de restricciones de integridad intra e inter-tabla, y algunas tablas muy grandes, tuvo una pesadilla combinatoria.
Pude refactorizar un poco, documentar el idioma y agregar la verificación de sintaxis con mensajes de error comprensibles, pero unos meses más tarde salté a una transferencia a un nuevo grupo.
Una vez trabajé en una aplicación CAD escrita en BASIC donde la política de la compañía era que cada programa debe comenzar con la siguiente declaración:
ON ERROR RESUME
jMM
Una vez intenté escribir un decodificador MP3 . No funcionó.
Mantuve ExtUtils :: MakeMaker . MakeMaker ciertamente no es el peor código que he tenido que mantener; en realidad es una maravilla de la ingeniería. Sin embargo, es en esa clase única de horrores de codificación donde el código más crítico para la misión es también el más aterrador.
MakeMaker es el instalador de la mayoría de los módulos de Perl. Cuando ejecuta "Makefile.PL" está invocando MakeMaker. Si MakeMaker se rompe, Perl se rompe. Perl funciona con todo, por lo que MakeMaker tiene que funcionar en todo. Cuando digo todo, me refiero a TODO. Cada extraña variante de Unix. Windows 95 en adelante. Y VMS . Sí, VMS.
¿Qué hace MakeMaker? Makefile.PL es un programa Perl que escribe un Makefile que contiene comandos de shell, que a menudo ejecutan Perl, para construir e instalar un módulo Perl. Permítanme repetir: escribe comandos de shell para ejecutar Perl. Perl, el lenguaje que reemplaza los scripts de shell.
Oh, también puede compilar y vincular el código C. Y también puede vincular estáticamente módulos de Perl en Perl. Ah, y puede administrar las cajas de RCS. Ah, y rodar tarballs de su distribución ... y archivos zip. Y haz todo esto relacionado vagamente con la instalación de módulos.
Y tiene que hacer todo esto de una manera portátil y compatible con versiones anteriores. Tiene que lidiar con variantes de y errores en ...
- make (GNU make, BSD make, nmake, dmake, mms, mmk por nombrar algunos)
- cáscara
- Perl
- El sistema de archivos (si no crees que sea un gran problema, prueba con VMS)
- Compiladores de C y enlazadores
Absolutamente, positivamente no puede fallar y debe permanecer 100% compatible con versiones anteriores.
Ah, y tiene muy poco en el camino de una API de extensión real, por lo que tiene que seguir siendo compatible con los hacks de Makefile ad hoc que las personas tienen que hacer para extenderlo.
¿Por qué hace todo esto? Hace 15 años, cuando Perl solo se ejecutaba en Unix, parecía una gran idea. ¿Por qué escribir un sistema de construcción completo cuando puedes usar make? Perl es un lenguaje de procesamiento de texto; ¡lo usaremos para escribir un Makefile!
Afortunadamente, hay un reemplazo, Module :: Build , y he cifrado mis esperanzas de que matará rápidamente a MakeMaker. Pero su aceptación ha sido lenta y la comunidad muy resistente al cambio, así que estoy atascado manteniendo MakeMaker.
Un sistema de gestión de contactos en línea PHP / MySQL, donde la tabla de contactos no tenía una clave natural. Hubo numerosas instancias de campos de base de datos que contenían datos compuestos en la forma de una cadena delimitada que posteriormente necesitaba ser analizada por el código de la aplicación.
El HTML y la lógica se entrelazaron, casi no se usaron funciones, sino que el código se cortó y se pegó en docenas de archivos de código fuente. Los datos no se desinfectaron y, por lo tanto, los campos con (por ejemplo) pestañas verticales incrustadas causaron un mal funcionamiento del XML devuelto por las llamadas Ajax y, para colmo, archivos con docenas o cientos de declaraciones vacías que consisten en cerrar llaves inmediatamente seguidas de punto y coma : "};"
Una vez me llamaron para ayudarme a rastrear un bloqueo periódico en un lector EDIF. Casi de inmediato comencé a tener dolor de cabeza. El autor original parecía sentir que Yacc lo iba a penalizar por el espacio en blanco, y su gramática Yacc era un desastre denso e ilegible. Pasé un par de horas formateándolo, agregando reglas para las terminales que faltaban a medida que aparecían, estructurando las declaraciones para evitar el crecimiento de la pila, y voila, el accidente se extinguió.
Así que recuerde, por cada vez que espera mientras Yacc procesa su gramática, habrá miles de ejecuciones con el analizador generado. ¡No seas barato con el espacio en blanco!
Una vez tuve que mantener una aplicación C heredada que había sido escrita y mantenida previamente por algunos programadores que habían perdido la voluntad de programar (y posiblemente vivir). Tenía demasiados WTF para mencionar, pero recuerdo una función booleana que en varios casos especiales devolvería TRUE + 1, TRUE + 2, etc.
Luego leí el ensayo de Roedy Green y me reí mucho, hasta que me di cuenta de que la razón por la que me pareció gracioso era que reconocía la mayoría de los ejemplos del código que mantenía. (Ese ensayo se ha vuelto un poco hinchado durante años de adiciones, pero aún vale la pena mirarlo).
Mantenimiento de aplicaciones ASP para una empresa específica que contrata desarrolladores para mantener sus contrataciones anteriores ... Todas estas aplicaciones no están documentadas, ni hay ningún comentario.
Cada función se copia y pega en cada página ASP. Así que no hay funciones definidas o cualquier cosa ... Todos los días estoy paralizado por sus entornos, porque primero tengo que desconectarme a un servidor, para eludir el DMZ . Después de eso tengo que ir a un servidor de producción donde tengo que hacer los cambios.