¿Cómo manejas un salto de complejidad?

13

Parece una experiencia infrecuente pero común que a veces estás trabajando en un proyecto y de repente algo aparece inesperadamente, arroja una llave masiva en las obras y aumenta mucho la complejidad.

Por ejemplo, estaba trabajando en una aplicación que hablaba con los servicios SOAP en varias otras máquinas. Creé un prototipo que funcionó bien, luego desarrollé un front end regular y, en general, puse todo en funcionamiento de una manera agradable, bastante simple y fácil de seguir. Funcionó muy bien hasta que comenzamos a probar en una red más amplia y de repente las páginas comenzaron a agotar el tiempo, ya que la latencia de las conexiones y el tiempo requerido para realizar cálculos en máquinas remotas dieron como resultado solicitudes de tiempo de espera agotadas para los servicios de jabón. Resultó que necesitábamos cambiar la arquitectura para distribuir las solicitudes en sus propios subprocesos y almacenar en caché los datos devueltos para que pudieran actualizarse progresivamente en segundo plano en lugar de realizar cálculos solicitud por solicitud.

Los detalles de ese escenario no son demasiado importantes; de hecho, no es un gran ejemplo, ya que era bastante previsible y las personas que han escrito muchas aplicaciones de este tipo para este tipo de entorno podrían haberlo anticipado, excepto que ilustra una forma en que uno puede comenzar con una premisa y un modelo simples y de repente tener una escalada de complejidad en el desarrollo del proyecto.

¿Qué estrategias tiene para lidiar con este tipo de cambios funcionales cuya necesidad surge, a menudo como resultado de factores ambientales en lugar de cambios en las especificaciones, más adelante en el proceso de desarrollo o como resultado de las pruebas? ¿Cómo se equilibra entre evitar los riesgos de optimización prematura / YAGNI / ingeniería excesiva de diseñar una solución que mitigue los problemas posibles pero no necesariamente probables en lugar de desarrollar una solución más simple y fácil que sea tan efectiva pero no incorpore preparación para cada eventualidad posible?

Editar: La respuesta de Crazy Eddie incluye "lo succionas y encuentras la forma menos costosa de implementar la nueva complejidad". Eso me hizo pensar en algo que estaba implícito en la pregunta pero que no planteé específicamente.

Una vez que golpeas ese golpe, e incorporas los cambios necesarios. ¿Hace lo que mantendrá el proyecto lo más cerca posible del cronograma posible pero puede afectar la capacidad de mantenimiento o vuelve a su arquitectura y la reelabora en un nivel más detallado que puede ser más sostenible pero retrasará todo durante el desarrollo?

glenatron
fuente

Respuestas:

8

Lo que me viene a la mente al leer esto es el adagio ágil: abordar las tareas más riesgosas y / o menos entendidas primero dentro del ciclo de vida del proyecto . Es decir, intente armar un esqueleto de trabajo del proyecto lo antes posible, para demostrar que el concepto funciona. Esto a su vez también permite ejecutar cualquier tipo de pruebas crueles para detectar si la arquitectura realmente cumple su promesa en circunstancias de la vida real. Además, si hay alguna tecnología / plataforma / herramienta nueva y desconocida incluida en la solución, tómela también temprano.

Si la arquitectura central está bien, las funcionalidades individuales se pueden agregar y probar de forma incremental y refactorizar cuando sea necesario, con un costo relativamente menor. La necesidad de cambiar la arquitectura es el gran riesgo, que uno debe enfrentar por adelantado. Esto proporciona una retroalimentación rápida: en el peor de los casos, si todo el concepto se desmorona, lo sabemos temprano y podemos abortar el proyecto con una pérdida mínima.

Péter Török
fuente
6

Su ejemplo tocó algunos de los aspectos más desafiantes de la programación, a saber, la computación distribuida y la programación concurrente , que cada vez se usan más y hacen que los programadores trabajen cada vez más difícil.

