¿Qué perjudica a la capacidad de mantenimiento? [duplicar]

73

Para alguien que aún no tiene mucha experiencia en el mundo real, la noción de código que se puede mantener es un poco vaga, a pesar de que sigue las reglas típicas de buenas prácticas. Intuitivamente puedo decir que el código puede estar bien escrito, pero no se puede mantener si, por ejemplo, transmite información que está sujeta a cambios constantes, pero todavía tengo dificultades para mirar el código y decidir si se puede mantener o no.

Entonces la pregunta es: ¿qué daña la mantenibilidad? ¿Qué debo buscar?

    
pregunta EpsilonVector 05.12.2011 - 08:58

17 respuestas

94

La facilidad de mantenimiento tiene muchos aspectos, pero los más importantes son la acoplamiento suelto y alta cohesión . Esencialmente, cuando trabajas con un buen código, puedes hacer pequeños cambios aquí y allá sin tener que mantener todo el código en tu cabeza. Con un código incorrecto, tendrías que tener en cuenta más cosas: repáralo aquí, y se romperá en otro lugar.

Cuando tienes más de 100 kLOC de código frente a ti, esto es crucial. Las reglas de "buenas prácticas" que se mencionan con frecuencia, como el formateo de código, los comentarios, la denominación de variables, etc., son superficiales en comparación con los problemas de acoplamiento / cohesión.

El problema con el acoplamiento / cohesión es que no es fácil de medir o ver rápidamente. Hay algunos intentos académicos de hacer eso, y quizás algunas herramientas de análisis de código estático, pero no hay nada que yo sepa que pueda dar una estimación confiable de cuánto tiempo va a tener con el código.

    
respondido por el Joonas Pulakka 05.12.2011 - 09:36
66

Código duplicado :

Copiar Pegar es probablemente la operación más costosa cuando se trata de los costos de mantenimiento de los programas.

Por qué molestarse en mover este código a un componente común, simplemente lo copiaré y terminaré con él Sí ... el programador probablemente ahorró una hora de trabajo o al hacer las cosas de esta manera . Sin embargo, más tarde aparece un error y ... sí, por supuesto, se soluciona solo en la mitad del código en ejecución. Lo que significa que más tarde aún se registrará una regresión que dará como resultado otra solución DE LO MISMO .

Esto suena obvio, pero en el gran esquema de las cosas es insidioso. Los programadores se ven bien porque hacen más cosas. El gerente del equipo de desarrollo se ve bien porque lo entregaron a tiempo. Y dado que el problema se encuentra, mucho más tarde, la culpa nunca recae en el verdadero culpable.

Perdí la cuenta de cuántas veces tuve que detener un lanzamiento debido a esas "regresiones" . Ahora mismo perdí un día completo rastreando una solución que ya estaba hecha, corríjala de nuevo y presiona La nueva construcción a través de los aros. Así que sí, una hora para un dev hace seis meses (aproximadamente CAD $ 40) solo costó un día completo para 1 dev senior + medio día para un dev menor + un retraso de un día completo en un proyecto que ya llega tarde ...

tú haces las matemáticas ....

así que el siguiente que haga esto me jura, ¡juro que será mejor que corra rápido porque estaré justo detrás de él!

    
respondido por el Newtopian 05.12.2011 - 09:10
52

Aunque el código que rompe las reglas en las otras respuestas es ciertamente peor de mantener, todo el código es difícil de mantener , por lo tanto, cuanto menos sea tener el mejor . Los defectos se correlacionan fuertemente con la cantidad de código, para que usted tenga más códigos. Cuantos más errores tengas . Una vez que el código supera un cierto tamaño, ya no puedes mantener todo el diseño en tu cabeza y las cosas se ponen mucho más difíciles. Finalmente, más código finalmente significa más ingenieros, lo que significa más costos, más problemas de comunicación y más problemas de administración.

Aunque tenga en cuenta que no se debe escribir código más complejo, sino más corto. El código más fácil de mantener es simple y simplemente no contiene redundancia para mantenerlo lo más corto posible. Delegar tareas a otros programas o API también puede ser una solución viable siempre que las llamadas a la API sean lo suficientemente simples.

En pocas palabras, cualquier código superfluo es un problema de mantenimiento

    
respondido por el jk. 08.11.2018 - 10:48
31

Irónicamente, los intentos explícitos de "probar el futuro" de un sistema a menudo hacen que sea más difícil de mantener.

No codificar números mágicos ni cadenas es una cosa, pero continúe y comience a poner la lógica en los archivos de configuración. Lo que significa que está agregando un analizador y un intérprete para un lenguaje de programación nuevo (y probablemente mal diseñado) oscuro a su carga de mantenimiento.

