Cómo gestionar la complejidad accidental en proyectos de software

73

Cuando se le preguntó a Murray Gell-Mann cómo Richard Feynman logró resolver tantos problemas difíciles, Gell-Mann respondió que Feynman tenía un algoritmo:

  1. Escribe el problema.
  2. Piensa muy duro.
  3. Escriba la solución.

Gell-Mann estaba tratando de explicar que Feynman era un tipo diferente de solucionador de problemas y que no se podían obtener ideas al estudiar sus métodos. Me siento un poco igual acerca de la gestión de la complejidad en proyectos de software medianos / grandes. Las personas que son buenas son intrínsecamente buenas en eso y de alguna manera logran colocar en capas y apilar varias abstracciones para hacer que todo sea manejable sin tener que introducir ningún otro extraño.

Entonces, ¿es el algoritmo de Feynman la única forma de manejar la complejidad accidental o existen métodos reales que los ingenieros de software puedan aplicar de manera consistente para dominar la complejidad accidental?

    
pregunta davidk01 17.02.2014 - 12:33

6 respuestas

100
  

Cuando veas un buen movimiento, busca uno mejor.
    —Emanuel Lasker, campeón mundial de ajedrez por 27 años

En mi experiencia, el mayor impulsor de la complejidad accidental es que los programadores sigan con el primer borrador, simplemente porque funciona. Esto es algo que podemos aprender de nuestras clases de composición de inglés. Ellos construyen el tiempo para revisar varios borradores en sus tareas, incorporando los comentarios de los maestros. Las clases de programación, por alguna razón, no.

Hay libros llenos de formas concretas y objetivas para reconocer, articular y corregir el código subóptimo: Clean Code , Trabajar eficazmente con el código heredado y muchos otros. Muchos programadores están familiarizados con estas técnicas, pero no siempre se toman el tiempo para aplicarlas. Son perfectamente capaces de reducir la complejidad accidental, simplemente no lo han convertido en un hábito para intentar .

Parte del problema es que a menudo no vemos la complejidad intermedia del código de otras personas, a menos que se haya revisado por pares en una etapa temprana. El código limpio parece que fue fácil de escribir, cuando en realidad generalmente involucra varios borradores. Al principio, escribe la mejor forma que le viene a la cabeza, observa las complejidades innecesarias que introduce, luego "busca un movimiento mejor" y refactoriza para eliminar esas complejidades. Luego sigues "buscando un movimiento mejor" hasta que no puedas encontrar uno.

Sin embargo, no pone el código para revisión hasta después de toda esa rotación, por lo que externamente parece que podría haber sido un proceso similar al de Feynman. Tienes una tendencia a pensar que no puedes hacerlo todo un trozo así, así que no te molestes en intentarlo, pero la verdad es que el autor de ese código hermosamente simple que acabas de leer generalmente no puede escribirlo todo en un trozo. así tampoco, o si pueden, es solo porque tienen experiencia en la escritura de código similar muchas veces antes, y ahora pueden ver el patrón sin las etapas intermedias. De cualquier manera, no puedes evitar los borradores.

    
respondido por el Karl Bielefeldt 17.02.2014 - 14:41
44

"La habilidad de arquitectura de software no se puede enseñar" es una falacia generalizada.

Es fácil entender por qué mucha gente lo cree (aquellos que son buenos en eso quieren creer que son místicamente especiales y aquellos que no quieren creer que no es su culpa que no lo sean) .) Sin embargo es incorrecto; la habilidad es solo un poco más práctica que otras habilidades de software (por ejemplo, comprensión de bucles, manejo de punteros, etc.)

Creo firmemente que la construcción de grandes sistemas es susceptible a la práctica repetida y al aprendizaje a partir de la experiencia de la misma manera en que convertirse en un gran músico u orador público es: una cantidad mínima de talento es una condición previa, pero no es un mínimo tan deprimente que está fuera del alcance de la mayoría de los practicantes.

Lidiar con la complejidad es una habilidad que adquieres en gran medida al intentar y fallar algunas veces. Es solo que las muchas pautas generales que la comunidad ha descubierto para la programación en general (usar capas, luchar contra la duplicación donde quiera que levante la cabeza, adherirse religiosamente al 0/1 / infinito ...) no son tan obviamente correctas y necesarias para un Principiante hasta que realmente programen algo que sea grande. Hasta que realmente haya sido mordido por la duplicación que causó problemas solo unos meses más tarde, simplemente no puede "captar" la importancia de tales principios.

    
respondido por el Kilian Foth 17.02.2014 - 13:07
22

El pensamiento pragmático de Andy Hunt aborda este problema. Se refiere al modelo de Dreyfus, según el cual hay 5 etapas de competencia en diversas habilidades. Los principiantes (etapa 1) necesitan instrucciones precisas para poder hacer algo correctamente. Los expertos (etapa 5), por el contrario, pueden aplicar patrones generales a un problema dado. Citando el libro,

  