Incluso la programación "normal" (subproceso único en una máquina) es tan enormemente compleja para cualquier programa no trivial, que se necesita una gran habilidad y años de experiencia para obtener algo bueno, pero aún lejos de estar "resuelto". Incluso en este nivel, las complejidades, en su mayoría debidas a una explosión combinatoria , superan con creces la capacidad del cerebro humano para comprender y comprender por completo. Pensar lo contrario es una tontería.

La computación distribuida y la programación concurrente agregan dos dimensiones más en el tamaño del espacio de "complejidad", que crece al menos en cubic (sp?) (N ^ 3) en comparación con la programación "normal". Solo por ejemplo, piense en algunos nuevos conjuntos de problemas y falacias que tenemos que enfrentar. Incluso jugar con una idea, que podría comprender las interconexiones y los efectos secundarios a esta escala es ridículo.

Claramente no tengo balas de plata, pero estoy bastante seguro de que el mayor error que uno puede cometer es pensar que lo entiendes todo y lo resolviste.

Algunas ideas sobre cómo lidiar con todo esto, además de lo que otras respuestas ya han cubierto:

  • Gran humildad
  • Acepte que su sistema / programa es imperfecto, impermanente e incompleto .
  • Prepárate para errores
  • Aceptar el cambio
  • Plan de redundancia
  • Piensa en pruebas futuras
  • Mire (o estudie) biología o sociología cómo se comportan los sistemas complejos
  • Haga todo lo posible para evitar el estado mutable. Elija protocolos sin estado (como REST y HTTP).
  • La programación funcional podría aliviar algo del dolor

Supongo que podría seguir y seguir. Tema muy interesante :)

Maglob
fuente
Mire (o estudie) biología o sociología cómo se comportan los sistemas complejos : vamos. El resto de su respuesta fue sólida, pero esto tiene una aplicación tan periférica al problema descrito.
Jim G.
1
@ Jim G. Quizás. La biología no ayudará a optimizar sus for-loops, pero si desea encontrar nuevas perspectivas, ideas o abstracciones efectivas (sobre el desarrollo de software), ayuda a salir de la caja de arena. Argumentando que la biología (o la sociología) no tiene nada que ver con la programación, solo se aleja un poco de argumentar que OOP o los patrones de diseño no tienen nada que ver con la programación. Por ejemplo: OOP : biología -> Alan Kay -> OOP / Smalltalk. O Patrones de diseño : sociología -> diseño urbano -> Christopher Alexander -> Un lenguaje de patrones -> Patrones de diseño.
Maglob
@Jim G. Cont. Algunas citas, Alan Kay: "Pensé en los objetos como células biológicas y / o computadoras individuales en una red, solo capaces de comunicarse con mensajes", y Wikipedia: "[Patrón de diseño] La idea fue presentada por el arquitecto Christopher Alexander en el campo de la arquitectura [1] y se ha adaptado para varias otras disciplinas, incluida la informática "
Maglob
Bien. Le doy un +1 para Intentar su máximo para evitar el estado mutable y otras pepitas. Mi punto es que si su gerente le encomendó reducir la complejidad, seguramente aplicaría la afeitadora de Occam al problema y se pondría a trabajar. No creo que usted u otra persona "recurra a la biología" para obtener ayuda con el problema inmediato.
Jim G.
2

No estoy de acuerdo con el espíritu de la respuesta de @ Péter Török porque supone que un equipo (o individuo) necesariamente puede prever los elementos más riesgosos al principio del ciclo de vida del proyecto. Por ejemplo, en el caso del OP, el equipo no pudo prever la creciente complejidad asociada a la solución de subprocesos múltiples hasta que sus espaldas estuvieran contra la pared.

La pregunta del OP es buena y habla de un problema que tienen muchas tiendas de desarrollo de software.

