Código sin sentido en su fuente

34

He escuchado historias de esto de codificadores senior y he visto algunas de ellas. Parece que hay más de unas pocas instancias de programadores que escriben código sin sentido. Veré cosas como:

  • Llamadas a métodos o funciones que no hacen nada de valor.
  • Verificaciones redundantes realizadas en un archivo de clase, objeto o método separado.
  • if declaraciones que siempre se evalúan como verdaderas.
  • Hilos que se desprenden y no hacen nada notable.

Sólo para nombrar unos pocos. Me han dicho que esto se debe a que los programadores quieren hacer que el código sea confuso intencionalmente para aumentar su propio valor para la organización o asegurarse de repetir los negocios en el caso de un trabajo contractual o subcontratado.

Mi pregunta es. ¿Alguien más ha visto un código como este? ¿Cuál fue su conclusión de por qué ese código estaba allí?

Si alguien ha escrito un código como este, ¿puede compartir por qué?

Ali
fuente
44
if (false) {...}¡Los bloques son geniales para comentar código! </sarcasm>
dlras2
18
Nunca atribuya a la malicia lo que se explica adecuadamente por la estupidez , especialmente en el desarrollo de software donde los ataques rápidos temporales rara vez son temporales.
wildpeaks
1
@ dlras2 giro de la trama: #DEFINE falso verdadero :)
Silviu Burcea

Respuestas:

17

He escuchado a desarrolladores que intentan hacer que sus logros de codificación suenen más complejos de lo que realmente son. Nunca escuché a nadie admitir esto, pero he visto un código que cumple con sus criterios que fue creado intencionalmente por prisa o malas prácticas y no por sabotaje. El código que rodea al código difamado puede haberse modificado hasta el punto en que una función en particular ya no es útil.

Alguien tendría que ver este código de primera mano antes de llegar a la conclusión de que solo este desarrollador puede gestionar la complejidad. La mayoría de los gerentes y otras personas de negocios simplemente llegan a esta conclusión porque no entienden ningún tipo de código y no quieren volver a llenar el puesto.

JeffO
fuente
2
Me inclino a darle la respuesta correcta en este caso porque algunos de los códigos que veo simplemente no pueden ser involuntarios ... ¡a menos que alguien sea alto cuando codificaron y pensaron que sería divertido! Creo que otros también tienen razones relevantes para el código inútil, pero el código que veo está en proyectos en los que algunas personas han trabajado y yo soy el primero fuera del equipo de desarrollo original que trabaja en él. Tengo que decir que parece un caso de complejidad añadido al shock y al asombro.
Ali
18
@ Ali: Nunca atribuya a la malicia lo que se explica mejor por incompetencia. En otras palabras, el código probablemente evolucionó a este tipo de desorden porque nadie fue lo suficientemente valiente como para pasar el tiempo de mirarlo y ver qué hace realmente. Todo esto suena como un montón de soluciones rápidas aplicadas, una y otra vez, hasta que todo lo que queda es un montón de asco.
rapid_now
1
+1 para @quickly_now. Eso suele ser lo que termina pasando; todos tienen miedo de tocar cualquier cosa que "funcione" por miedo a romperlo (o, Dios no lo quiera, ¡tomar más tiempo en una tarea para mejorar el código! ¡El horror!). Entonces el código se pudre y se pudre y finalmente se derrumba muchos años más adelante.
Wayne Molina
@ Ali, ha habido casos en los que se ha descrito un código que parece el más semántico y razonable porque probablemente pienso que es divertido o presumir. Y viceversa, conmigo viendo el código de otras personas. Nunca se sabe quién está loco, y la mayoría de las veces todo se reduce a la experiencia y las preferencias. (no hablamos de código objetivamente malo aquí, solo que tales descripciones son fácilmente arrojadas)
Mihail Malostanidis
73

