¿Cuáles son algunos malos olores en la programación?

En mi experiencia, el olor más malo es: los desarrolladores tienen miedo de cambiar el código.

//
// Estimado mantenedor:
//
// Una vez que hayas terminado de tratar de “optimizar” esta rutina,
// y me he dado cuenta del terrible error que fue,
// incremente el siguiente contador como advertencia
// al siguiente chico:
//
// total_hours_wasted_here = 42
//


Trabajé como consultor durante 4 años. Durante este tiempo, trabajé en docenas de diferentes equipos y diferentes compañías. Por lo general, cuando me uní a un nuevo equipo, los conocí en el momento de la parada, luego me presenté y escuché sus actualizaciones. Cuando escuché algo como “Esa parte del código es complicada, así que tuve que …”, “No estoy seguro de si mi cambio es correcto, porque no hubo pruebas …”, “Necesitaré ayuda para cambiar esta parte del código”. código, porque no es legible … “ . Sabía que mi trabajo los ayudaría a refactorizar su código.

Volviendo a su pregunta, describiré algunos olores con ejemplos (¡no haga eso en casa!):

  • El código no es legible . Algunos problemas que puede encontrar son, muchas variables de una sola letra, nombres de clase y método que no describen lo que hacen, el método es demasiado largo, número mágico en todas partes. Algunas personas podrían decir que no hay comentarios también es un problema. Creo que si el código es legible, no necesita comentarios. Sin embargo, a veces está cambiando un código existente y el código en sí no es legible. Entonces es útil agregar comentarios para aclarar qué hace ese código.

public Integer somethingSome (int x, int y) {
int z = (x * y * 3000) – 500
devuelve z * z;
}

¿Qué demonios hace eso?

  • Grandes métodos Idealmente, un método debe hacer solo una cosa y su nombre debe representar lo que hace esta cosa. Es por eso que los nombres de los métodos deben tener verbos para describir lo que hacen. Desafortunadamente, no es difícil encontrar métodos grandes que hagan varias cosas al mismo tiempo, especialmente en sistemas antiguos. Es un método de cambio bastante desafiante que tiene muchas responsabilidades, sin romper nada más. Por lo general, el método grande también se acompaña de varios parámetros, lo que conduce al siguiente olor

public Integer somethingSome (int x, int y) {
int z = (x * y * 3000) – 500
// 150 líneas de código aquí
devuelve z * w + 234;
}

Oh bueno, está empezando a complicarse

  • Métodos complejos Si un método es grande, por lo general, también tiene muchos parámetros, dependencias e integraciones. Realmente se entiende cuál es el resultado de una entrada específica. ¡Hace las pruebas realmente difíciles!

public Integer somethingSome (int x, int y, String a, long d, List l) {
int z = (x * y * 3000) – 500
// 150 líneas de código aquí
si (x == 123) {
return z * aClass.thatDoesSomething (w) + 234;
}
if (z> y || a == null) {
return Integer.valueOf (l.get (5)) + anotherClass.thatIntegratesWithSomethingElse (2);
}
// más 10 si y elses aquí
}

This.is.not.from.this.world

  • No hay pruebas ¡Las pruebas son una buena fuente de documentación! Describen perfectamente cómo se comporta un método cuando especifica una entrada y espera una salida. Desafortunadamente, la mayoría de las veces, estos métodos complejos no tienen pruebas en absoluto. ¿Por qué alguien crearía pruebas para semejante bestia?

// ¿dónde están las pruebas? : ‘(

  • Bonus: comentarios divertidos. Por lo general, este tipo de código desordenado tiene muchos comentarios divertidos. Bueno, la gente necesita divertirse, ¿verdad? https://stackoverflow.com/questi…

/ **
* Para las almas valientes que llegan tan lejos: ustedes son los elegidos,
* los valientes caballeros de la programación que trabajan sin descanso,
* arreglando nuestro código más horrible. Para ustedes, verdaderos salvadores, reyes de los hombres,
* Digo esto: nunca te rendiré, nunca te decepcionaré,
* Nunca correré y te abandonaré. Nunca te haré llorar
* Nunca voy a decir adiós. Nunca diré una mentira y te lastimaré. * /