Así es como trataría el problema:

  1. Siga los consejos de Fred Brooks y organice a sus desarrolladores como un equipo de cirugía .
  2. Elija un maestro cirujano sabio y "benevolente" que pueda: A) Obtener la confianza y el respeto de sus compañeros; y B) Tomar decisiones difíciles de manera oportuna.
  3. Espere que el cirujano maestro reduzca la complejidad en el front-end y el back-end del proceso de desarrollo.

Más sobre el punto 3:

  1. El maestro cirujano debe hacer un esfuerzo consciente para proponer la solución más simple que funcione. Años de experiencia significativa deberían colocar al maestro cirujano en una posición para hacerlo.
  2. La organización más amplia, que son los superiores del maestro cirujano, debe dar al equipo suficiente tiempo y recursos para reducir la complejidad después de la fecha de envío. Esto permitirá que el equipo de desarrollo envíe el código de manera oportuna y realice kaizen para reducir la complejidad de forma continua.
Jim G.
fuente
En mi humilde opinión, en el caso del OP, deberían haber comenzado a probar antes, para descubrir cómo (y si) su arquitectura funciona en circunstancias de la vida real. Por cierto, sugiriendo que tener un "maestro cirujano", que parece dar a entender, básicamente, que no son personas que pueden prever los riesgos técnicos del proyecto - el punto exacto que reclama no estar de acuerdo con.
Péter Török
@ Péter Török: ... Al sugerir tener un "maestro cirujano", parece implicar básicamente que hay personas que pueden prever los riesgos técnicos del proyecto : No, no lo soy. Estoy diciendo que estas personas son: A) Las más adecuadas para evitar por completo la complejidad en primer lugar; y B) Se adapta mejor para sacar a un equipo de la complejidad después de que se haya enviado el código.
Jim G.
En mi humilde opinión estamos hablando de lo mismo. La experiencia que ayuda a su "cirujano maestro" a elegir la solución más simple que posiblemente pueda funcionar se basa en recuerdos de proyectos y soluciones anteriores, y saber qué solución funcionó (o no) en qué caso específico. En otras palabras, analiza las soluciones aplicables para problemas específicos y evalúa los posibles beneficios y riesgos de cada uno. Esto es lo que lo ayuda a elegir el más adecuado para la situación actual, evitando así los caminos más riesgosos .
Péter Török
1
Esto me recuerda una cita del último y excelente entrenador de caballos Ray Hunt: "¿Cómo se obtiene un buen juicio? Experiencia. ¿Cómo se obtiene experiencia? Mal juicio".
glenatron
1

Código para interfaces

Al escribir una nueva funcionalidad que interactúa con otra funcionalidad, establezca un límite en forma de interfaz (del tipo Java) a través del cual pasan todos. Esta voluntad

  1. asegúrese de tener un control total sobre las funcionalidades que se utilizan
  2. le permite tener múltiples implementaciones de la misma funcionalidad.
  3. mantenga baja la complejidad general porque los módulos solo están conectados de forma delgada en lugar de estar completamente entrelazados.

fuente
0

uno puede comenzar con una premisa y un modelo simples y de repente tener una escalada de complejidad en el desarrollo del proyecto

No es sorprendente.

Este es el desarrollo de software. Si no está inventando algo nuevo, está descargando una solución existente y probada.

Hay poco terreno intermedio.

Si está inventando algo nuevo, debe haber al menos una característica que no comprenda completamente. (Para comprenderlo completamente , tendría que tener una implementación funcional, que simplemente usaría).

¿Cómo gestionar esto?

  1. Tener expectativas realistas. Estás inventando algo nuevo. No debe haber partes que no entienda.

  2. Tener expectativas realistas. Si parece funcionar bien la primera vez, has pasado por alto algo.

  3. Tener expectativas realistas. Si fuera simple, alguien más lo habría hecho primero, y usted simplemente podría descargar esa solución.

  4. Tener expectativas realistas. No puedes predecir el futuro muy bien.