No he visto el código de esta manera, pero he visto el código que parece inútil o no tiene sentido por las otras razones:

  1. Compatibilidad con versiones anteriores. Encontraste una forma mucho mejor de hacer las cosas, pero debes mantener la API / función antigua (y ahora no muy útil) porque algún módulo de terceros puede estar usando esta API / función para algo. Incluso si la función no hace nada útil, su ausencia podría romper algún código.

  2. Codificación defensiva. Usted sabe que las verificaciones en este código no tienen sentido porque esto ya se verificó en otro lugar. Pero, ¿qué pasa si alguien cambia este código en otro lugar y elimina o cambia las comprobaciones para que ya no coincidan con sus condiciones previas?

  3. Cultivo orgánico. En los grandes proyectos, a lo largo de los años, muchas cosas cambian, y resulta que algunos métodos que se usaron antes ya no se usan, pero nadie se molestó en eliminarlos ya que nadie hizo un seguimiento de si se usa o no este método específico, simplemente refactorizaron sus piezas de código y por casualidad sucedieron, todos se detuvieron para usar este método. O condiciones que alguna vez tuvieron significado, pero la aplicación se refactorizó en otros lugares para que esa condición se volviera siempre cierta, pero nadie se molestó en eliminarla.

  4. Sobre diseño. La gente puede codificar algunas cosas "en caso de que lo necesitemos" y en realidad nunca lo necesitan. Como "generemos un hilo en caso de que tengamos que hacer un trabajo fuera de línea" y luego nadie pide hacer nada fuera de línea y el programador lo olvida y pasa a otros proyectos (o tal vez incluso a otra compañía) y ese código permanece allí para siempre porque nadie sabe por qué está allí o si es seguro eliminarlo.

Entonces, aunque nunca lo he visto por malicia o por un enfoque equivocado de la seguridad laboral, he visto toneladas de veces que sucede como resultado natural del desarrollo de software.

StasM
fuente
22
Creo que # 3, Organic Growth explica una gran fracción del Código Inútil que he visto en el trabajo. Pero las 4 razones suponen un programador inteligente. Algunos códigos inútiles se derivan de que alguien no comprende lo que debe suceder y lo que no, y deja mucho código por miedo a cambiar lo que funciona (más o menos).
Bruce Ediger
2
He visto el n. ° 4 en mi proyecto: a menudo no se hace a propósito para tener más poder dentro de la empresa, sino que hay personas que siempre intentan crear una solución más general, incluso si no es necesaria. Con respecto al n. ° 2, lo uso mucho por las razones que usted explicó: en mi humilde opinión, incluso la función o el método más pequeño no debe hacer ninguna suposición sobre cómo funciona o cambiará el resto del código. En cambio, mi código sigue el patrón simple: "si la entrada es correcta, la salida es un error". Esto sigue el principio general de diseño de minimizar las dependencias.
Giorgio
3
También olvidaste: malos desarrolladores. Algunas personas que escriben código no deberían hacerlo, y en muchas tiendas no existen buenos procesos de revisión.
Joe
20

Mi pregunta es. ¿Alguien más ha visto un código como este? ¿Cuál fue su conclusión de por qué ese código estaba allí?

1) si.

2) En los casos que he visto, lo atribuiría a:

  • Programador inexperiencia
  • El programador no comprende un diseño particularmente complicado y / o mal ejecutado que intenta modificar
  • Programador siendo interrumpido en medio de (digamos) un refactor.
  • Descuido

Ahora tal vez estoy siendo caritativo al respecto, pero mi enfoque general es que es mejor ser indulgente / no confrontar estas cosas, que señalar con el dedo y continuar con la mala calidad. Obviamente, las cosas podrían ponerse bastante malo que algo se tiene que hacer , pero un pequeño empujoncito en la dirección correcta suele ser suficiente.

Por supuesto, no puede adoptar un enfoque tan laissez-faire si la calidad / los errores van a afectar seriamente "al negocio". Pero en esa situación, necesita revisiones de código obligatorias y diligentes de todo, combinadas con un procedimiento de prueba integral.


En mi experiencia, las personas tienden a "ponerse rígidas" sobre el código de baja calidad (al menos en parte) porque ofende sus estándares personales. Está muy bien (personalmente) luchar por la perfección, pero es un poco irracional proyectar sus estándares personales en otras personas. Por el sonido de las cosas (por ejemplo, por la naturaleza de sus ejemplos), esto es lo que está haciendo.

En mi opinión, esto no es productivo y no conduce a una buena relación de trabajo con sus compañeros de trabajo.

Stephen C
fuente
+1 Estaba escribiendo una respuesta y encontré que enumeraba todas las razones que iba a mencionar.
canadiancreed
+1 por ser caritativo. Arregle los errores de alguien sin señalar con el dedo y sus colegas respetarán tanto sus habilidades técnicas como las habilidades de su personal. Arriesgue al autor por su código malo y sus colegas se molestarán por su enfoque y descontarán sus habilidades.
Caleb
13