Las capas de abstracción e indirección agregadas por ninguna otra razón por la cual algún componente podría cambiarse en el futuro y no quiere depender de él directamente son venenos de mantenimiento puro y un excelente ejemplo para < a href="http://en.wikipedia.org/wiki/YAGNI"> YAGNI . La Preguntas frecuentes de SLF4J explica los problemas con esta idea tan común:

  

Dado que escribir un contenedor de registro no parece tan difícil, algunos   los desarrolladores tendrán la tentación de envolver SLF4J y vincularlo solo si   ya está presente en el classpath, por lo que SLF4J es opcional   Dependencia de Wombat. Además de resolver el problema de dependencia,   la envoltura aislará a Wombat de la API de SLF4J asegurando que el registro   en Wombat está preparado para el futuro.

     

Por otra parte, cualquier SLF4J-wrapper por definición depende de SLF4J.   Está obligado a tener la misma API general. Si en el futuro un nuevo y   Se produce una API de registro significativamente diferente, código que utiliza el   El envoltorio será igual de difícil de migrar a la nueva API como código   que utilizaba SLF4J directamente. Por lo tanto, la envoltura no es probable que   Prepare su código para el futuro, pero para hacerlo más complejo agregue un   indirección adicional en la parte superior de SLF4J, que es una indirección en   En sí.

La última media frase sugiere la dulce ironía de que el propio SLF4J es un envoltorio de registro al que se aplica este argumento también (excepto que está haciendo un intento creíble de ser el único envoltorio que necesita para poder sentarse encima de y debajo de todas las demás API comunes de registro de Java).

    
respondido por el Michael Borgwardt 19.02.2013 - 17:48
21

En mi experiencia, un factor importante en la capacidad de mantenimiento es consistencia . Incluso si el programa funciona de manera extraña y absurda, es aún más fácil de mantener si lo hace de la misma manera en todas partes. Trabajar con código que usa diferentes esquemas de nomenclatura, patrones de diseño, algoritmos, etc. para tareas similares, dependiendo de quién lo escribió cuándo, es un PITA importante.

    
respondido por el user281377 05.12.2011 - 09:54
18

Creo que el código que no está probado en la unidad perjudica el mantenimiento. Cuando el código está cubierto por pruebas de unidad, puede estar seguro de que cuando lo cambie, sabrá lo que está rompiendo porque las pruebas de unidad asociadas fallarán.

Las pruebas de unidad junto con las otras sugerencias aquí hacen que su código sea robusto y se asegura de saber cuándo cambia, exactamente qué impacto está teniendo.

    
respondido por el Deco 05.12.2011 - 09:45
15

La única forma verdadera de aprender qué es el código que se puede mantener es involucrarse en el mantenimiento de una base de código grande (tanto correcciones de errores como solicitudes de características).

Aprenderás sobre:

  • acoplamiento suelto y alta cohesión
  • código duplicado
  • pruebas de regresión y refactorización: con un conjunto de pruebas de regresión eficiente, te sientes más seguro para cambiar las cosas.
  • observación y registro de errores: si el software rastrea lo que está mal, el error se localiza más fácilmente.
  • documentación precisa: no del tipo que se escribe una vez al principio del proyecto y nunca se modifica.
  • código ofuscado no documentado que toma la forma de una idea de diseño supuestamente brillante u optimizaciones ala Raíz cuadrada inversa rápida .
  • tradición oral transmitida de una generación de desarrolladores / mantenedores a la siguiente.
respondido por el mouviciel 12.04.2017 - 09:31
5

Tengo un par de "pesadillas" que, aunque nunca las hubiera puesto en la parte superior de esta lista, definitivamente causaron cierta lucha.

El mega objeto / página / función : (el llamado " God Object " anti-patrón ): Poner todo en el método de un objeto (en su mayoría mundo de Java), página (en su mayoría PHP y ASP) , método (en su mayoría VB6).

Tuve que mantener el código de un programador que tenía en un script php: código html, lógica de negocios, llamadas a mysql todo desordenado. Algo como:

foeach( item in sql_query("Select * from Items")) {
<a href="/go/to/item['id']">item['name']</a>
}

Confiar en características oscuras del lenguaje que pueden no ser bien conocidas o están a punto de volverse obsoletas.

Había vuelto a pasar con las variables php y preset. Historia corta: para algunas configuraciones de php v4, un parámetro de solicitud se asignó automáticamente a una variable. así que enlace generaría "mágicamente" $ id con valor '42'.

