¿En qué casos no es mejor el código? [cerrado]

54

Últimamente he refactorizado algunos códigos en el trabajo, y pensé que hice un buen trabajo. Dejé caer 980 líneas de código a 450 y reduje a la mitad el número de clases.

Al mostrar esto a mis colegas, algunos no estuvieron de acuerdo en que esto fue una mejora.

Dijeron: "menos líneas de código no son necesariamente mejores"

Puedo ver que podría haber casos extremos en los que las personas escriban líneas realmente largas y / o pongan todo en un solo método para guardar algunas líneas, pero eso no fue lo que hice. En mi opinión, el código está bien estructurado y es más sencillo de comprender / mantener, ya que tiene la mitad del tamaño.

Estoy luchando para ver por qué alguien querría trabajar con el doble del código requerido para hacer un trabajo, y me pregunto si alguien se siente igual que mis colegas y puede hacer algunos buenos casos por tener más código sobre menos?

    
pregunta PiersyP 31.08.2017 - 01:51

10 respuestas

123

Una persona delgada no es necesariamente más saludable que una persona con sobrepeso.

Un cuento infantil de 980 líneas es más fácil de leer que una tesis de física de 450 líneas.

Hay muchos atributos que determinan la calidad de su código. Algunos simplemente se computan, como Complejidad ciclomática , y Halstead Complexity . Otros están más definidos, como cohesión , legibilidad, comprensibilidad, extensibilidad, robustez, corrección, autodocumentación , limpieza, comprobabilidad y muchos más.

Podría ser, por ejemplo, que al reducir la longitud total del código, introdujiste una complejidad adicional injustificada e hiciste el código más críptico.

Dividir un fragmento largo de código en pequeños métodos podría ser tan perjudicial como podría ser beneficioso .

Pida a sus colegas que le proporcionen comentarios específicos sobre por qué creen que sus esfuerzos de refactorización produjeron un resultado no deseado.

    
respondido por el M.A. Hanin 31.08.2017 - 02:32
35

Curiosamente, un colega y yo estamos actualmente en medio de un refactor que aumentará el número de clases y funciones en un poco menos del doble, aunque las líneas de código se mantendrán casi iguales. . Así que tengo un buen ejemplo.

En nuestro caso, tuvimos una capa de abstracción que realmente debería haber sido dos. Todo estaba abarrotado en la capa ui. Al dividirlo en dos capas, todo se vuelve más cohesivo, y probar y mantener las piezas individuales se vuelve mucho más sencillo.

No es el tamaño del código que está molestando a sus colegas, es otra cosa. Si no pueden articularlo, intente ver el código usted mismo como si nunca hubiera visto la implementación anterior, y evalúelo por sus propios méritos en lugar de solo en comparación. A veces, cuando hago un refactor largo, pierdo de vista el objetivo original y llevo las cosas demasiado lejos. Eche un vistazo crítico al "panorama general" y vuelva a encarrilarlo, tal vez con la ayuda de un programador en pareja cuyos consejos valora.

    
respondido por el Karl Bielefeldt 31.08.2017 - 05:22
18

Una cita, a menudo atribuida a Albert Einstein, viene a la mente:

  

Haz que todo sea lo más simple posible, pero no más simple.

Cuando exageras al recortar cosas, puede hacer que el código sea más difícil de leer. Como "fácil / difícil de leer" puede ser un término muy subjetivo, explicaré exactamente lo que quiero decir con esto: una medida del grado de dificultad que tendrá un desarrollador experto para determinar "¿qué hace este código?" solo mirando la fuente, sin la ayuda de herramientas especializadas.

Los lenguajes como Java y Pascal son infames por su verbosidad. Las personas a menudo señalan ciertos elementos sintácticos y dicen burlonamente que "simplemente están allí para facilitar el trabajo del compilador". Esto es más o menos cierto, excepto por la parte "justa". Cuanto más explícita sea la información, más fácil será leer y comprender el código, no solo por un compilador, sino también por un ser humano.

Si digo var x = 2 + 2; , es inmediatamente obvio que se supone que x es un número entero. Pero si digo var foo = value.Response; , es mucho menos claro qué foo representa o cuáles son sus propiedades y capacidades. Incluso si el compilador puede inferirlo fácilmente, pone mucho más esfuerzo cognitivo en una persona.

Recuerde que los programas deben estar escritos para que la gente los lea, y solo de forma incidental para que las máquinas los ejecuten. (¡Irónicamente, esta cita proviene de un libro de texto dedicado a un lenguaje infame por ser extremadamente difícil de leer!) Es una buena idea eliminar las cosas que son redundantes, pero no quite el código que hace que sea más fácil para los demás seres humanos averiguar qué está pasando, incluso si no es estrictamente necesario para el programa que se está escribiendo.

    
respondido por el Mason Wheeler 31.08.2017 - 06:08
15

El código más largo puede ser más fácil de leer. Por lo general, es lo contrario, pero hay muchas excepciones, algunas de ellas resumidas en otras respuestas.