Todos estos son a menudo síntomas de cómo envejece un proyecto.

 1. Llamadas a métodos o funciones que no hacen nada de valor. Hay muchas ocasiones en que se deja algo de código tal como está (con suerte con una gran advertencia en desuso , pero como la mayoría de los idiomas no tienen esa advertencia, no siempre se sigue ...) porque, en un momento, sirvió propósito genuino y nadie sabía qué podría pasar si se eliminaban las líneas ofensivas.

Me parece recordar esto de un dailywtf:

@deprecated // he might have been crazy enough to use reflection...
boolean getTrue() {
    return false; 
}

 2. Verificaciones redundantes realizadas en un archivo de clase, objeto o método separado. Las capas de comunicación también son imperfectas (¿alguna vez leyeron Mythical Man Month? Si no, ¿qué están haciendo en la computadora? ¡Vaya! ¡LEA!). A menudo, una persona trabajará en algo y luego abandonará el proyecto, y luego el siguiente tipo, al encontrar algún error extraño, lanza un cheque adicional aquí y allá para tratar de eliminarlo. Cuando se elimina el error, las comprobaciones no son porque, bueno, si no está roto, no lo arregles.

 3. afirmaciones que siempre se evalúan como verdaderas. Oh, ya hice este. Obtuve un proyecto una vez, tenía una serie de probablemente 10-15 bloques if / else . Para cambiar el comportamiento, simplemente pongo un true||en el primer bloque. No fue sino hasta meses (¿años?) Más tarde que volví y dije: "Oh, wow, se suponía que este código había sido destruido pero nunca lo fue"

 4. Hilos que se desprenden y no hacen nada notable. Me imagino una línea de pensamiento como esta:

  1. ¡Lo sé! ¡Puedo manejar estos dos problemas de forma asíncrona! Crearé hilos foo y bar.
  2. (dos meses después) Huh, ya sabes, la funcionalidad de bar es un poco mejor en foo. Voy a mover un poco más.
  3. (un año después) Sabes, poner estas otras cosas de bar en foo tiene sentido.
  4. (muchos, muchos años después) "Oye, este barhilo no parece estar haciendo nada, ¿podemos eliminarlo?" "Mejor no, ha estado allí muchos, muchos años ..."
cwallenpoole
fuente
55
+1 para "Mejor no, ha estado allí muchos, muchos años ..." - esto sucede una y otra vez. Miedo a eliminar por temor a las consecuencias ("¿Cómo probamos que no hemos roto algo", especialmente si no hay pruebas unitarias).
rapid_now
11

Soy un poco más optimista. Creo que lo que has descrito a menudo ocurre cuando el código se refactoriza descuidadamente.

Jonathan Khoo
fuente
13
Aunque es difícil, nunca atribuyas a Malicia lo que puede explicarse por estupidez.
Bruce Ediger
8

Los viejos me contaron sobre una época en que los consultores pagaban por la cantidad de líneas de código que producían. Y así maximizaron las ganancias utilizando construcciones asombrosamente largas.

Hoy en día siempre asumo que el chico todavía está aprendiendo el idioma mientras hace el trabajo. Y tiene prisa.

revs ZJR
fuente
Habla sobre cortarte la nariz para fastidiar tu cara. Supongo que está bien si nunca tienes que mirar el código nuevamente.
JeffO
6

La mayoría de las respuestas se reducen a estos dos hechos simples:
[1] El código refleja la historia del código, y
[2] El código refleja el futuro esperado del código.

He escrito funciones que no hacen nada de valor, EN EL ENTORNO DE APLICACIÓN ACTUAL dadas las ESPECIFICACIONES ACTUALES, pero pueden ser necesarias en el futuro.

He escrito declaraciones if que, EN PRESENTE, siempre se evalúan como verdaderas. Pero tal vez en el pasado podría ser falso.

En cuanto a las verificaciones redundantes, oye, no confío en otro código, ni siquiera confío en mi propio código. Si un módulo depende de que N sea 1 o 2 o 3, es mejor que se asegure de eso y se bloquee informativamente si no lo es. Es ilegítimo que el Módulo B explote porque el Módulo A se equivocó; Es bastante legítimo que el Módulo B se queje de que el Módulo A se equivocó. Y recuerde que, el próximo mes, ese parámetro puede provenir del Módulo C, aún no escrito.