A menudo es difícil para los expertos explicar sus acciones con un buen nivel de detalle; muchos   Sus respuestas están tan bien practicadas que se convierten en acciones preconscientes. Su vasta experiencia se extrae de áreas no verbales y preconscientes del cerebro, lo que nos dificulta la observación y la articulación.

     

Cuando los expertos hacen lo suyo, parece casi mágico para el resto de nosotros: extraños encantamientos, una percepción que parece surgir de la nada y una capacidad aparentemente extraña de saber la respuesta correcta cuando el resto de nosotros ni siquiera estamos todo eso seguro sobre la pregunta   No es magia, por supuesto, pero la forma en que los expertos perciben el mundo, cómo resuelven los problemas, los modelos mentales que utilizan, etc., son muy diferentes de los no expertos.

Esta regla general de ver (y, como resultado, evitar) diferentes problemas puede aplicarse específicamente al problema de la complejidad accidental. Tener un conjunto dado de reglas no es suficiente para evitar este problema. Siempre habrá una situación que no está cubierta por esas reglas. Necesitamos ganar experiencia para poder prever problemas o identificar soluciones. La experiencia es algo que no se puede enseñar, solo se puede ganar intentando, fracasando o triunfando constantemente y aprendiendo de los errores.

Esta pregunta de Workplace es relevante e IMHO sería interesante de leer en este contexto.

    
respondido por el superM 17.02.2014 - 13:10
4

No lo explicas, pero la "complejidad accidental" se define como la complejidad que no es inherente al problema, en comparación con la complejidad "esencial". Las técnicas requeridas para "Domar" dependerán de dónde empieces. Lo siguiente se refiere principalmente a los sistemas que ya han adquirido una complejidad innecesaria.

Tengo experiencia en varios proyectos grandes de varios años en los que el componente "accidental" superó significativamente el aspecto "esencial", y también aquellos en los que no lo hizo.

En realidad, el algoritmo de Feynman se aplica en cierta medida, pero eso no significa que "pensar realmente duro" significa solo magia que no se puede codificar.

Me parece que hay dos enfoques que deben tomarse. Tómalos a ambos, no son alternativas. Uno es abordarlo de forma parcial y el otro es hacer un gran trabajo. Así que ciertamente, "anote el problema". Esto podría tomar la forma de una auditoría del sistema: los módulos de código, su estado (olor, nivel de pruebas automatizadas, la cantidad de personal que afirma entenderlo), la arquitectura general (existe una, incluso si “tiene problemas” ), estado de los requisitos, etc. etc.

Es la naturaleza de la complejidad "accidental" que no hay un problema que solo deba abordarse. Así que necesitas triaje. ¿Dónde le duele, en términos de capacidad para mantener el sistema y avanzar en su desarrollo? Quizás algún código sea realmente maloliente, pero no es la máxima prioridad y la reparación puede hacerse para esperar. Por otro lado, puede haber algún código que devuelva rápidamente el tiempo empleado en refactorizar.

Defina un plan para una mejor arquitectura e intente asegurarse de que el nuevo trabajo se ajuste a ese plan: este es el enfoque incremental.

También, articule el costo de los problemas y utilícelo para construir un caso de negocios para justificar un refactor. La clave aquí es que un sistema bien diseñado puede ser mucho más robusto y comprobable, lo que da como resultado un tiempo mucho más corto (costo e cronograma) para implementar el cambio, esto tiene un valor real.

Un retrabajo importante viene en la categoría de "pensar realmente duro": es necesario hacerlo bien. Aquí es donde tener un "Feynman" (bueno, una pequeña fracción de uno estaría bien) da sus frutos enormemente. Una revisión importante que no resulte en una mejor arquitectura puede ser un desastre. Las reescrituras completas del sistema son conocidas por esto.

Implícito en cualquier enfoque es saber distinguir "accidental" de "esencial", es decir, es necesario tener un gran arquitecto (o equipo de arquitectos) que realmente entienda el sistema y su propósito.

Habiendo dicho todo eso, la clave para mí es pruebas automatizadas . Si tienes suficiente, tu sistema está bajo control. Si no lo haces . .

    
respondido por el Keith 18.02.2014 - 07:00
3
  

" Todo debería ser lo más simple posible, pero no más simple. "
    - atribuido a Albert Einstein

Permítame hacer un bosquejo de mi algoritmo personal para tratar con la complejidad accidental.

  1. Escriba una historia de usuario o un caso de uso. Revisión con el propietario del producto.
  2. Escriba una prueba de integración que falle porque la función no está allí. Revíselo con QA, o con el ingeniero principal, si existe tal cosa en su equipo.
  3. Escriba pruebas de unidad para algunas clases que podrían pasar la prueba de integración.
  4. Escriba la implementación mínima para aquellas clases que pasen las pruebas unitarias.
  5. Revise las pruebas de unidad y la implementación con un compañero desarrollador. Vaya al paso 3.

Toda la magia del diseño estaría en el Paso 3: ¿cómo configurar esas clases? Esto se convierte en la misma pregunta: ¿cómo imagina que tiene una solución para su problema antes que tiene una solución para su problema?