Pero veamos desde un ángulo diferente. Asumimos que el código nuevo será visto como superior por la mayoría de los programadores expertos que ven las 2 piezas de código sin tener conocimiento adicional de la cultura, la base de código o la hoja de ruta de la compañía. Incluso entonces, hay muchas razones para objetar el nuevo código. Por brevedad, llamaré "Gente que critica el nuevo código" Pecritenc :

  • Estabilidad. Si se sabía que el código antiguo era estable, la estabilidad del nuevo código es desconocida. Antes de que se pueda usar el nuevo código, aún debe ser probado. Si por alguna razón no se dispone de pruebas adecuadas, el cambio es un problema bastante grande. Incluso si las pruebas están disponibles, Pecritenc puede pensar que el esfuerzo no merece la mejora (menor) del código.
  • Rendimiento / escalado. El código antiguo puede haberse escalado mejor, y Pecritenc asume que el rendimiento se convertirá en un problema en el futuro, ya que los clientes y las características pronto se acumularán.
  • Extensibilidad. El código antiguo podría haber permitido la introducción fácil de algunas características que Pecritenc asume que se agregarán pronto *.
  • familiaridad. El código antiguo puede tener patrones reutilizados que se usan en otros 5 lugares del código base de la compañía. Al mismo tiempo, el nuevo código utiliza un patrón sofisticado del que solo la mitad de la compañía ha oído hablar en este momento.
  • Lápiz labial en un cerdo. Pecritenc puede pensar que tanto el código antiguo como el nuevo son basura, o irrelevantes, por lo que cualquier comparación entre ellos no tiene sentido.
  • Orgullo. Pecritenc puede haber sido el autor original del código y no le gusta que las personas realicen cambios masivos en su código. Incluso podría ver las mejoras como un ligero insulto, porque implican que debería haberlo hecho mejor.
respondido por el Peter 31.08.2017 - 11:54
1

Qué tipo de código es mejor puede depender de la experiencia de los programadores y también de las herramientas que utilizan. Por ejemplo, aquí es por qué lo que normalmente se considera un código mal escrito puede ser más efectivo en algunas situaciones que un código bien orientado a objetos que hace uso completo de la herencia:

(1) Algunos programadores simplemente no tienen una comprensión intuitiva de la programación orientada a objetos. Si su metáfora para un proyecto de software es un circuito eléctrico, entonces esperará mucha duplicación de código. Le gustaría ver más o menos los mismos métodos en muchas clases. Te harán sentir como en casa. Y un proyecto en el que tienes que buscar métodos en las clases para padres o incluso en las clases de abuelos para ver qué está pasando puede parecer hostil. No desea comprender cómo funciona la clase principal y, a continuación, comprender cómo difiere la clase actual. Desea comprender directamente cómo funciona la clase actual, y encuentra confuso el hecho de que la información está distribuida en varios archivos.

Además, cuando solo quiere solucionar un problema específico en una clase específica, es posible que no le guste tener que pensar si solucionar el problema directamente en la clase base o sobrescribir el método en su clase de interés actual. (Sin la herencia, no tendría que tomar una decisión consciente. La opción predeterminada es simplemente ignorar problemas similares en clases similares hasta que se informen como errores). Este último aspecto no es realmente un argumento válido, aunque podría explicar algunos de los problemas. oposicion.

(2) Algunos programadores usan mucho el depurador. Aunque en general yo mismo estoy firmemente del lado de la herencia de código y evito la duplicación, comparto parte de la frustración que describí en (1) al depurar código orientado a objetos. Cuando sigues la ejecución del código, a veces sigue saltando entre clases (antecesoras) aunque permanezca en el mismo objeto. Además, al establecer un punto de interrupción en un código bien escrito, es más probable que se active cuando no es útil, por lo que es posible que tenga que esforzarse para que sea condicional (cuando sea práctico), o incluso continuar manualmente varias veces antes del activador relevante.

    
respondido por el Hans Adler 31.08.2017 - 11:41
1

Depende totalmente. He estado trabajando en un proyecto que no permite variables booleanas como parámetros de función, sino que requiere un enum dedicado para cada opción.

Entonces,

enum OPTION1 { OPTION1_OFF, OPTION1_ON };
enum OPTION2 { OPTION2_OFF, OPTION2_ON };

void doSomething(OPTION1, OPTION2);

es mucho más detallado que

void doSomething(bool, bool);

Sin embargo,

doSomething(OPTION1_ON, OPTION2_OFF);

es mucho más legible que

doSomething(true, false);

El compilador debe generar el mismo código para ambos, por lo que no se gana nada usando el formulario más corto.

    
respondido por el Simon Richter 01.09.2017 - 15:15
0

Yo diría que la cohesión podría ser un problema.

Por ejemplo, en una aplicación web, digamos que tiene una página de administrador en la que indexa todos los productos, que es esencialmente el mismo código (índice) que usaría en una situación de página de inicio, para ... simplemente indexar el productos.