Andy Canfield
fuente
1
Yo llamo a eso mala codificación. Espera que lo necesite en el futuro, pero eso rara vez sucede. YAGNI Escribir un if que siempre se evalúa como verdadero es un esfuerzo desperdiciado y confunde a la persona que, para empezar, debe agregar una funcionalidad muy diferente. Ese parámetro que viene el próximo mes puede esperar hasta que se agregue el próximo mes. El código de desorden AHORA no tiene sentido.
Andy
1
if (language = 'en' o language = th ') - ¿quizás el próximo mes intentemos chino? if (! isset ($ TITLE)): se supone que todos los módulos establecen $ TITLE, pero tal vez alguien algún día lo deletree mal. if (file_exists ($ TARGET)): un buen código ya habrá creado el archivo, pero tal vez hay un error del sistema y no se creó. Mi código de interfaz PHP / MySQL estándar siempre comprueba si hay errores, aunque nunca he detectado uno todavía.
Andy Canfield
3

Lo he visto varias veces, de hecho, ayer, tengo que fusionar algunos de los códigos de mis jefes en mi nueva aplicación. En su caso, se debe a una falta general de habilidad y comprensión y a la creencia de que él cree que es un desarrollador bastante hábil.

'Llamadas a métodos o funciones que no hacen nada de valor'. y 'si las declaraciones que siempre se evalúan como verdaderas' son un problema importante con su código.

DBlackborough
fuente
3

Sospecho que aunque muchos han visto código que tiene estos problemas, pocos confesarían escribir lo mismo. Con toda probabilidad, lo que está viendo es la podredumbre acumulada del software: alguien agrega algo, lo que realmente no funciona, el siguiente mantenedor agrega un código de protección más adelante en la cadena para protegerse contra la condición que no se verificó correctamente en el primer momento. lugar; entonces alguien recibe un informe del problema y agrega aún más blindaje contra una instancia específica de un problema; otra persona agrega una verificación más general y se olvida de eliminar parte del antiguo código agregado anteriormente que trataba con síntomas más específicos, etc.

Luego está el problema de la limpieza del código: no hay incentivo especial para eliminar lo que parece ser el código muerto, y un tremendo incentivo no hacerlo, porque si usted no entiende el código completo, su valoración de que el código es "muerto" voluntad ser defectuoso, y terminarás rompiendo el sistema.

Scott C Wilson
fuente
2
  • Llamadas a métodos o funciones que no hacen nada de valor.

No necesariamente malo. Los métodos en una clase base a menudo llaman métodos vacíos que se consideran puntos de anulación para subclases. Ejemplo: UIView de Cocoa Touch tiene un -didAddSubview:método documentado que no hace nada en la versión predeterminada. El -addSubview:método de UIView tiene que llamar -didAddSubview:aunque no haga nada porque las subclases pueden implementarlo para hacer algo. Los métodos que no hacen nada y las razones para ellos deben documentarse, por supuesto.

Si una función / método vacío o inútil está obviamente allí por razones históricas, debe eliminarse. Eche un vistazo a las versiones anteriores del código en su repositorio de código fuente si no está seguro.

  • Verificaciones redundantes realizadas en un archivo de clase, objeto o método separado.

Es difícil decir si está bien sin algún contexto. Si las verificaciones se realizan claramente por la misma razón, puede significar que no hay una separación clara de responsabilidades y se requiere una refactorización, especialmente cuando ambas verificaciones dan como resultado la misma acción. Si la acción resultante de ambas comprobaciones no es la misma, entonces las dos comprobaciones probablemente se realicen por diferentes razones, incluso si la condición es la misma, y ​​eso probablemente esté bien.

  • si declaraciones que siempre se evalúan como verdaderas.

Hay una gran diferencia entre:

if (1) {
    // ...
}

y:

if (foo() == true) {
    // ...
}

donde foo()siempre vuelve true.

El primer caso ocurre mucho cuando las personas están depurando. Es fácil usar if (0) {...y eliminar temporalmente un fragmento de código mientras intentas aislar un error, y luego cambiarlo 0a 1para restaurar ese código. El ifdebe ser eliminado una vez que haya terminado, por supuesto, pero es fácil olvidar que paso, o para perder uno o dos si lo ha hecho en varios lugares. (Es una buena idea identificar tales condicionales con un comentario que luego pueda buscar). El único daño es la confusión que puede causar en el futuro; si el compilador puede determinar el valor de la condición en tiempo de compilación, lo eliminará por completo.

El segundo caso puede ser aceptable. Si la condición representada por foo()necesita ser probada desde varios lugares en el código, factorizarla en una función o método separado es a menudo lo correcto, incluso si foo()siempre es cierto en este momento. Si es posible que foo()eventualmente regrese false, entonces aislar esa condición en un método o función es una forma de identificar todos los lugares donde el código depende de esa condición. Sin embargo , hacerlo crea cierto riesgo de que la foo() == falseafección no se pruebe y pueda ocasionar problemas más adelante; la solución es asegurarse de agregar pruebas unitarias que prueben explícitamente el falsecaso.

  • Hilos que se desprenden y no hacen nada notable.

Esto suena como un artefacto de la historia, y algo que podría identificarse durante una revisión de código o mediante la elaboración periódica de perfiles del software. Supongo que podría crearse intencionalmente, pero me cuesta imaginar que alguien realmente lo haría a propósito.

Caleb
fuente
1

Sucede. Muy a menudo en realidad.

A veces, estos callejones sin salida de codificación son más como viejos senderos de cabras que se han deteriorado cuando se ha instalado una autopista más eficiente / moderna / rápida a su alrededor.

En otras ocasiones (y probablemente soy culpable de esto), son bases para la extensión del software cuando se solicita un conjunto de características / funciones informadas pero no confirmadas. (A veces, poner un poco de trabajo en la compilación inicial proporcionando asas y similares para el trabajo posterior que planea "atornillar" puede facilitar la vida, cuando sucede, o más difícil / desordenado si el trabajo posterior no lo hace participar en concursos.)

Y, mucho de esto se debe directamente al viejo "Si no está roto, no lo encajes". A veces, extraer código que no entiendes, o crees que no se usa, puede causar la versión de programación de "The Butterfly Effect". Han tenido que suceder una o dos veces también.

Luke Stevenson
fuente
1

A veces tendré un valor booleano global establecido en verdadero, y más adelante en mi código un if (bool), luego, durante el tiempo de ejecución, podría establecer un punto de interrupción en la instrucción if y cambiar el valor booleano para probar algo.

Patricio
fuente
0

Me opongo a que las if truedeclaraciones se clasifiquen indiscriminadamente como "código sin sentido".

Hay un caso legítimo para usar un if (1) { ... }bloque en el código C que quiere ser compatible con el estándar C que insistió en que las definiciones de las variables estén al comienzo de una función, o simplemente quiere que las variables locales se definan de la manera más local posible.

switch (i) {
    case 23:
        if (1) {
            /* I can declare a local var here! */
        }
        break;
}
ndim
fuente
55
No hay necesidad de 'if (1)', ¿por qué no solo tener el bloque?
FigBug
3
Tanto C / C ++ como C #, y estoy bastante seguro de que Java (así como probablemente muchos otros lenguajes similares) permiten bloques de instrucciones anónimos; hay necesidad de una if, whileo una construcción similar. Es poco probable que sea muy limpio, pero ciertamente está permitido según las especificaciones del idioma.
un CVn
0

Un profesor mío me contó una historia un día que un empleador anterior les pagaría en función de la cantidad de líneas que completaron. Entonces, escribieron varias docenas de funciones alineadas que nunca fueron llamadas. Parece una buena razón para escribir código sin sentido.

Casey
fuente
0

Como @Andy mencionó, un gran componente que he visto está rompiendo YAGNI .

Alguien comienza con una suposición acerca de lo que serán todos los elementos en lugar de lo que muchos elementos pueden necesitar , lo cual es una confusión de las relaciones "es un" y "tiene un" .

Esta confusión conduce a una estructura rígida de herencia y luego son métodos que no se implementan porque nunca se los llama, la lógica repetida en la que se necesita ajustar las porciones y, en general, flujos de trabajo extraños que no se alinean con el modelo de negocio.

Al igual que muchos otros aquí, no he visto esto maliciosamente, pero por falta de conocimiento del buen diseño y el intento de hacer que los demás lo vean así. Es curioso, parece que los desarrolladores aún menos conocedores parecen mejorar en este aspecto, porque al menos su código no termina demasiado diseñado ad naseum. ( Principio KISS )

MaxWell
fuente