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é?
coding-style
Ali
fuente
fuente
if (false) {...}
¡Los bloques son geniales para comentar código! </sarcasm>Respuestas:
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.
fuente
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:
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.
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?
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.
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.
fuente
1) si.
2) En los casos que he visto, lo atribuiría a:
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.
fuente
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:
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:
bar
hilo no parece estar haciendo nada, ¿podemos eliminarlo?" "Mejor no, ha estado allí muchos, muchos años ..."fuente
Soy un poco más optimista. Creo que lo que has descrito a menudo ocurre cuando el código se refactoriza descuidadamente.
fuente
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.
fuente
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.
fuente
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.
fuente
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.
fuente
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.
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.
Hay una gran diferencia entre:
y:
donde
foo()
siempre vuelvetrue
.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 cambiarlo0
a1
para restaurar ese código. Elif
debe 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 sifoo()
siempre es cierto en este momento. Si es posible quefoo()
eventualmente regresefalse
, 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 lafoo() == false
afección no se pruebe y pueda ocasionar problemas más adelante; la solución es asegurarse de agregar pruebas unitarias que prueben explícitamente elfalse
caso.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.
fuente
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.
fuente
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.
fuente
Me opongo a que las
if true
declaraciones 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.fuente
if
,while
o una construcción similar. Es poco probable que sea muy limpio, pero ciertamente está permitido según las especificaciones del idioma.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.
fuente
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 )
fuente