Esto fue una pesadilla de mantenimiento, no solo porque no era posible saber de dónde provenían los datos, sino también porque php v5 la eliminó por completo (+1), por lo que las personas capacitadas después de esa versión, ni siquiera entenderían qué estaba pasando y por qué .

    
respondido por el dimitris mistriotis 05.12.2011 - 13:22
4

Lo que hace necesario el mantenimiento es que los requisitos son un objetivo móvil . Me gusta ver el código donde se anticipan los posibles cambios futuros, y se piensa en cómo manejarlos en caso de que surjan.

Tengo una medida de mantenibilidad. Supongamos que aparece un nuevo requisito, o un cambio en un requisito existente. Luego se implementan los cambios en el código fuente, junto con la reparación de cualquier error relacionado, hasta que la implementación sea completa y correcta. Ahora ejecute un diff entre la base de código posterior y la base de código anterior. Si eso incluye cambios en la documentación, incluye esos en el código base. Desde diff puede obtener un conteo N de cuántas inserciones, eliminaciones y reemplazos de código fueron necesarios para lograr el cambio.

Cuanto más pequeña es N, como un promedio aproximado de los cambios pasados y futuros de los requisitos, más fácil de mantener es el código. La razón es que los programadores son canales ruidosos. Ellos cometen errores. Cuantos más cambios tienen que hacer, más errores cometen, que se convierten en errores, y cada error es más difícil de encontrar y corregir que hacer en primer lugar.

Por lo tanto, estoy de acuerdo con las respuestas que dicen "No repetirte" (DRY) o lo que se llama código de corte de cookie.

También estoy de acuerdo con el movimiento hacia lenguajes específicos de dominio (DSL) provistos que reducen N. A veces las personas asumen que el propósito de un DSL es ser "compatible con el codificador" al tratar con "abstracciones de alto nivel". Eso no necesariamente reduce N. La forma de reducir la N es entrar en un idioma (que puede ser simplemente cosas definidas en la parte superior de un idioma existente) que se asigna más estrechamente a los conceptos del dominio del problema.

La capacidad de mantenimiento no significa necesariamente que cualquier programador pueda sumergirse directamente en. El ejemplo que uso de mi propia experiencia es Ejecución diferencial . El precio es una curva de aprendizaje significativa. La recompensa es un orden de reducción de magnitud en el código fuente para los diálogos de interfaz de usuario, especialmente aquellos que tienen elementos que cambian dinámicamente. Los cambios simples tienen N alrededor de 2 o 3. Los cambios más complejos tienen N alrededor de 5 o 6. Cuando N es tan pequeño como eso, la probabilidad de introducir errores se reduce mucho, y da la impresión de que "simplemente funciona".

En el otro extremo, he visto un código donde N normalmente estaba en el rango de 20-30.

    
respondido por el Mike Dunlavey 23.05.2017 - 14:40
2

Creo que la capacidad de mantenimiento también está estrechamente unida a la complejidad del problema, que puede ser una cuestión subjetiva. He visto situaciones en las que el único desarrollador pudo mantener con éxito y hacer crecer constantemente una base de código grande, pero cuando otros se ubican en su lugar, aparece un desastre que no se puede mantener, simplemente porque tienen modelos mentales muy diferentes.

Los patrones y las prácticas realmente ayudan (consulte otras respuestas para obtener excelentes consejos). Sin embargo, su abuso puede llevar a más problemas cuando la solución original se pierde detrás de fachadas, adaptadores y abstracciones innecesarias.

En general, la comprensión viene con la experiencia. Una excelente manera de aprender es analizar cómo otros han resuelto problemas similares, intentando encontrar puntos fuertes y débiles en su implementación.

    
respondido por el Bazurbat 05.12.2011 - 12:15
2

Esto no es realmente una respuesta sino un comentario largo (porque estas respuestas ya se han presentado).

Descubrí que esforzarme religiosamente por dos factores: las interfaces limpias y utilizables y la no repetición han mejorado mucho mi código y con el tiempo me han convertido en un programador mucho mejor.

A veces, eliminar código redundante es DIFÍCIL, te obliga a crear algunos patrones difíciles.

Por lo general analizo lo que DEBE cambiar y luego ese es mi objetivo. Por ejemplo, si está realizando una GUI en un cliente para actualizar una base de datos, ¿qué necesita para agregar "Otro" (otro control vinculado a la base de datos?) Debe agregar una fila a la base de datos y necesita la posición del componente, eso es todo.

