algorithm - index - ¿Cuán complejo debe ser el código?
maintainability index (28)
Buena cita ...
Cualquier tonto puede escribir código que una computadora puede entender. Los buenos programadores escriben código que los humanos pueden entender
- Martin Fowler
Estoy estudiando algoritmos que pueden ayudarme a escribir código más pequeño pero más complejo. En lugar de escribir 150 líneas de instrucciones if-else, puedo diseñar un algoritmo que lo haga en 20 líneas. El problema es que muchos de estos algoritmos pueden ser complejos y requieren mucha matemática para comprenderlos. También soy el único por aquí que los entiende.
Para la mantenibilidad del código, ¿sería mejor simplemente escribir el código como todos los demás, o es mejor usar los algoritmos?
Bueno, el código puede ser bastante difícil de entender y mantener solo debido al volumen si es lo suficientemente grande (vea la mayoría del código de Java). Si todo lo demás (rendimiento, etc.) es igual y la diferencia de longitud entre el algoritmo simple y complejo es realmente drástica, siempre elegiría el algoritmo complejo pero conciso y elegante sobre el simple pero prolijo. Suponiendo que ambos algoritmos han demostrado ser correctos, el que tiene menos sentencias if y menos código en general es menos probable que tenga errores sutiles de implementación y, por lo tanto, es menos probable que requiera mantenimiento. Si fuera un mantenedor, preferiría dedicar más tiempo a aprender un nuevo algoritmo que a aprender cómo alguien implementó un algoritmo ridículamente largo pero aburrido.
Complejo no significa necesariamente más o menos líneas de código para mí.
El sistema perfecto nunca se construye la primera vez. Todo lo que puede hacer es tratar de no tomar demasiadas decisiones complejas que lo vinculen a hacer las cosas de una manera.
Por esa razón, me gusta mantener la complejidad baja durante las versiones iniciales de cualquier proyecto. La razón por la que construiste algo (nueva flexibilidad) se ve gravemente afectada. Si lo haces lo más complejo posible, menos personas lo entenderán al principio. Eso podría ser algo bueno o malo.
Si lo hace demasiado simple (y 50 a 70% más de código) puede tener problemas de rendimiento.
A medida que un sistema envejece y madura, la complejidad parece venir a través de re-factoring. Para entonces, puede llegar a un punto en el que es posible que nunca vuelva a tocar el código y, si lo hace, los costos para comprender la complejidad serán menores debido a la menor frecuencia de tocarlo.
Me gusta resolver problemas complejos con pasos simples. Donde no es posible, la complejidad aumenta en consecuencia. Hubo un punto en otra pregunta sobre saber cuándo es "lo suficientemente bueno". A veces, un poco más de código (5-20%) puede compensar la complejidad de manera significativa, lo que puede ser más costoso para volver a aprender o entender por alguien.
Necesitar un algoritmo mejor suele ser un buen problema, ya que significa que sus cosas se están utilizando y hay que hacer frente a nuevas demandas.
Este es el mismo tipo de complejidad que se aplica a la abstracción de la base de datos para mí, debe saber cuándo hacerlo más flexible y cuándo mantenerlo simple y mejor aprendido a través de su construcción, y desecharlo mucho antes de escribir un una sola línea de cualquier cosa.
Creo que la pregunta es: ¿el código complejo es mejor? ¿Es más rápido, más confiable? El objetivo es escribir los mejores programas, si el código complejo es la mejor solución, solo tiene que escribir mejores comentarios para que su sucesor pueda administrar el código en el futuro. En principio, el código debe ser lo más simple posible en cualquier situación dada.
Curiosamente, una de las medidas más comunes de la complejidad del código es la profundidad de anidación. Su solución "If-then-else" bien puede ser clasificada como más compleja por un analizador de código automatizado que su fórmula.
Sin embargo, he visto suficiente de la "WTF"? codificación que describa que generalmente usaría el enfoque if-then-else. Pero considera esto; ¿Es posible usar su enfoque formulal más complejo, pero para dividir componentes particularmente difíciles en métodos bien nombrados? Si puede hacerlo (posiblemente incluso refactorizando para eliminar la redundancia), podrá evitar los peores aspectos de su algoritmo y, al mismo tiempo, evitará una estructura multinivel de if-then-else.
Depende No me gustaría tener que mantener 150 líneas de declaraciones if-then-else que constituyen una función, particularmente si el árbol de decisión es denso. 20 líneas de matemática complicada pueden o no ser mejores. Puede llevar tiempo entenderlo, pero la solución más larga puede demorar aún más en verificarse.
Idealmente, encontraría la forma de hacerlo en pocas y más simples líneas, pero no todas las funciones funcionan de esa manera.
Si usa las 20 líneas, use algunas de las 130 líneas guardadas en los comentarios para explicar qué algoritmo está usando. Si no puede explicarlo bien, vaya con la solución de 150 líneas.
El Zen de Python hace un buen trabajo al abordar este problema:
...
Simple es mejor que complejo.
Complejo es mejor que complicado.
...
Si la implementación es difícil de explicar, es una mala idea
...
En otras palabras (y como otros han dicho), el algoritmo más simple que hará el trabajo bajo las limitaciones impuestas (tiempo, memoria, etc.) es el mejor. Obviamente, un algoritmo más simple que no hace el trabajo no es lo suficientemente bueno. Si su código es más corto porque usa ideas complejas, es complejo. Si nadie más puede entender su código, es complicado y si le resulta difícil explicárselo (aunque lo entiendan con un doctorado en matemáticas), es una mala idea .
...
Los casos especiales no son lo suficientemente especiales como para romper las reglas.
Aunque la practicidad supera a la pureza.
...
A menudo hay un montón de código de caso especial que se cuela en el código originalmente "puro" durante su vida útil. Debes luchar contra la complejidad de este edificio, pero acéptalo cuando sea necesario.
El código se escribe una vez, y lee 10 veces. Por lo tanto, debe intentar que sea tan fácil de entender como sea posible.
Además, la depuración es mucho más difícil que escribir código. Entonces, ¿cómo puedes depurar tu código cuando ya pones todo tu poder mental en la escritura de código complejo?
Solo trata de seguir Las Tres Leyes del Desarrollo de Software :
- Un desarrollador debe escribir código que crea valor.
- Un desarrollador debe hacer que su código sea fácil de mantener, excepto cuando dicho gasto entre en conflicto con la primera ley.
- Un desarrollador debe reducir su código al tamaño más pequeño posible, siempre que tal reducción no entre en conflicto con las dos primeras leyes.
El nivel de "complejo" puede ser un poco arriesgado aquí, pero mientras el algoritmo no requiera un doctorado en matemáticas para resolver,
Yo diría que adelante y use los algoritmos. Asegúrese de poner un nivel de documentación decente sobre el nombre del algoritmo, y tal vez una breve descripción de cómo funciona o una referencia a un documento al respecto.
Se mantendrá la cantidad de código bajo, con suerte le dará un pequeño rendimiento extra a la aplicación, y con suerte animará a algunos programadores a su alrededor a aprender cosas nuevas para agregar a su repertorio. Además, siempre existe la posibilidad de que un programador que sí sabe estas cosas no lo haga más adelante.
El otro riesgo de simplificar el código es que te agruparán en la pila de codificadores promedio. La línea que estás buscando es un código legible / ilegible. Si puedes volver en una semana y todavía leer el código fácilmente, entonces diría que es lo suficientemente simple.
El riesgo de hacerlo complejo es que nadie más lo entenderá. En este caso, coméntelo claramente y cite algunas fuentes que ayudarán a otra persona a aprender y resolverlo.
El riesgo de hacerlo simple es que alguien no querrá entenderlo porque es muy largo.
Encontrar el camino a través de 100 líneas de declaraciones if / else es a menudo (en mi opinión) más difícil para la resistencia del mantenedor que pasar el mismo tiempo entendiendo un mejor algoritmo (que debe explicarse o vincularse en los comentarios) y finalmente verificar que el 20 líneas de la implementación sí ejecutan esa. También tiene el beneficio de la educación en el trabajo, lo que lo hace mucho más interesante (en un sentido positivo), y a menudo también un mejor perfil de ejecución (menos uso de recursos).
La astucia que debe evitar está en la forma de "hackeos inteligentes" que abusan del lenguaje sin un beneficio real. La astucia que debe adoptar siempre está utilizando el mejor algoritmo para el trabajo.
editar: Respecto al ejemplo de PID: me resulta muy difícil imaginar que la funcionalidad de un PID pueda ser sustituida por un montón de instrucciones if-else. La solución if-else siempre funcionará peor (transiciones menos suaves), será muy difícil de mantener y muy difícil de sintonizar (ajustar las partes PID es muy importante para obtener el comportamiento deseado). Me gustaría agregar que comprender el PID no es muy difícil, incluso si no conoces las matemáticas, que se pueden consultar fácilmente.
Escriba el código de la forma más fácil de mantener y comprender para el programador promedio que trabaja con su código.
He visto que el código muy optimizado se ejecuta muy lentamente, y el código no optimizado se ejecuta rápidamente, y eso se debe a que los compiladores saben cómo la computadora quiere trabajar, y los escritores de compiladores se centran primero en el código fácil de optimizar.
Puede ser que su algoritmo de 150 líneas se compile en algo que sea mucho más rápido en tiempo de ejecución que su versión de 20 líneas. Su algoritmo de 20 líneas puede terminar siendo más lento porque el compilador no sabe cómo optimizarlo.
También sugiero que desee colocar la versión if-else en un bloque de comentarios sobre la versión del algoritmo de 20 líneas (o viceversa) para que los mantenedores puedan entender lo que su versión compleja está tratando de hacer. Tenerlos a ambos en el código hace que sea fácil probar el rendimiento de los dos también (y una vez que los hayas escrito, no hay razón para eliminar el otro). Esto también permitirá una migración fácil a otros lenguajes / plataformas / compiladores en el futuro. Puede que no lo espere, pero si el código funciona, es probable que siga vivo durante décadas y vea muchos compiladores y plataformas diferentes.
Intenta que otros revisen tu versión "más compleja" unas semanas después de que la hayas escrito. Juzgue la complejidad de lo difícil que es para usted leer y explicar a los demás, y su reacción al código.
La complejidad innecesaria siempre es mala. Sí, podría ser divertido escribir un trazador de líneas brillante que haga el trabajo de una función de 200 líneas. Pero recuerda que debes mantener el código. Y el código complejo corto es un infierno para mantener.
Los algoritmos complejos son buenos solo si la velocidad o el aumento de espacio son realmente necesarios. No se moleste en escribir un sofisticado algoritmo factorial que se ejecute en O (1) porque un factorial de aproximadamente 25 básicamente nunca va a ser usado. Sin embargo, si el código está en el ciclo más interno y una aceleración del 25% en este código mejorará la aplicación completa en un 20% y luego lo buscará.
Más simple es usualmente mejor. Recuerde que otras personas probablemente tendrán que mantenerlo algún día.
Necesita ser tan complejo como sea necesario. Lo que no puede ser es una gran diferencia, complicada.
Realmente depende de lo que significa complejo. Si tu algoritmo "simple" tiene 150 líneas de casi el mismo código, mis ojos se nublarán y no seré capaz de entenderlo. Si pones esas condiciones en una matriz o algo así y tu código es un ciclo para leer la matriz y tomar decisiones, lo entenderé mejor aunque el ciclo sea menos "simple" que un montón de enunciados if / else.
Si básicamente estás hablando de un programa de Perl donde haces todo en una línea haciendo un gran uso de la variable predeterminada $ _, yo diría que te quedas con la versión más larga y más detallada.
Si hay una fórmula aceptada para hacer algo, entonces puede usar la fórmula aceptada. Si hay una fórmula algebraica simple que toma 3 o 4 líneas y existe una ecuación diferencial compleja que toma una línea, debe usar la fórmula algebraica simple. En el caso mencionado en los comentarios, creo que un Algoritmo PID es "más simple" que 150 líneas de código if / else. No creo que estés usando el algoritmo para oscurecer nada, sino que estás usando técnicas estándar en el dominio del problema. Solo asegúrate de comentarlo bien e incluso incluir un enlace a una página web que describa la fórmula.
Recuerde que el código debe ser entendido principalmente por los humanos ... los compiladores se encargan de que la computadora lo entienda.
Si puedes hacerlo en 20 líneas y luego comentar adecuadamente esas líneas, yo diría que síguelo. No solo hace que sea más fácil de mantener al tener menos código para mantener, sino que también contribuye a que sus compañeros programadores sean más inteligentes.
Pre-optimizaciones y hacks inteligentes es una cosa, pero los algoritmos inteligentes son y siempre han sido un juego justo.
¡Pero mantenga esos algoritmos aislados en sus propias funciones y recuerde explicar las variables de entrada y salida!
Cita obligatoria:
"Controlar la complejidad es la esencia de la programación de computadoras". (Brian Kernigan)
Tan complejo como debe ser y nada más.
No puedo creer que alguien piense que 150 líneas de algo son más simples que 20 líneas de algo.
Vaya con las 20 líneas y proteja a sus compañeros de trabajo de la siguiente manera:
Documente cada invariante de estructura de datos y cada invariante de bucle no trivial. Documente estos no en comentarios, sino en un código real que verifique las invariantes. La verificación se puede desactivar durante la producción.
Si utiliza fórmulas matemáticas en su código o para derivar su código, incluya referencias en comentarios (o cadenas estáticas). Lo ideal sería incluir dos referencias: una referencia web que probablemente esté disponible en segundos y un libro de texto bien considerado que probablemente se imprima por mucho tiempo y se encuentre en las bibliotecas de la universidad.
Si la comprensión de su código requiere experiencia especial (comprensión de ecuaciones en derivadas parciales, un título en física, teoría de Galois, lo que sea), entonces para protegerse, puede dirigirse a su dirección y decir: "Tengo esta experiencia especial que lo hace Es posible que escriba un código que sea más pequeño (y por lo tanto más rápido, más confiable y más fácil de mantener), pero cuando tenga que reemplazarme tendrá que contratar a alguien con experiencia similar. ¿Qué quiere que haga? " Será útil si puede decirle a su gerencia cuán fácilmente se puede obtener dicha experiencia. Por ejemplo, muchas personas con títulos de ingeniería pueden aprender ecuaciones en derivadas parciales, pero los teóricos de Galois son bastante delgados sobre el terreno.
PD Aquí hay un ejemplo de comentario de un proyecto de hobby para que pueda depurar mi propio código más adelante:
/*
* Compute the position of a point partially along the geodesic from
* pt1.lat,pt1.lon to pt2.lat,pt2.lon
*
* Ref: http://mathworld.wolfram.com/RotationFormula.html
*/
Como dijo Einstein:
Hacer todo lo más simple posible, pero no más sencillo.
Se aplica tanto al código como a la física.
Use su juicio: ¿será más fácil de mantener? A menudo, al reducir grandes desordenes si / otros en algo más pequeño, se eliminan los casos de esquina que no necesitan estar allí, evitando errores que pueden haber surgido en el futuro. Por supuesto, al reducir un conjunto claro de condiciones en un oscuro giro de la lógica booleana que solo funciona, puede hacer las cosas mucho más difíciles de mantener cuando algo cambia.
EDITAR:
Con el caso citado, si la fórmula va a funcionar, probablemente sea la mejor opción, incluso puedes dejar un comentario citando la fuente. Sin embargo, es muy posible que la fórmula estuviera allí antes, pero se descartó para permitir que se solucione un caso específico. Este es el tipo de comentarios que debería ayudar en su repositorio de versiones.
Dado que nadie ha publicado un enlace todavía, aquí hay una descripción del algoritmo PID al que se refiere.
Admito que me agradan las matemáticas y los algoritmos, pero para mí un lío de 150 líneas si / si es mucho más complejo y difícil de mantener que cualquier cosa que pueda expresarse limpiamente en 20 líneas. Documente el código correctamente y ponga referencias a documentos, libros o Wikipedia.
Creo que es importante separar la complejidad del dominio de la complejidad técnica subyacente.
Algunas funcionalidades específicas del dominio para las cuales le gustaría usar la computadora son intrínsecamente complejas, otras no. Por ejemplo, los problemas de contabilidad están llenos de reglas extrañas, pero la mayoría de los cálculos son bastante simples. La valuación de los títulos financieros por otro lado, según el tipo, puede incluir fórmulas matemáticas enormemente complejas y muchas simulaciones. La mayoría de los sistemas informáticos simplemente recopilan grandes cantidades de datos, pero muchos de ellos tienen algunos algoritmos complejos subyacentes.
La tecnología, por otro lado, a menudo impone su propia complejidad. Escribir un gran programa en C ++ para una PC puede ser un desafío. Escribir una aplicación basada en web para Internet puede ser mucho peor. Tratar de asegurar la tolerancia a fallas, el rendimiento o la seguridad a menudo causa una gran complejidad agregada. Cada tecnología diferente ayuda o dificulta el sistema.
Lo que realmente queremos hacer es expresar el código en la forma más simple posible que esté más cerca de su dominio inherente o especificación técnica. Si está escribiendo un sistema operativo, entonces C es un lenguaje más fácil de usar. Si está resolviendo probabilidades complejas de riesgo para un seguro, un lenguaje orientado a matrices como APL puede ser más apropiado. Si solo está creando informes masivos, algo que tiene una sintaxis sencilla orientada a la generación de informes es la mejor opción.
Entonces, si sus 150 líneas de cosas if / else "coinciden" con la forma en que el problema se expresa mucho mejor que 20 líneas inteligentes, es un bit de código mucho más mantenible y prescindible. Si tomas una perspectiva a largo plazo, escribir código de ejecución es fácil, mantenerlo así es el verdadero desafío ...
Pablo.
Robert C. Martin utiliza este comic como introducción a su libro Clean Code :