Cabe destacar que simplemente imaginando que tiene la solución parece ser una de las principales recomendaciones de las personas que escriben sobre resolución de problemas (llamadas "ilusiones" por Abelson y Sussman en Estructura e interpretación de programas informáticos y "trabajando hacia atrás" en Cómo Resolverlo )

Por otra parte, no todos tienen el mismo " gusto por las soluciones imaginadas ": hay soluciones que solo a usted le parecen elegantes, y hay otras más comprensibles para un público más amplio. Es por eso que necesita revisar su código con otros desarrolladores: no tanto para ajustar el rendimiento, sino para ponerse de acuerdo sobre las soluciones entendidas. Por lo general, esto lleva a un rediseño y, después de algunas iteraciones, a un código mucho mejor.

Si te limitas a escribir implementaciones mínimas para aprobar tus pruebas, y escribes pruebas que son entendidas por muchas personas, debes terminar con una base de código donde solo complejidad irreducible permanece.

    
respondido por el logc 18.02.2014 - 17:06
2

Complejidad accidental

La pregunta original (parafraseada) fue:

  

¿Cómo gestionan los arquitectos la complejidad accidental en proyectos de software?

La complejidad

Accidental surge cuando quienes tienen la dirección de un proyecto deciden agregar tecnologías que son únicas, y que la estrategia general de los arquitectos originales del proyecto no pretendía incorporar el proyecto. Por esta razón, es importante registrar el razonamiento detrás de la elección de la estrategia.

La complejidad accidental puede ser evitada por el liderazgo que se adhiere a su estrategia original hasta que se haga aparentemente necesario un alejamiento deliberado de esa estrategia.

Evitar la complejidad innecesaria

Basado en el cuerpo de la pregunta, lo reformularía así:

  

¿Cómo gestionan los arquitectos la complejidad de los proyectos de software?

Esta reformulación es más apropiada para el cuerpo de la pregunta, donde se introdujo el algoritmo Feynman, proporcionando un contexto que propone que para los mejores arquitectos, cuando se enfrentan a un problema, tienen una gestalt a partir de la cual construyen hábilmente un Solución, y que el resto de nosotros no podamos esperar aprender esto. Tener un gestalt de comprensión depende de la inteligencia del sujeto y de su disposición para aprender las características de las opciones arquitectónicas que podrían estar dentro de su alcance.

El proceso de planificación para el proyecto usaría el aprendizaje de la organización para hacer una lista de los requisitos del proyecto y luego intentar construir una lista de todas las opciones posibles y luego reconciliar las opciones con los requisitos. El gestalt del experto le permite hacer esto rápidamente, y quizás con poco trabajo evidente, haciendo que parezca que le resulta fácil.

Le aseguro que se trata de él debido a su preparación. Tener el gestalt del experto requiere familiaridad con todas sus opciones, y la previsión de proporcionar una solución directa que permita las necesidades futuras previstas que el proyecto debe proporcionar, así como la flexibilidad para adaptarse a las necesidades cambiantes de el proyecto. La preparación de Feynman fue que tenía una comprensión profunda de varios enfoques tanto en matemática teórica como en matemática y física aplicadas. Era innatamente curioso y lo suficientemente brillante como para dar sentido a las cosas que descubrió sobre el mundo natural que lo rodeaba.

El arquitecto experto en tecnología tendrá una curiosidad similar, aprovechando una profunda comprensión de los fundamentos, así como una amplia exposición a una gran diversidad de tecnologías. Él (o ella) tendrá la sabiduría de utilizar las estrategias que han tenido éxito en todos los dominios (como Principios de la programación de Unix ) y aquellos que se aplican a dominios específicos (como patrones de diseño y guías de estilo ). Puede que no tenga un conocimiento íntimo de todos los recursos, pero sabrá dónde encontrar el recurso.

Construyendo la solución

Este nivel de conocimiento, comprensión y sabiduría se puede extraer de la experiencia y la educación, pero requiere inteligencia y actividad mental para armar una solución estratégica gestáltica que funcione de manera que evite la complejidad accidental e innecesaria. Se requiere que el experto ponga estos fundamentos juntos; estos fueron los trabajadores del conocimiento que Drucker previó cuando acuñó el término por primera vez.

Volver a las preguntas finales específicas:

Los métodos específicos para controlar la complejidad accidental se pueden encontrar en los siguientes tipos de fuentes.

Siguiendo los Principios de la Programación de Unix, creará programas modulares simples que funcionan bien y son robustos con interfaces comunes. Seguir los patrones de diseño lo ayudará a construir algoritmos complejos que no son más complejos de lo necesario. Las siguientes Guías de estilo asegurarán que su código sea legible, fácil de mantener y óptimo para el idioma en el que se escribe su código. Los expertos habrán internalizado muchos de los principios que se encuentran en estos recursos, y podrán reunirlos de manera cohesiva y sin problemas.

    
respondido por el Aaron Hall 18.02.2014 - 02:54

Lea otras preguntas en las etiquetas