No Hay Balas de Plata

La frase “no hay balas de plata” es muy utilizada en la industria. Revisitaremos el paper clásico y muy vigente de Fred Brooks que le dio origen.

Maximiliano Contieri
Diseño de Software
10 min readSep 13, 2020

--

Pasaron casi 35 años desde la publicación original y poco ha cambiado.

En el paper, Fred Brooks predice (hasta ahora sabemos que con acierto), que los problemas inherentes al desarrollo de software hacen que algunas tareas sean esencialmente difíciles y no encontraremos soluciones mágicas en el presente ni las habrá en el futuro.

El problema

El paper se basa en los conceptos aristotélicos de Esencia y Accidente.

La metáfora de la bala de plata, que dá título al artículo, tiene dos sentidos:

El primero nos indica que estamos frente a un monstruo que no puede ser eliminado con armas convencionales y conocidas.

El segundo significado nos habla de un enemigo que fue nuestro aliado, pero luego se transformó en nuestro temor más grande.

El hombre lobo crece desde adentro de nuestros proyectos.

El licántropo se transforma de manera inesperada.

Donde teníamos un contacto familiar ahora existe un monstruo que es necesario derrotar.

No se trata de un extraterrestre que vino como inesperada amenaza exterior, es uno de nuestros aliados, súbitamente convertido en nuestra peor pesadilla.

Foto por NeONBRAND en Unsplash

En el lugar que teníamos un proyecto planificado y bajo control, nos encontramos en el presente con un monstruo consumidor de tiempo y recursos, fallas en las fechas de entrega y sin un final en el horizonte.

Necesitamos la bala de plata para poder volver a tener al monstruo bajo control.

El autor

Frederick Brooks es autor del libro The Mythical Man-Month.

El trabajo recopila la experiencia de Brooks en el desarrollo del proyecto de software más grande hasta el momento: el sistema operativo OS/360.

En el libro aparece la famosa tesis conocida ahora como Ley de Brooks:

Si una embarazada tarda 9 meses en gestar un hijo, con nueve embarazadas no se puede acelerar el proceso de gestación.

Uno de los corolarios de dicha ley sostiene que agregar desarrolladores a un proyecto atrasado, lo demora aún más.

Esta tesis enfrenta a la idea taylorista del desarrollo de software, en sintonía con lo propuesto por Peter Naur.

Brooks ganó el premio Turing por sus trabajos en el año 1999.

La tesis

Brooks argumenta que ningún desarrollo tecnológico presente (en 1986), ni futuro (por lo menos hasta la fecha del artículo), podrá reducir los costos y planificación de los proyectos del software.

Este proceso de reducción, sí ha ocurrido sistemáticamente con el hardware como lo enuncia la ley de Moore.

Los problemas esenciales

Históricamente, hemos querido encasillar a la ingeniería del software como una metáfora más familiar y conocida, similar a la construcción de puentes o edificios. La ingeniería civil es previsible y familiar y cuenta con milenios de historia y experiencia.

Los informáticos queremos identificarnos con dicha ingeniería y buscamos similitudes que no existen si tomamos en cuenta la realidad, muy diferente, del desarrollo del software.

Existen atributos específicos y únicos en el software que lo hacen esencialmente distinto a todas las ingenierías conocidas:

Según Brooks, nos preocupamos por errores accidentales (tales como errores de sintaxis de código o compilación) en vez de enfrentarnos a los problemas esenciales (los errores conceptuales de modelado del software).

La complejidad

Las entidades modeladas en el software serán, esencialmente, varios órdenes de magnitud más complejas que cualquier otra construcción humana.

Un sistema con 300 configuraciones booleanas tiene más combinaciones de prueba (2³⁰⁰), que la cantidad de átomos existentes en el universo (10⁸⁰).

Foto por Jeremy Thomas rn Unsplash

Esta explosión inmanejable de estados hace que un sistema sea muy difícil de testear, afectando la confiabilidad.

La inmensa cantidad de variables hace que el proceso de desarrollo sea esencialmente difícil de manejar por seres humanos.

La complejidad de la construcción del software, genera problemas de administración, de capacitación y de transferencia de conocimiento, provocando que el software esté intrínsecamente atado a las personas que lo desarrollaron, como enumera Peter Naur.

