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:
- Escribe el problema.
- Piensa muy bien.
- Escribe la solución.
Gell-Mann estaba tratando de explicar que Feynman era un tipo diferente de solucionador de problemas y que no se obtenían conocimientos al estudiar sus métodos. Siento lo mismo sobre 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 superponer y apilar varias abstracciones para hacer que todo sea manejable sin introducir ninguna ruina extraña.
Entonces, ¿es el algoritmo Feynman la única forma de gestionar la complejidad accidental o existen métodos reales que los ingenieros de software puedan aplicar consistentemente para domar la complejidad accidental?
fuente
Respuestas:
En mi experiencia, el mayor impulsor de la complejidad accidental son los programadores que se quedan con el primer borrador, solo porque sucede. Esto es algo que podemos aprender de nuestras clases de composición en inglés. Se acumulan a tiempo para revisar varios borradores en sus tareas, incorporando comentarios de los maestros. Las clases de programación, por alguna razón, no lo hacen.
Hay libros llenos de formas concretas y objetivas para reconocer, articular y corregir el código subóptimo: Código limpio , Trabajar eficazmente con 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 han hecho un hábito intentarlo .
Parte del problema es que a menudo no vemos la complejidad intermedia del código de otras personas, a menos que haya pasado por una revisión por pares en una etapa temprana. Parece que el código limpio fue fácil de escribir, cuando de hecho generalmente involucra varios borradores. Primero escribe la mejor manera que se le ocurre, nota las complejidades innecesarias que introduce, luego "busca un mejor movimiento" y refactoriza para eliminar esas complejidades. Luego sigue "buscando un mejor movimiento" hasta que no pueda encontrar uno.
Sin embargo, no publica el código para su revisión hasta después de toda esa rotación, por lo que externamente parece que también podría haber sido un proceso similar a Feynman. Tienes la tendencia a pensar que no puedes hacerlo todo de una sola vez, así que no te molestas en intentarlo, pero la verdad es que el autor de ese código maravillosamente simple que acabas de leer por lo general no puede escribirlo todo de una vez así, o si pueden, es solo porque tienen experiencia escribiendo 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.
fuente
"La habilidad de la arquitectura de software no se puede enseñar" es una falacia generalizada.
Es fácil entender por qué muchas personas lo creen (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 está mal; la habilidad es un poco más práctica que otras habilidades de software (por ejemplo, comprender bucles, tratar con punteros, etc.)
Creo firmemente que la construcción de sistemas grandes es susceptible a la práctica repetida y al aprendizaje de la experiencia de la misma manera que convertirse en un gran músico o orador público es: una cantidad mínima de talento es una condición previa, pero no es un mínimo deprimentemente enorme lo que está fuera de lugar. alcance de la mayoría de los practicantes.
Tratar 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 sea que se levante, adherirse religiosamente a 0/1 / infinito ...) no son tan evidentemente correctas y necesarias para un principiante hasta que realmente programen algo que sea grande. Hasta que haya sido mordido por una duplicación que causó problemas solo unos meses después, simplemente no puede "entender" la importancia de tales principios.
fuente
El pensamiento pragmático de Andy Hunt aborda este problema. Se refiere al modelo Dreyfus, según el cual hay 5 etapas de competencia en diversas habilidades. Los novatos (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,
Esta regla general de ver (y como resultado evitar) diferentes problemas se puede aplicar 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 obtener intentando constantemente, fallando o teniendo éxito y aprendiendo de los errores.
Esta pregunta de Workplace es relevante y en mi humilde opinión sería interesante leer en este contexto.
fuente
No lo deletrea, 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 comience. Lo siguiente se refiere principalmente a sistemas que ya han adquirido una complejidad innecesaria.
Tengo experiencia en varios proyectos grandes de varios años donde 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" significa solo magia que no se puede codificar.
Encuentro que hay dos enfoques que deben tomarse. Tómelos a ambos, no son alternativas. Una es abordarla poco a poco y la otra es hacer una revisión importante. Así que ciertamente, "escriba 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, cuánto personal afirma comprenderlo), la arquitectura general (hay 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 necesita abordarse. Entonces necesitas triaje. ¿Dónde duele, en términos de capacidad para mantener el sistema y progresar en su desarrollo? Tal vez algún código es realmente maloliente, pero no es la máxima prioridad y se puede hacer la fijación para esperar. Por otro lado, puede haber algún código que devuelva rápidamente el tiempo dedicado a la refactorización.
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.
Además, articule el costo de los problemas y úselo 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 resulta en un tiempo mucho más corto (costo y cronograma) para implementar el cambio; esto tiene un valor real.
Una revisión importante viene en la categoría de "pensar realmente duro": debe hacerlo bien. Aquí es donde tener un "Feynman" (bueno, una pequeña fracción de uno estaría bien) vale la pena 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 entre "accidental" y "esencial", lo que significa que debe tener un gran arquitecto (o equipo de arquitectos) que realmente comprenda el sistema y su propósito.
Habiendo dicho todo eso, la clave para mí son las pruebas automatizadas . Si tiene suficiente, su sistema está bajo control. Si no lo haces . .
fuente
Permítanme esbozar mi algoritmo personal para lidiar con la complejidad accidental.
Toda la magia del diseño estaría en el Paso 3: ¿cómo se configuran esas clases? Esta resulta ser la misma pregunta que: ¿cómo imagina que tiene una solución para su problema antes de tener una solución para su problema?
Sorprendentemente, solo imaginar 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 de computadora y "trabajando hacia atrás" en Polya's How to Resuélvelo )
Por otro lado, 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 por pares con otros desarrolladores: no tanto para ajustar el rendimiento, sino para acordar soluciones entendidas. Por lo general, esto lleva a un rediseño y, después de algunas iteraciones, a un código mucho mejor.
Si sigue escribiendo implementaciones mínimas para pasar sus pruebas, y escribe pruebas que muchas personas entienden, debe terminar con una base de código donde solo quede una complejidad irreducible .
fuente
Complejidad accidental
La pregunta original (parafraseada) fue:
La complejidad accidental surge cuando aquellos con dirección sobre un proyecto eligen agregar tecnologías que son únicas, y que la estrategia general de los arquitectos originales del proyecto no tenía la intención de incorporar al proyecto. Por esta razón, es importante registrar el razonamiento detrás de la elección en la estrategia.
La complejidad accidental puede ser evitada por el liderazgo que se adhiere a su estrategia original hasta el momento en que aparentemente sea necesario un alejamiento deliberado de esa estrategia.
Evitar la complejidad innecesaria
Basado en el cuerpo de la pregunta, lo reformularía así:
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, tengan una gestalt a partir de la cual construyan hábilmente una solución, y que el resto de nosotros no puede esperar aprender esto. Tener una buena comprensión depende de la inteligencia del tema y de su disposición a aprender las características de las opciones arquitectónicas que podrían estar dentro de su alcance.
El proceso de planificación del proyecto utilizaría el aprendizaje de la organización para hacer una lista de los requisitos del proyecto, y luego trataría de construir una lista de todas las opciones posibles, y luego conciliar las opciones con los requisitos. La gestalt del experto le permite hacer esto rápidamente, y tal vez con poco trabajo evidente, haciendo que parezca fácil para él.
Les presento que se trata de él debido a su preparación. Tener la gestalt del experto requiere estar familiarizado con todas sus opciones, y la previsión para proporcionar una solución directa que permita las necesidades futuras previstas que se determina que el proyecto debe satisfacer, 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 en matemáticas y física teórica y aplicada. Tenía una curiosidad innata y era lo suficientemente brillante como para entender las cosas que descubrió sobre el mundo natural que lo rodeaba.
El arquitecto experto en tecnología tendrá una curiosidad similar, aprovechando una comprensión profunda de los fundamentos, así como una amplia exposición a una gran diversidad de tecnologías. Él (o ella) tendrá la sabiduría para recurrir a las estrategias que han tenido éxito en todos los dominios (como los Principios de Programación 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 profundo de todos los recursos, pero sabrá dónde encontrar el recurso.
Construyendo la solución
Este nivel de conocimiento, comprensión y sabiduría puede extraerse de la experiencia y la educación, pero requiere inteligencia y actividad mental para crear una solución estratégica gestalt que funcione de manera que evite la complejidad accidental e innecesaria. 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.
De vuelta a las preguntas finales específicas:
Los métodos específicos para domar la complejidad accidental se pueden encontrar en los siguientes tipos de fuentes.
Seguir los Principios de la Programación Unix le permitirá crear programas modulares simples que funcionan bien y son robustos con interfaces comunes. Seguir 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 está escrito. Los expertos habrán internalizado muchos de los principios que se encuentran en estos recursos y podrán reunirlos de manera coherente y sin interrupciones.
fuente
Esta puede haber sido una pregunta difícil hace algunos años, pero hoy en día ya no es difícil eliminar la complejidad accidental.
Lo que Kent Beck dijo sobre sí mismo, en algún momento: "No soy un gran programador; solo soy un buen programador con grandes hábitos".
Vale la pena resaltar dos cosas, en mi opinión: se considera un programador , no un arquitecto, y se centra en los hábitos, no en el conocimiento.
La forma de Feynman de resolver problemas difíciles es la única forma de hacerlo. La descripción no es necesariamente muy fácil de entender, así que la analizaré. La cabeza de Feynman no solo estaba llena de conocimiento, sino que también estaba llena de la habilidad para aplicar ese conocimiento. Cuando tienes tanto el conocimiento como las habilidades para usarlo, resolver un problema difícil no es difícil ni fácil. Es el único resultado posible.
Hay una forma completamente no mágica de escribir código limpio, que no contiene complejidad accidental, y es en su mayoría similar a lo que hizo Feynman: adquirir todo el conocimiento requerido, entrenar para acostumbrarse a ponerlo a trabajar, en lugar de simplemente esconderlo en algún rincón de tu cerebro, luego escribe código limpio.
Ahora, muchos programadores ni siquiera son conscientes de todo el conocimiento requerido para escribir código limpio. Los programadores más jóvenes tienden a descartar el conocimiento sobre algoritmos y estructuras de datos, y la mayoría de los programadores más viejos tienden a olvidarlo. O gran notación O y análisis de complejidad. Los programadores mayores tienden a descartar patrones u olores de código, o incluso no saber que existen. La mayoría de los programadores de cualquier generación, incluso si conocen los patrones, nunca recuerdan exactamente cuándo usar las piezas y los controladores. Pocos programadores de cualquier generación evalúan constantemente su código según los principios SOLID. Muchos programadores mezclan todos los niveles posibles de abstracción en todo el lugar. No estoy al tanto de un compañero programador, por el momento, para evaluar constantemente su código contra los hedores descritos por Fowler en su libro de refactorización. Aunque algunos proyectos usan alguna herramienta de métrica, la métrica más utilizada es la complejidad, de un tipo u otro, mientras que otras dos métricas, el acoplamiento y la cohesión, se ignoran en gran medida, incluso si son muy importantes para limpiar el código. Otro aspecto que casi todos ignoran es la carga cognitiva. Pocos programadores tratan las pruebas unitarias como documentación, y aún menos son conscientes de que las pruebas unitarias difíciles de escribir o nombrar son otro hedor de código, que generalmente indica una mala factorización. Una pequeña minoría conoce el mantra del diseño impulsado por el dominio para mantener el modelo de código y el modelo de dominio comercial lo más cerca posible entre sí, ya que las discrepancias pueden crear problemas en el futuro. Todo esto debe tenerse en cuenta, todo el tiempo, si desea que su código esté limpio. Y muchos más que no puedo recordar en este momento. la métrica más utilizada es la complejidad, de un tipo u otro, mientras que otras dos métricas, el acoplamiento y la cohesión, se ignoran en gran medida, incluso si son muy importantes para un código limpio. Otro aspecto que casi todos ignoran es la carga cognitiva. Pocos programadores tratan las pruebas unitarias como documentación, y aún menos son conscientes de que las pruebas unitarias difíciles de escribir o nombrar son otro hedor de código, que generalmente indica una mala factorización. Una pequeña minoría conoce el mantra del diseño impulsado por el dominio para mantener el modelo de código y el modelo de dominio comercial lo más cerca posible entre sí, ya que las discrepancias pueden crear problemas en el futuro. Todo esto debe tenerse en cuenta, todo el tiempo, si desea que su código esté limpio. Y muchos más que no puedo recordar en este momento. la métrica más utilizada es la complejidad, de un tipo u otro, mientras que otras dos métricas, el acoplamiento y la cohesión, se ignoran en gran medida, incluso si son muy importantes para un código limpio. Otro aspecto que casi todos ignoran es la carga cognitiva. Pocos programadores tratan las pruebas unitarias como documentación, y aún menos son conscientes de que las pruebas unitarias difíciles de escribir o nombrar son otro hedor de código, que generalmente indica una mala factorización. Una pequeña minoría es consciente del mantra del diseño impulsado por el dominio para mantener el modelo de código y el modelo de dominio comercial lo más cerca posible entre sí, ya que las discrepancias pueden crear problemas en el futuro. Todo esto debe tenerse en cuenta, todo el tiempo, si desea que su código esté limpio. Y muchos más que no puedo recordar en este momento. mientras que otras dos métricas, el acoplamiento y la cohesión, se ignoran en gran medida, incluso si son muy importantes para un código limpio. Otro aspecto que casi todos ignoran es la carga cognitiva. Pocos programadores tratan las pruebas unitarias como documentación, y aún menos son conscientes de que las pruebas unitarias difíciles de escribir o nombrar son otro hedor de código, que generalmente indica una mala factorización. Una pequeña minoría conoce el mantra del diseño impulsado por el dominio para mantener el modelo de código y el modelo de dominio comercial lo más cerca posible entre sí, ya que las discrepancias pueden crear problemas en el futuro. Todo esto debe tenerse en cuenta, todo el tiempo, si desea que su código esté limpio. Y muchos más que no puedo recordar en este momento. mientras que otras dos métricas, el acoplamiento y la cohesión, se ignoran en gran medida, incluso si son muy importantes para un código limpio. Otro aspecto que casi todos ignoran es la carga cognitiva. Pocos programadores tratan las pruebas unitarias como documentación, y aún menos son conscientes de que las pruebas unitarias difíciles de escribir o nombrar son otro hedor de código, que generalmente indica una mala factorización. Una pequeña minoría es consciente del mantra del diseño impulsado por el dominio para mantener el modelo de código y el modelo de dominio comercial lo más cerca posible entre sí, ya que las discrepancias pueden crear problemas en el futuro. Todo esto debe tenerse en cuenta, todo el tiempo, si desea que su código esté limpio. Y muchos más que no puedo recordar en este momento. Otro aspecto que casi todos ignoran es la carga cognitiva. Pocos programadores tratan las pruebas unitarias como documentación, y aún menos son conscientes de que las pruebas unitarias difíciles de escribir o nombrar son otro hedor de código, que generalmente indica una mala factorización. Una pequeña minoría es consciente del mantra del diseño impulsado por el dominio para mantener el modelo de código y el modelo de dominio comercial lo más cerca posible entre sí, ya que las discrepancias pueden crear problemas en el futuro. Todo esto debe tenerse en cuenta, todo el tiempo, si desea que su código esté limpio. Y muchos más que no puedo recordar en este momento. Otro aspecto que casi todos ignoran es la carga cognitiva. Pocos programadores tratan las pruebas unitarias como documentación, y aún menos son conscientes de que las pruebas unitarias difíciles de escribir o nombrar son otro hedor de código, que generalmente indica una mala factorización. Una pequeña minoría conoce el mantra del diseño impulsado por el dominio para mantener el modelo de código y el modelo de dominio comercial lo más cerca posible entre sí, ya que las discrepancias pueden crear problemas en el futuro. Todo esto debe tenerse en cuenta, todo el tiempo, si desea que su código esté limpio. Y muchos más que no puedo recordar en este momento. s mantra para mantener el modelo de código y el modelo de dominio comercial lo más cerca posible entre sí, ya que las discrepancias pueden crear problemas en el futuro. Todo esto debe tenerse en cuenta, todo el tiempo, si desea que su código esté limpio. Y muchos más que no puedo recordar en este momento. s mantra para mantener el modelo de código y el modelo de dominio comercial lo más cerca posible entre sí, ya que las discrepancias pueden crear problemas en el futuro. Todo esto debe tenerse en cuenta, todo el tiempo, si desea que su código esté limpio. Y muchos más que no puedo recordar en este momento.
¿Quieres escribir código limpio? No se requiere magia. Simplemente aprenda todo lo que se requiere, luego úselo para evaluar la limpieza de su código y refactorice hasta que esté satisfecho. Y siga aprendiendo: el software todavía es un campo joven y se adquieren nuevas percepciones y conocimientos a un ritmo acelerado.
fuente