¿Cuáles son las causas de la complejidad accidental en el software?

El más frecuente que he visto, uno que parece casi universal, se puede resumir de manera muy simple: pensar que tiene un problema que resolver cuando realmente tiene dos (o más), y luego tratar de resolverlos como un problema único.

Esto es realmente una falla al analizar el problema completamente. “Dividir y conquistar” se trata de la mejor herramienta analítica que tenemos para robustecer las cosas, y la mayoría de los problemas se pueden dividir en al menos dos problemas más simples. Si los resuelve, puede combinar las soluciones para resolver el problema más grande.

Todo se reduce a la combinatoria: si el problema A puede dar como resultado 3 estados posibles, y el problema B puede dar como resultado 10 estados posibles, entonces puede escribir código que trate con cada uno de ellos y saber con certeza que los ha capturado a todos o escriba una solución combinada que trate con 3 * 10 posibles estados combinados. Este último va a ser más complejo, porque tiene que manejar 30 casos en lugar de 13. Y cuanto más complejo es el código, más probabilidades hay de tener problemas simplemente porque hay más código para tener errores.

Entonces, si ve algún código que parece ser casos de muerte por esquina, o una pila gigante de declaraciones de casos y si, y prueba más de una cosa, entonces probablemente esté en presencia de un caso incompleto problema analizado.

Estas cosas generalmente parecen funcionar (o incluso son inteligentes) cuando son nuevas; es más tarde, cuando descubres que necesitas manejar las combinaciones de estados que se tragaron con un código que no consideraba interesantes los resultados de ambos problemas, las cosas se pusieron feas.

Un ejemplo común es confundir y engordar la autenticación y la autorización . Autenticación es donde el sistema descubre quién eres ; autorización es donde se da cuenta de lo que puede hacer . Puede ver las consecuencias de no resolver ese problema en el corazón de Unix: el usuario root o superusuario siempre tiene un id (el problema de autenticación ) de cero, y el usuario raíz tiene un * role * (el problema de autorización ) que hacer cualquier cosa, incluida la eliminación del sistema operativo. Eso suena genial hasta que el sistema operativo ejecute contenedores (por ejemplo, Docker), y cada contenedor debe tener su propio superusuario root , pero solo hay un cero , y eso es lo que el universo del código Unix va a probar para ver si usted ‘ re root – no hay rasgadura que de 45 años de software ahora. Un amigo mío me dio una demostración de Kubernetes, una herramienta de implementación de implementación de Docker, el otro día, que tiene una rabieta si un contenedor necesita ejecutar un proceso como raíz. Él vio eso como algo normal, por supuesto que no puedes hacer eso. No es normal (y de hecho, otro SO que tiene contenedores, SmartOS, no tiene esa limitación). Entonces la idea de que hay un superusuario, autorizado para hacer cualquier cosa, que siempre se identifica con el cero de la identificación , funciona de maravilla hasta que necesite dos superusuarios. Y en ese momento puede haber miles de millones de líneas de código que debe ejecutar, que todas suponen que root = 0 y que solo hay un superusuario. En ese punto estás jodido.

Verdaderamente, nadie es inmune a fallar en romper los problemas lo suficiente. Considere HTTPS, el protocolo seguro que usan los navegadores web. Combina dos problemas heterogéneos y los trata como uno solo:

  • Sabiendo que la comunicación está encriptada
  • Identificando con quién te estás comunicando

Ahora, debe resolver estos dos problemas para tener una navegación segura. Y la criptografía proporciona excelentes herramientas para resolver ambas. Pero el hecho de que HTTPS fue diseñado con la fantasía de que esos son un problema único crea todo tipo de consecuencias desagradables para quienes lo usan:

  • Una industria de certificados parasitarios que cobra masivamente por los certificados
  • No hay forma de tener un mecanismo alternativo para identificar con quién se está comunicando: está atrapado con lo que HTTPS le brinda si es adecuado para usted o no (o si lo necesita)