Si decides parcializar todo para que puedas mantenerte SECO y elegante, tendrías que agregar muchas condiciones con respecto a si el usuario que está navegando es un administrador o no y desordena el código con cosas innecesarias que lo harán altamente ilegible. de digamos un diseñador!

Entonces, en una situación como esta, incluso si el código es más o menos igual, solo porque podría escalarse a otra cosa y los casos de uso podrían cambiar ligeramente, sería malo ir tras cada uno de ellos agregando condiciones y ifs . Por lo tanto, una buena estrategia sería deshacerse del concepto DRY y dividir el código en partes mantenibles.

    
respondido por el frcake 31.08.2017 - 15:49
0
  • Cuando menos código no hace el mismo trabajo que más código. Refactorizar por simplicidad es bueno, pero debe tener cuidado de no simplificar demasiado el espacio del problema que esta solución encuentra. 980 líneas de código podrían manejar más casos de esquina que 450.
  • Cuando menos código no falla tan fácilmente como más código. He visto un par de trabajos "ref *** toring" realizados en el código para eliminar el intento de captura "innecesario" y otro manejo de errores. El resultado inevitable fue en lugar de mostrar un cuadro de diálogo con un bonito mensaje sobre el error y lo que el usuario podía hacer, la aplicación se bloqueó o YSODed.
  • Cuando menos código es menos mantenible / extensible que más código. La refactorización para la concisión del código a menudo elimina las construcciones de código "innecesarias" en interés de LoC. El problema es que esas construcciones de código, como las declaraciones de interfaz paralelas, los métodos / subclases extraídas, etc., son necesarias en caso de que este código deba hacer más de lo que hace actualmente, o hacerlo de manera diferente. En el extremo, ciertas soluciones personalizadas para el problema específico pueden no funcionar en absoluto si la definición del problema cambia solo un poco.

    Un ejemplo; Tienes una lista de enteros. Cada uno de estos enteros tiene un valor duplicado en la lista, excepto uno. Su algoritmo debe encontrar ese valor impar. La solución general es comparar cada número con cada otro número hasta que encuentre un número que no tenga duplicidad en la lista, que es una operación N ^ 2 veces. También puedes construir un histograma usando una tabla hash, pero eso es muy ineficiente para el espacio. Sin embargo, puede hacerlo en tiempo lineal y espacio constante utilizando una operación XOR a nivel de bits; XOR cada entero contra un "total" en ejecución (comenzando con cero), y al final, la suma en ejecución será el valor de su entero no pareado. Muy elegante. Hasta que cambien los requisitos, y más de un número en la lista no se pueda emparejar, o los enteros incluyen cero. Ahora su programa devuelve basura o resultados ambiguos (si devuelve cero, ¿significa que todos los elementos están emparejados o que el elemento no emparejado es cero?). Tal es el problema de las implementaciones "inteligentes" en la programación del mundo real.

  • Cuando menos código se documenta menos que más código. Ser capaz de leer el código en sí y determinar lo que está haciendo es fundamental para el desarrollo del equipo. Darle un algoritmo mental que escribiste que funciona bien a un desarrollador junior y pedirle que lo modifique para modificar un poco la salida no va a llegar muy lejos. Muchos desarrolladores senior también tendrían problemas con esa situación. Poder entender en un momento dado lo que hace el código, y lo que podría ir mal con él, es clave para un entorno de trabajo en equipo (e incluso solo; le garantizo que el destello de genio que tuvo cuando escribió un 5 El método de línea para curar el cáncer va a desaparecer cuando regreses a esa función buscando curarlo también para el Parkinson.
respondido por el KeithS 01.09.2017 - 15:56
0

El código de computadora debe hacer varias cosas. Un código "minimalista" que no hace estas cosas no es un buen código.

Por ejemplo, un programa de computadora debe cubrir todo lo posible (o, como mínimo, todos los casos probables). Si un fragmento de código cubre solo el "caso base" e ignora otros, no es un buen código, aunque sea breve.

El código de la computadora debe ser "escalable". Un código críptico puede funcionar solo para una aplicación especializada, mientras que un programa más largo pero más abierto puede hacer que sea más fácil agregar nuevas aplicaciones.

El código de la computadora debe estar claro. Como lo demostró otro respondedor, es posible que un codificador de núcleo duro produzca una función de tipo "algorítmico" de una línea que haga el trabajo. Pero el one-liner tuvo que dividirse en cinco "oraciones" diferentes antes de que quedara claro para el programador promedio.

    
respondido por el Tom Au 01.09.2017 - 18:36
-2

Rendimiento computacional. Al optimizar el tendido de tuberías o la ejecución de partes de su código en paralelo puede ser beneficioso, por ejemplo, no realizar bucles de 1 a 400, sino de 1 a 50 y poner 8 instancias. de código similar en cada bucle. No estoy asumiendo que este fue el caso en su situación, pero es un ejemplo donde hay más líneas mejor (en cuanto al rendimiento).

    
respondido por el Hans Janssen 31.08.2017 - 14:15

Lea otras preguntas en las etiquetas