Bueno, también hay otros olores, pero esos son los más frecuentes que encontré. Afortunadamente, hay esperanza! Hay un libro que tiene algunas técnicas sobre cómo refactorizar este tipo de código. Trabajo efectivo con código heredado: Michael Feathers: 0076092025986: Amazon.com: Libros

Buena suerte y no olvides el comentario anterior. ¡Nunca te rindas!

Cuando me encuentro con estos olores de código “inmediato” sé “aquí hay dragones”.

  • Formateo inconsistente: el formateo del código es importante. El código bien formateado ayuda al desarrollador a ver inconsistencias que podrían ser errores o detectar código repetitivo que podría expresarse de manera más concisa. O el codificador original era tan descuidado o inexperto, o había varios codificadores que trabajaban en un ámbito demasiado estrecho.
  • Falta total de formateo: hoy en día no es un problema, pero cualquier código que carece de sangría grita “el novato simplemente hackeando las cosas”. Es casi imposible de leer por los mantenedores, y mucho menos el desarrollador original.
  • Modificadores de acceso excesivamente público: cuando las propiedades y métodos de los miembros no están estrictamente bloqueados, hay dos problemas generales que parecen seguir:
  • El desarrollador original ha abusado del acceso , rompiendo abstracciones y separando preocupaciones. El resultado es el clásico código de espagueti.
  • El mantenedor de código usa métodos que cambian fraccionalmente el estado. ¡Loco!
  • Métodos o clases inusualmente largos: son un signo de descargas cerebrales no estructuradas. Difícil de mantener y con frecuencia propenso a errores, pruebas o no.
  • Anidado cualquier cosa más allá de los niveles de 2 ish en un solo método. Relacionado con lo anterior. La complejidad mata.
  • Nombres de métodos hereditarios: por ejemplo, doSomething (), doSomething2 (), doSomething3 (), etc. Las características del lenguaje no se están utilizando correctamente o el codificador está siendo flojo al describir el nuevo método. En raras ocasiones, esto puede ser válido para las API externas. Pero raramente.
  • Métodos / clases / paquetes muertos: apunta al codificador o al mantenedor que pierde contexto y posiblemente le falta algo serio.
  • Comentó bloques de código: el control de código fuente es tu amigo. El código comentado envejece rápidamente y distrae.
  • Dependencias sospechosas de bibliotecas externas: las bibliotecas con mantenimiento deficiente son malas, especialmente si provienen del desarrollador original

  • Encuentro esta lista de Patrones de diseño y refactorización para cubrir “olores de codificación” fácilmente evitables razonablemente bien:

    Estos son signos generales centrados en la codificación . También debe tener en cuenta cómo hacer las cosas bien al codificar en un idioma específico. Por ejemplo, hay muchas guías de estilo en línea específicas del idioma [1] [2] [3] [4] que pueden ayudarlo a identificar lo que podrían ser malos signos al aprender cuáles son buenos de la experiencia de los desarrolladores detrás del software utilizado por millones de personas diariamente.

    Además, como otros han señalado, los antipatrones [5] son ​​”malos olores” de la ingeniería de software cuando se trata de escalar lo que sea que esté construyendo. Estos no se limitan a la codificación, sino que pueden estar presentes en otras partes del proceso (por ejemplo, diseño, pruebas, implementaciones, dependencias, escalado).

    Notas al pie

    [1] NYTimes / aim-c-style-guide

    [2] http://google.github.io/stylegui

    [3] Guía de estilo de Google Python

    [4] airbnb / javascript

    [5] Patrones de diseño y refactorización

    Hay pocas clases de olores, a medida que profundiza en un producto de software.

    En el nivel de Arquitectura / Aplicación, encontré dos cosas que me molestaron más:

    Duplicación de código. Si trabaja en una gran organización, entregando una serie de productos, apuesto a que hay una duplicación de código innecesaria en todos los proyectos. Pero incluso dentro de un proyecto

    Complejidad innecesaria . Diseñe patrones que se usan por el simple hecho de usarlos, que simplemente complican el código y hacen el mantenimiento, agregando nuevas características extremadamente engorrosas. Cuanto más tiempo tenga que mantener el código, peor será. Otra forma de agregar complejidad, en un nivel de abstracción más bajo, es hacer que su código cubra todas las situaciones posibles: cubra todas las combinaciones de entrada teóricamente posibles, etc. Sin embargo, a menudo eso significa escribir “código muerto”, el código que nunca se ejecutará. Eso sucede a menudo cuando los codificadores pierden (o no tienen) el “panorama general”: no conocen el dominio de la aplicación lo suficientemente bien, cómo utilizará el software el usuario en el campo.

    Si observa el diseño a nivel de clase, puede encontrar una descripción general bastante buena de los olores aquí.

    Hay otra cosa más: “antipatrón”. Su pregunta se relaciona con los patrones de ingeniería de software, pero los otros también son interesantes: consulte aquí Anti-patrón

    Hola,

    Me acaban de asignar a un proyecto donde hay un fuerte abuso del mecanismo de señal / ranura de Qt. Si se usa bien, puede obtener objetos sueltos. Pero en el código actual, las señales y las ranuras aparentemente se usan deliberadamente para ofuscar el código. Tal vez el autor intentó trasladar su pensamiento de brainfuck a C / C ++. Las conexiones y desconexiones son ubicuas. Este es un software integrado, donde varios dispositivos deben comunicarse entre sí. La forma en que el software logra hacerlo tan bien dado su diseño es uno de los mayores misterios de este universo. Por supuesto, no hay tiempo para rediseñar todo y necesito hacer trucos sucios para que los clientes se quejen mucho después de que me haya mudado a otro trabajo.

    También en este código: nombres cortos, ininteligibles, variables y una gran cantidad de constantes mágicas. Y nunca se puede saber qué hace la clase “EthernetClient”. Tiene algo de código alrededor de un QTcpSocket, pero para mayor claridad, todo esto ha sido comentado. En cambio, la clase hace una conexión en serie … Lo que falla es que no han redefinido el operador + () para hacer una resta.

    Para terminar todo esto, hay muchos comentarios en los que no los necesita y no hay comentarios en los que hubiera sido muy útil.

    En resumen, estoy trabajando en una colección de antipatrones. Parece que hay un patrón para usar antipatrones en todo el lugar.

    Cuando la forma en que las personas hablan sobre su código, sus pruebas y su proceso de desarrollo, no coincide con la evidencia de sus propios ojos al leer el código.

    Siempre lea el código fuente tan pronto como tenga la oportunidad, y decídase sobre su calidad. ¿Está bien factorizado en clases / métodos con un significado claro? ¿Son las piezas de tamaño manejable? ¿Existen funciones / métodos que tienen varios cientos de líneas de largo? ¿Hay comentarios útiles significativos? ¿Lo que dice en los comentarios realmente coincide con lo que hace el código? ¿Maneja los punteros nulos y otros casos de esquina de forma segura? ¿Hay pruebas? Si se encuentra con una combinación de circunstancias que no puede manejar correctamente, ¿falla de una manera fácilmente reconocible? ¿Tiene un enfoque coherente y plausible para todas las fases de la vida útil de un objeto, por ejemplo, inicializar a todos los miembros, tener alguna política comprensible para que el objeto se limpie y libere correctamente todos los recursos? ¿Prueba los códigos de retorno / error en todas las llamadas del sistema o llama a otras bibliotecas y maneja los errores de manera adecuada?

    Está bien que algunos tipos de código no sean perfectos en todas estas preguntas. Lo que debería ser una gran bandera roja es leer el código y juzgar que es escamoso / incompleto / con errores / desordenado en varios aspectos, y luego hablar con los desarrolladores y gerentes que anteriormente tenían la responsabilidad sobre él y escucharlos decirle que todo es maravilloso.

    Un corolario es que si ha escrito algún código, y alguien viene y le pregunta al respecto, debe estar bien preparado para decirle qué tiene de malo: qué casos no maneja, si va a escalar mal, lo que habría solucionado si hubiera tenido tiempo, brechas en las pruebas.

    Ningún código es perfecto. Algún código está “bajo control”, ya que el código actualmente tiene deficiencias, pero esas deficiencias se entienden bien. Y la mayoría del código está fuera de control, porque fue escrito a medias por alguien que solo escribe un código a medias o alguien puede escribir un buen código pero no tuvo el tiempo (esas personas están en demanda de combatir incendios) por todo el lugar).

    Como programador de computadoras, los siguientes son algunos malos olores en la programación que siento

    1. Formateo
      El formato es muy importante en la programación, un programa debe estar escrito de tal manera que sea comprensible para todos. El uso de punto y coma o llaves en las posiciones correctas lo hace mucho más legible.

    MAL ENFOQUE

    BUEN ENFOQUE

    1. Comentarios
      Los comentarios correctos deben ser el hábito de un programador, los comentarios hacen que el programa sea legible y comprensible, también hace que su programa sea fácil de diagnosticar. Además, si tiene algunos errores en los comentarios de salida, también le ayuda a comprender.
    2. Programación procesal
      La programación de procedimientos en estos días es obsoleta, por qué uno debería escribir el mismo código muchas veces cuando puede escribirse una vez y usarse muchas veces con el enfoque OO.
    3. Métodos inútiles
      La creación de métodos inútiles o irrelevantes o innecesarios también es común, por ejemplo, ¿crea un método para tomar dos entradas y luego crea otro método para realizar operaciones en ellas? ¿Por qué hacer eso? O, en otro caso, crea un método para tomar datos de entrada y crea otros 4 métodos para operaciones básicas (+ – / *), puede usar un interruptor o if / else para hacer todo eso en un método.

    Hay muchos, pero aquí están mis dos favoritos:

    1. La trampa booleana:

    doSomething (false) // ¿Esto significa * no * lo haces?
    doSomething (true) // ¿Esto significa * realmente * hacerlo?

    función doSomething (booleanThatControlsSomethingMysterious) {

    }

    Funciones como esta casi siempre deberían ser:

    doSomething ({someOption: true})

    2. Ye Olde ‘Infinite Loop esperando que suceda’

    while (verdadero) {
    hacer algo()
    si (condición) se rompe
    }

    Ahora, hay algunos casos en los que usar while (true) está bien, la mayoría de las veces estos son solo fragmentos de código frágiles que esperan causar bloqueos

    • Métodos más largos que una pantalla más o menos.
    • Métodos que toman parámetros booleanos para cambiar su comportamiento (en su lugar, tienen dos métodos separados y extraen el código común a un método privado).
    • Método_y_nombre_de_variable_informable, incoherente e incorrecto. En general, creo que uno debería adherirse a algún estándar, como el esquema de nomenclatura de Java, que realmente funciona y está bien establecido.

    En Java / orientado a objetos, los métodos estáticos son generalmente olor a código, aunque hay excepciones, es decir, java.lang.Math. Si todos sus métodos son estáticos, está haciendo algo mal.

    More Interesting

    ¿Se incorporan las mejores prácticas modernas de desarrollo (TDD, patrones de diseño, separación de preocupaciones, etc.) en los cursos universitarios de desarrollo de software?

    ¿Puedo pensar en mi futuro como el de un probador de software?

    ¿Vale la pena aprender COBOL hoy en día?

    ¿Por qué las grandes empresas contratan a grandes ingenieros y les piden que hagan un trabajo mundano? ¿Por qué no pueden automatizarse estas tareas cotidianas, como las operaciones de desarrollo, utilizando IA?

    ¿Son los compiladores la pieza de software más difícil de desarrollar?

    ¿A qué compañía te unirías si te ofrecieran un trabajo como ingeniero de software, BMW o Facebook? ¿Cuáles son algunas de las ventajas y desventajas de trabajar para cada empresa?

    ¿Cómo puede probar qué producto de software es 'mejor' para una competencia competitiva de desarrollo de software?

    ¿Por qué las páginas web enumeran varias fuentes (Ej: font-family: 'Helvetica', 'Times', 'Times New Roman', serif;) en lugar de una sola?

    ¿Cuáles son los 7 desechos del desarrollo de software?

    Como recién graduado, ¿dónde aprendería más y dónde puedo tener una progresión profesional más rápida, Facebook o Google? El papel en Google es un ingeniero de confiabilidad del sitio frente a un ingeniero de software en Facebook.

    ¿Cuál es la pila tecnológica de SurveyMonkey?

    Para convertirse en un experto en seguridad de la información, ¿qué debe hacer un desarrollador de software con experiencia de 6 años?

    Cómo medir el éxito de un proyecto de software típico (por ejemplo, un producto de software desarrollado para un cliente que lo vende)

    ¿Es necesario poseer habilidades de programación para probadores de software?

    Cómo implementar HP UFT en un proceso ágil