Aprende, cada vez que resuelves un problema en el código, pregúntate, sin piedad y repetidamente, ¿Puedo dividir este problema en dos problemas más simples? antes de escribir una línea de código. Hazlo de forma recursiva hasta que llegues a problemas que no se pueden reducir más o que ya tienes una solución. Por lo general, una vez que los problemas se vuelven lo suficientemente pequeños, se llega a los ya resueltos, donde puede usar su código existente o en las bibliotecas. Terminas escribiendo menos código, y los resultados son más robustos y más legibles para los demás, no terminarás haciendo reescrituras masivas cuando descubras una nueva necesidad, y obtendrás soluciones que puedes reutilizar.

Y si alguien te dice que hacer ese tipo de análisis inicial no es “ágil” o que es “cascada” … dales una botella, asegúrate de eructarlos, ponles un binky en la boca y ponlos a dormir una siesta. Luego haz el análisis.

Buena pregunta. La respuesta puede ajustarse al principio de Anna Karenina: “Todas las familias felices son iguales; cada familia infeliz es infeliz a su manera ”

Es probable que haya miles de razones, que incluyen:

  1. Como otros han señalado, no conceptualizar el problema antes de obtener la codificación.
  2. Acreción y alcance fluencia. Empiezas a hacer algo simple y ahora alguien agrega requisitos adicionales. No tienes tiempo para refactorizar, entonces te escabulles. Registros de salud electrónicos completos han sido hechos con cosas que están principalmente atornilladas. ¿Cómo te atrapa trabajar con más de 5000 tablas SQL?
  3. Elección del idioma equivocado. Siempre hay una solución, pero … cuesta en términos de complejidad.
  4. Error al normalizar sus tablas SQL.
  5. Fallo de la documentación. Pobre comprensión => complejidad.
  6. No modularizar tu código.
  7. Éxito. De repente, todos quieren funciones y tu código crece como Topsy.
  8. Programadores incompetentes que “simplemente hacen el trabajo” cortando y pegando.
  9. Módulos excesivamente autónomos, para que tenga código duplicado en módulos separados. El problema de atornillar nuevamente.
  10. La complejidad se alimenta de sí misma. Cuando se trata de millones de líneas de código, nadie quiere refactorizar el código existente. Así que los extras están realmente conectados.

Este último problema es interesante porque es como los genetistas de las “enfermedades de triple repetición”. En el ADN, cada “triplete” codifica un único aminoácido. Algunas veces, obtienes secuencias repetidas del mismo aminoácido.

Los mecanismos normales de copia de ADN pueden hacer frente a esto, hasta cierto punto. Pero por encima de un umbral, se insertan copias adicionales. Y luego simplemente se vuelve loco. Este parece ser el mecanismo principal para una serie de trastornos como la enfermedad de Huntington, y explica por qué los niños que han heredado el trastorno a menudo contraen la enfermedad a una edad más temprana; esto se llama anticipación genética.

Por supuesto, el hilo que une a todos estos es la filosofía de programación de mierda, pero a nadie le importa esto, así que no voy a ampliarlo 😉

Mi 2c, Jo.

Imagen de: Tomar el software en serio – Blog de Sean Blanchfield

Una complejidad inducida por la elección en el espacio de la solución y no vinculada al problema por resolver. Resolviendo problemas innecesarios. Usar el nivel incorrecto de abstracción o abstracción en diferentes ejes o no adaptar las abstracciones a la nueva comprensión. Reinventar la rueda o usar una herramienta demasiado compleja que no comprenda. Así que mantener las cosas simples es la máxima sofisticación. La complejidad es mínima, entonces solo existe una complejidad esencial, lamentablemente no es fácil de medir, por ejemplo. la programación reactiva es un gran simplificador de procesos accionados por eventos asíncronos y proporciona seguridad de composición y de subprocesos; sin embargo, si no lo sabe, tal vez sea todo lo que tiene, por lo que elegir la herramienta adecuada para el trabajo tiene la implicación pragmática de que conoce un muchas opciones o tener amigos Auch. No hay fórmula, pero sí mucha experiencia, intuición y deseo de una solución simple.

Pobre abstracción derivada de una propensión a comenzar a codificar demasiado pronto antes de que el problema se comprenda correctamente. Como dice el viejo proverbio danés: si tienes un hacha y 8 horas para cortar un árbol, pasa 7 horas afilando el hacha.