Entonces, si eso es lo mínimo, no veo NINGÚN código en eso, DEBES poder hacerlo sin tocar tu base de código. Esto es sorprendentemente difícil de lograr, algunos juegos de herramientas lo harán (generalmente de manera deficiente), pero es un objetivo. ¿Qué tan difícil es acercarse? No terriblemente Puedo hacerlo y lo he hecho con código cero de un par de maneras, una es etiquetar nuevos componentes GUI con el nombre de la tabla en la base de datos, otra creando un archivo XML: el archivo XML (o YAML si no te gusta) XML) puede ser realmente útil porque puede vincular validadores y acciones especiales al campo, haciendo que el patrón sea extremadamente flexible.

Además, no lleva más tiempo implementar las soluciones correctamente; para cuando ya ha enviado la mayoría de los proyectos, en realidad es más barato.

Puedo señalar que si dependes mucho de Setters & Getters ("Bean Patterns"), Genéricos & Las clases internas anónimas probablemente NO ESTÁN codificando genéricamente de esta manera. En los ejemplos anteriores, tratar de forzar en cualquiera de estos realmente te jodirá. Setters & Los programadores te obligan a usar el código para los nuevos atributos, los genéricos te obligan a crear instancias de las clases (que requieren código) & Las clases internas anónimas tienden a no ser fáciles de reutilizar en otros lugares. Si realmente está codificando genéricamente, estas construcciones de lenguaje no son malas, pero pueden dificultar la visualización de un patrón. Para un ejemplo totalmente absurdo:

user.setFirstName(screen.getFirstName());
user.setLastName(screen.getLastName());

Se ve bien, no es en absoluto redundante, al menos no de una manera que puedas arreglar, ¿verdad? Pero hace que agregues una línea cuando quieres agregar un "Segundo nombre", por lo que es "Código redundante"

user.getNameAttributesFrom(screen);

No necesita código nuevo para esta tarea; simplemente requiere que algunos atributos en "pantalla" estén etiquetados con un atributo "Nombre", pero ahora no podemos copiar la dirección, ¿qué tal esto?

user.getAttributesFrom(screen, NAME_FIELDS, ADDRESS_FIELDS);

Mejor, una var-args le permite incluir un grupo de atributos (de una enumeración) para recopilar desde la pantalla; aún tiene que editar el código para modificar los tipos de atributos que desea. Tenga en cuenta que "NAME_FIELDS" es una instancia de enumeración simple: los campos en "pantalla" están etiquetados con esta enumeración cuando están diseñados para colocarlos en la (s) categoría (es) correcta (s) - no hay tabla de conversión.

Attribute[] attrs=new Attributes[]{NAME_FIELDS, ADDRESS_FIELDS, FRIENDS_FIELDS};
user.getAttributesFrom(screen, attrs);

Ahora lo tienes donde simplemente estás cambiando "Datos". Aquí es donde normalmente lo dejo, con los datos en el código, porque es "lo suficientemente bueno". El siguiente paso es externalizar los datos si es necesario, para que pueda leerlos desde un archivo de texto, pero rara vez lo es. Las refactorizaciones se "enrollan" mucho como esta una vez que te adquieres en el hábito, y lo que acabo de hacer ^ creó una nueva enumeración y patrón que terminará rodando en muchas otras refactorizaciones.

Finalmente, tenga en cuenta que las buenas soluciones genéricas para problemas como este NO son dependientes del idioma. He implementado una solución sin código por ubicación para analizar una GUI de texto desde la línea de comandos de un enrutador, actualizarla en la pantalla y volver a escribirla en el dispositivo, en VB 3. Solo requiere dedicación al principio de no escribir código redundante, nunca, incluso si tiene que escribir el doble de código para hacerlo.

La Interfaz Limpia (Completamente Factada & no permite que pasen cosas ilegales) también es importante. Cuando factoriza una interfaz entre dos unidades de código correctamente, le permite manipular el código en ambos lados de la interfaz con impunidad y permite que los nuevos usuarios implementen las interfaces de forma limpia.

    
respondido por el Bill K 05.12.2011 - 18:26
2

Además de los patrones mencionados, una de las cosas más importantes es

código legible , cada línea de código que coloque en una base de código se leerá más de 100 veces y, a medida que las personas estén rastreando errores, no querrá que dediquen 30 minutos a intentar averiguar qué lo hace cada vez.

Así que no intente ser inteligente con el código "optimizado" a menos que realmente sea un cuello de botella y luego coméntelo y coloque el código legible (y funcional) original en los comentarios (o en la ruta de compilación alternativa para la prueba) como referencia para lo que debería hacer.