S.Lott
fuente
2
Espera, entonces lo que estás diciendo es: ¿Tienes expectativas realistas?
glenatron
0

Diseño y código teniendo en cuenta la obsolescencia. Suponga que lo que codifique hoy deberá recortarse y reemplazarse mañana.

Ian
fuente
0

El entorno debe ser parte de la especificación. Por lo tanto, un cambio en el entorno ES un cambio en la especificación. Si, por otro lado, basó su prototipo y diseño en un entorno diferente al que estaba en la especificación, cometió un error tonto. De cualquier manera, lo absorbe y encuentra la forma menos costosa de implementar la nueva complejidad.

Edward extraño
fuente
0

Como con la mayoría de los problemas de programación, depende , en mi opinión. Este problema es tan intrínseco al trabajo creativo, que no debes olvidar que las fallas van a suceder, y eso está bien . La programación es un problema perverso y, por lo general, no conoce la solución correcta hasta que ya lo haya resuelto.

Sin embargo, hay una serie de factores locales y específicos que podrían entrar en juego aquí, como:

  • Los objetivos de este sistema. ¿Es una cosa única? ¿Tiene la intención de mantener este sistema funcionando a mediano y largo plazo?

Para las cosas a corto plazo, puede que no valga la pena pensarlo más que suficiente para que funcione. La refactorización es costosa y es algo que no crea un valor final inmediato para su usuario. sin embargo, prácticamente no hay ningún caso en el que pueda pensar que no sea un software descartable absoluto, donde es tan corto que no vale la pena mejorar su diseño. Es mucho más importante poder entender lo que hiciste y arreglarlo rápidamente, que terminar ahora mismo. Si es a largo plazo, es muy probable que valga la pena eventualmente (y posiblemente mucho antes de lo que piensan todos los involucrados), o lo contrario (no hacerlo causará dolor muy pronto en lugar de "cuando tengamos que arreglarlo"). Estoy casi tentado a decir "siempre tómate el tiempo para mejorarlo", pero hay algunos casos en los que eso no es posible.

  • Los objetivos del equipo. ¿Es más un "hazlo ahora, a cualquier costo", o un tipo de "hagámoslo bien"?

Esto debería influir enormemente en sus decisiones. Su equipo respaldará esta decisión dándole recursos para rediseñar, o exigirá que se haga una solución rápida ahora. En mi opinión, si descubres que el equipo te está presionando constantemente en la dirección equivocada, es una gran bandera roja. He visto este tipo de cosas terminar en un escenario donde hay una constante extinción de incendios, donde nunca hay tiempo para rediseñar porque siempre estás solucionando los problemas que crea tu mal diseño. Sin embargo, también puede haber un término medio: "cinta adhesiva" ahora, arregle lo antes posible (pero en realidad hágalo).

  • Tu comprensión del problema. ¿Por qué no funcionó la solución anterior?

Realmente importante. Piense en cuál es el error o problema y por qué está sucediendo. Este tipo de situación es una gran oportunidad para encontrar suposiciones, restricciones e interacciones defectuosas (o faltantes). En general, siempre favorezca comprender mejor su problema en lugar de resolver el problema actual. Esta es probablemente su mayor defensa contra YAGNI / sobre ingeniería. Si usted entiende su problema lo suficientemente bien, entonces usted va a resolver es , y no otros problemas.

Finalmente, intente construir las cosas de la manera correcta . No estoy hablando de los errores y problemas que enfrenta cuando comprende más sobre el problema o su error humano inherente. No quiero decir "no cometer errores y hacerlo perfecto la primera vez", eso es imposible. Quiero decir, trata de manejar bien la complejidad en tu trabajo diario, arregla ventanas rotas, hazlo lo más simple posible, mejora tu código y tu pensamiento todo el tiempo. De esa manera, cuando (no si) cambian los golpes en su puerta, puede darle la bienvenida con los brazos abiertos en lugar de una escopeta.

Juan Carlos Coto
fuente