La originalidad de cada desarrollo de software provoca que cada problema nuevo sea diferente y no logremos encontrar buenas herramientas de reuso de componentes repetidos como en otras industrias donde pueden construirse nuevos modelos con piezas estándar.

El reuso ha sido siempre un norte en el desarrollo del software. Sin embargo, pasamos gran parte de nuestro tiempo inventando nuevamente la rueda, descartando librerías, creando nuevos lenguajes, reinventando el manejo de excepciones, redescubriendo las funciones anónimas o las ventajas de la programación funcional, inmutabilidad etc.

Foto por serjan midili en Unsplash

La dependencia entre componentes de software es mucho más compleja que la de las máquinas, estructuras físicas o conceptos científicos.

Las relaciones entre estos componentes son únicas e intrincadas y dificultan enormemente la evolución del todo por el acoplamiento entre las partes.

La complejidad de los sistemas de software es un problema esencial

Las relaciones entre componentes de software nunca son lineales, por lo tanto, su combinatoria se torna exponencial.

Los matemáticos y físicos han tardado siglos en buscar explicaciones simples para unificar conceptos aparentemente aislados como la Teoría del todo

Foto por Chang Duong en Unsplash

Conformidad

Sumada a la complejidad esencial del desarrollo del software y, debido a la inmadurez de nuestra profesión, le agregamos una capa de complejidad accidental al hacer modelos mucho más complejos de lo necesario.

Dado que no podemos evitar la complejidad esencial, la única tarea de un ingeniero de software debe consistir en eliminar toda complejidad accidental.

Maleabilidad

Nadie le pediría a un ingeniero civil que cambie los cimientos del sótano después de haber construido una torre de 60 pisos. Sin embargo, es una tarea que los ingenieros informáticos muchas veces aceptamos porque nuestro producto es mucho más maleable, aún estando en ambientes productivos.

Las líneas de producción automotrices muy difícilmente hagan una retirada de producto de un auto una vez vendido.

Sin embargo, introducimos cambios en los sistemas de manera periódica. Tanto si el software funciona como se espera, para realizar mantenimiento evolutivo, así como si tiene fallas, si necesita adaptarse a nuevos contextos, leyes, cambios tecnologicos etc.

El software vive mucho más tiempo del estimado inicialmente, por eso muchos de los desarrolladores del mundo están encargados de mantener estos sistemas “heredados”

Invisibilidad

El software es invisible. Durante décadas se intentó diseñar el software con planos como hacen los arquitectos. Todos esos intentos fracasaron.

El diseño del software está presente en el código, nunca en los diagramas. Los diagramas no ejecutan, no tienen errores y nadie los mantiene. Su lugar es la basura.

El software no tiene una representación espacial ni geométrica. La gran cantidad de ejes de cambio hace que no podamos imaginarlo ni visibilizarlo.

Tan solo realizar curvas de nivel para poder observar algunos aspectos aislados, como hacen los tests unitarios.

Sin embargo, nunca podemos abarcar su totalidad, seguir el flujo de los datos o ver todas las interacciones entre objetos.

Intentos fallidos

Foto por engin akyurt en Unsplash

Brooks enumera posibles balas de plata que para 1986 ya habían fracasado:

Lenguajes de alto nivel

Los lenguajes de alto nivel estaban en alza en la década del 70 y eran la esperanza por aquel entonces. Brooks acierta al afirmar que dichos lenguajes eliminan parte de la complejidad accidental al estar lejos de la máquina, sus estados, registros, discos etc.

Lo que parecía un asomo de esperanza, sigue aún muy lejos de la realidad en momentos tristes como 2020, donde lenguajes como Go o C# privilegian la ganancia de unos pocos ciclos de procesamiento por sobre los buenos modelos, seduciendo a los fanáticos de la optimización prematura con promesas de corto plazo.

Sistemas de Tiempo compartido

Brooks enumera un avance de los 70s que fueron las máquinas de tiempo compartido. Actualmente tenemos virtualización casi infinita, balanceadores de carga y escalabilidad accesible para todos. Pero esto no significa que hayamos encontrado la bala de plata.

Ambientes de programa unificados

Las librerías compartidas y los sistemas operativos, como el recién nacido Unix, prometían facilidades de reuso. Pero hoy sabemos que, aunque esta sea la norma, no lograron vencer a nuestro lobo interior en órdenes de magnitud considerables.