esto incluye funciones descriptivas, nombres de parámetros y variables, y comentarios que explican POR QUÉ se elige un determinado algoritmo en lugar de otro

    
respondido por el ratchet freak 05.12.2011 - 23:16
1

Casi todo lo que viola algunos buenos principios de desarrollo de software daña la capacidad de mantenimiento. Si observa un código y desea evaluar qué tan fácil es mantenerlo, puede elegir algunos principios específicos y verificar cuánto los viola. .

El principio DRY es un buen punto de partida: ¿cuánta información se repite en el código? YAGNI también es muy útil: ¿Cuánta funcionalidad hay que no es necesaria?

Si realiza una investigación en DRY y YAGNI encontrará otros principios que son aplicables como principios generales de desarrollo de software. Si está utilizando algún lenguaje de programación orientado a objetos y desea ser más específico, los principios de SOLID da algunas buenas pautas para un buen diseño orientado a objetos.

El código que viola cualquiera de esos principios tiende a ser menos mantenible. Pongo un énfasis en tender aquí, porque, en general, hay situaciones en las que es legítimo violar (levemente) cualquier tipo de principio, especialmente si se hizo por razones de mantenimiento.

Lo que también ayuda es buscar smells de código . Eche un vistazo en Refactorización: mejora del diseño del código existente de Martin Fowler. Allí puede encontrar ejemplos de olores de código, lo que agudiza su percepción de un código menos mantenible.

    
respondido por el Theo Lenndorff 05.12.2011 - 11:05
1

Demasiadas funciones.

Las características por sí mismas dañan la capacidad de mantenimiento porque necesita escribir código adicional para implementarlas, pero una aplicación sin características no tiene sentido, por lo que agregamos características. Sin embargo, al omitir funciones innecesarias, puede mantener su código más mantenible.

    
respondido por el user107729 05.12.2011 - 16:29
-1
  1. Falta de pruebas unitarias adecuadas
  2. Falta de pruebas de integración adecuadas
  3. Falta de pruebas de rendimiento de evaluación comparativa adecuadas
  4. YAGNI

3 y 4 son, según mi experiencia, los delincuentes más comunes en los sistemas de la vida real. No tener un conjunto adecuado de pruebas de rendimiento / carga hace que sea imposible comparar el impacto que tendrán los cambios futuros en el rendimiento y la estabilidad, y causará una degradación mucho más rápida y forzará refactorizaciones costosas con más frecuencia. YAGNI (lo que se denomina "desperdicio" en el vocabulario Lean ) hará que el sistema sea mucho más difícil de refactorizar y mantener, ya que se perderá mucho tiempo (se intentará con el juego de palabras) para mantener el código y la compatibilidad con versiones anteriores para funciones que ni siquiera se utilizan.

    
respondido por el pap 05.12.2011 - 11:23
-2

El mejor enfoque para la mantenibilidad de mi experiencia es tener el menor código posible y enviar el trabajo a otros (es decir, a los proveedores de software) que realizan el mantenimiento por usted.

Prefiero tener la menor fuente personalizada posible y enviar tanto como sea posible a las hojas de Excel, XML o archivos de propiedades que representan los datos de negocios y proporcionan lectores simples o utilizan una herramienta ETL para hacer la conversión al código de la aplicación.

También trato de encontrar algo que sea estándar (es decir, no hay un solo proveedor) si es posible. Sin embargo, hay algunas cosas que no podemos evitar, por ejemplo. Primavera para la inyección de dependencia antes de JEE6 e Hibernate para ORM antes de JEE5, log4j / commons-logging / slf4j antes de Java 1.4. Al elegir algo basado en el estándar, existe una mayor posibilidad de cambiar de proveedor, especialmente si uno se queda inactivo o tiene un costo prohibitivo y evita los problemas de classpath.

Por supuesto, si usted es el proveedor que proporciona el software, eso es otra cuestión. Sin embargo, si estás lidiando con situaciones de la vida real, a veces es mejor echar dinero al problema en lugar de habilidad y tiempo.

    
respondido por el Archimedes Trajano 05.12.2011 - 14:02
-3

Cualquier cosa que no sea fácil de entender.

La fuerza más destructiva para cualquier tipo de código es el mantenimiento. Desafortunadamente, el mantenimiento también es absolutamente necesario. Si el mantenedor no comprende rápidamente el código, lo enrutará, lo cual es tan destructivo como casi seguro que lo romperá.

Para citar mal a Einstein: Hazlo lo más simple posible, pero no más simple.

    
respondido por el OldCurmudgeon 06.12.2011 - 10:29

Lea otras preguntas en las etiquetas