Programación orientada a objetos

La programación orientada a objetos fue una llama de esperanza en los años 1970s por disminuir la complejidad accidental. Brooks nota con certeza que nada pueden hacer ella con la complejidad esencial.

Hoy en dia, vemos retomar conceptos descubiertos en los 1970s y olvidados por los lenguajes orientados a objetos de los 1990s como Java, Python o PHP: Así se redescubren los beneficios del encapsulamiento, el polimorfismo y la inmutabilidad.

Inteligencia Artificial

La inteligencia artificial tuvo muchos veranos muy esperanzadores y productivos, intercalados con inviernos oscuros y medievales.

Brooks enumera los ya extintos sistemas expertos con motores de inferencia basados en reglas preprogramadas. La versión moderna de estos sistemas es el machine learning, donde las reglas se “aprenden” y no están fijas utilizando aprendizaje supervisado o no supervisado.

Pero la inteligencia artificial actual no puede contra la dificultad esencial. Existen avances notables en visión y robótica y maravillas como GPT3, que logra producir buen código para resolver problemas de baja complejidad esencial.

Programación Automática

Relacionada con la inteligencia artificial, está la programación automática. Hoy existen ramas más interesantes como la programación competitiva o la generación de código mencionada en la sección anterior.

Programación Gráfica

Brooks critica fuertemente a los que buscan la bala de plata en el intento de desarrollar software de la misma manera en que se creaban los planos para la construcción de hardware.

Lo que Brooks no pudo anticipar, además del estrepitoso fracaso de las herramientas gráficas de diseño de software, es que diseñar circuitos sea en la actualidad un problema esencialmente complejo.

Verificación de software

La verificación formal de software siempre fue difícil de escalar.

Esto era válido en 1986, y en el presente.

No existen mecanismos que hagan viable verificar formalmente un software de complejidad elevada y no vemos publicaciones académicas que indiquen un cambio en esta tendencia.

Herramientas y ambientes

Los ambientes de programación o IDEs que enumera Brooks evolucionaron increíblemente en estas cuatro décadas, asistiendo a los desarrolladores a construir software cada vez más complejo.

De más está decir que ellas solas no pueden eliminar la complejidad esencial.

Promesas de 1986

Brooks enumera posibles opciones en el futuro:

  1. Construir y reutilizar software en vez de crearlo esperando una baja en los costos de desarrollo
  2. Prototipación Rápida
  3. Desarrollo iterativo e incremental
  4. Mejores diseños

Actualmente sabemos que debemos prestar atención a todos los items de esta lista, que nos van a ayudar a mejorar la calidad y son fuertemente utilizados en la industria.

Desafortunadamente, ninguno de ellos resultó ser la tan deseada bala de plata y seguimos cometiendo errores de diseño:

Actualización 1996

En la edición aniversario, Brooks aclara sobre el uso de la palabra accidental desvinculándola del valor de percance y acercándolo al concepto de casualidad.

A su vez, revisita la importancia de comprar paquetes de software en vez de producirlos y realza los progresos de la programación orientada a objetos, aunque señalando que no genera ventajas de grandes órdenes de magnitud.

El autor vuelve a poner el foco en la subestimación del reuso de software en oposición a la construcción sistemática de soluciones a los mismos problemas.

Conclusiones

Brooks nos mostró, casi 40 años atrás, que no debemos subestimar problemas esencialmente difíciles en la industria de software.

Esta afirmación se opuso al positivismo reinante de esta época, de manera similar al golpe que sufrió la matemática a principios del siglo XX con los Teoremas de incompletitud de Godel.

Una vez reconocidos los límites infranqueables, deberemos trabajar en las cosas que, afortunadamente, aún tenemos capacidad de mejorar.

Agradecimientos

Santiago Ceria me hizo conocer a Fred Brooks y sus ideas hace más de 20 años en la universidad.

Sin él no habría profundizado en su obra y no hubiera escrito este artículo.

Parte del objetivo de esta serie de artículos es generar espacios de debate y discusión sobre diseño de software.

Esperamos comentarios y sugerencias sobre este artículo.

Este artículo también está disponible en inglés aquí.

--

--

Maximiliano Contieri
Diseño de Software

I’m a senior software engineer specialized in declarative designs. S.O.L.I.D. and agile methodologies fan. Maximilianocontieri.com