El coste basal del software
Uno de los conceptos más importantes en el desarrollo de producto: cómo el aumento de la complejidad resta capacidad al equipo y por qué es imprescindible reducirla maximizando el trabajo no hecho.
Siguiendo con la temática de la importancia de reducir la complejidad que comenzamos la semana pasada, el de hoy puede ser el artículo que más he recomendado interna y externamente al respecto.
Lo escribió Eduardo Ferro, actualmente Head of Platform en Clarity.ai, y quien, con sus más de 20 años de experiencia en el sector, es sin duda todo un referente en cuestiones de ingeniería y producto.
El artículo en cuestión se titula “Basal Cost of Software”. Le pedí a Eduardo permiso para traducirlo para poder acercarlo así a la comunidad, y aceptó gustosamente, cosa que no puedo dejar de agradecerle.
Eduardo escribe regularmente en su blog eferro's random stuff, y le podéis encontrar en Twitter como @eferro. También da charlas habitualmente, y es fácil encontrar algunas de ellas en Internet. Por ejemplo, para complementar esta lectura, podéis escuchar estas dos:
También podréis encontrar algunos otros enlaces que el propio Eduardo recomienda al final del artículo.
Sin más, os dejo con el texto. ¡Disfrutadlo!
El coste basal del software
En mi opinión, el coste de desarrollo de cada funcionalidad en un producto puede dividirse de la siguiente manera:
El coste directo de desarrollo o coste inicial.
Un coste semanal o mensual únicamente asociado con su existencia en el sistema. Haciendo una comparación con la Tasa Metabólica Basal del cuerpo humano, podríamos llamar a este segundo coste la Tasa Metabólica Basal o Coste Basal.
El Coste Basal se compone de dos partes diferenciadas:
El impacto directo en la capacidad del equipo debido a la complejidad añadida (nuevas dependencias, más código para entender, más posibilidades de bugs ocultos, etc.).
El impacto en el coste de desarrollo o evolución de otras funcionalidades debido a posibles incompatibilidades, acoplamiento, etc.
El coste inicial
Es el coste incurrido por el equipo durante el desarrollo inicial de la funcionalidad. Incluye el coste desde que el equipo comienza a trabajar en la funcionalidad hasta que el cliente la tiene disponible y comienza a usarla. Por supuesto, este proceso debería consistir en múltiples despliegues y lanzamientos parciales para obtener feedback y hacer los ajustes necesarios...
El Coste Basal
Tras el coste inicial de desarrollo, afrontamos también un coste continuo que reduce la capacidad del equipo para el desarrollo de nuevas funcionalidades (innovación).
Este coste, que continúa con el tiempo y solo termina con la eliminación de la funcionalidad o el fin de vida del producto, es el coste que paga el equipo por la existencia de ese código en el producto.
Es importante tener en cuenta que este no se refiere al coste de hacer modificaciones o corregir errores; se refiere al coste de simplemente tener el código allí...
¿Por qué se da este coste? ¿Por qué una funcionalidad que no evoluciona supone un coste?
El equipo tiene que conocer ese código (dónde está, qué dependencias tiene, qué interactúa con él, cómo está diseñado...).
El conocimiento y las técnicas del equipo están en constante evolución. Cuando mejora en cualquiera de estos aspectos, deberá actualizar el código.
Cuando el equipo diseña una nueva funcionalidad, el código debe ser diseñado de manera que sea compatible con todas las anteriores y no genere problemas o regresiones. Y, por supuesto, este coste es proporcional a todas las que ya teníamos en el sistema.
Algo similar ocurre cuando un nuevo miembro se une al equipo. Este nuevo integrante debe aprender sobre todas las funcionalidades, por lo que el coste es proporcional a su número..
Y lo peor de todo, este sobrecoste es continuo hasta la "muerte" de la funcionalidad. Por ejemplo, hasta el fin de la vida del producto, hasta que ningún usuario la use, o hasta el fin del mundo (lo que ocurra primero).
Evolución del coste de una funcionalidad
Como hemos visto, el Coste Basal de una funcionalidad es más o menos constante durante su vida. Pero cada lenguaje, dependencia o tecnología que hayamos usado en su desarrollo, puede alcanzar un punto en el que ya no es utilizable por cualquier razón (dependencias obsoletas, problemas de seguridad, evolución normal que deprecia la versión del lenguaje que usamos, etc.). Desde este momento, el coste puede dispararse porque estamos obligados a actualizarlo, incluso aunque no queramos evolucionar la funcionalidad.
En resumen, el coste real puede ser representado por el siguiente gráfico:
El problema
Un error común es negar el Coste Basal y considerar que si no se hacen cambios, el coste de mantenimiento de una funcionalidad es cero. Supongo que esto proviene de utilizar metáforas de otras profesiones como la construcción, que no son aplicables al desarrollo de software.
La capacidad no es infinita
A pesar de que la capacidad de un equipo cambia (positiva o negativamente) con el paso del tiempo debido a distintos factores (conocimiento del negocio, técnicas, estrés…), esta es finita.
El Coste Basal acumulado de todas las funcionalidades de las que el equipo es responsable reduce la capacidad disponible para desarrollar nuevas funcionalidades (innovar).
Con el tiempo, podemos observar como la capacidad del equipo para innovar decae rápidamente:
Hasta el punto de que cuando la capacidad se agota, el equipo se encuentra en una situación en la que le es imposible innovar porque pasa todo su tiempo “manteniendo” las funcionalidades de las que es responsable.
O bien reduce su velocidad al desarrollar nuevas funcionalidades, acumulando coste basal, pero a menor ritmo.
Conclusiones
Las secciones anteriores destacan varios principios que debemos considerar para mejorar la eficiencia de nuestros equipos de producto.
Debemos minimizar el Coste Basal tanto como sea posible, logrando el impacto deseado con la menor cantidad de código posible.
Cuando sea posible, incluso sin añadirlo.
Iterando la funcionalidad, partiendo de una solución mínima, para adaptarla lo más posible a las necesidades del usuario.
Haciendo el software/diseño lo más simple posible. Debe ser fácil de evolucionar sin sobreingeniería (YAGNI).
Debemos eliminar cualquier funcionalidad del producto que no tenga el impacto deseado, eliminando así su Coste Basal.
Debemos monitorizar continuamente el código del equipo para detectar la obsolescencia de dependencias, lenguajes y tecnologías para evitar que el Coste Basal se dispare.
Veamos un ejemplo teórico sencillo para ayudar a transmitir el mensaje. Supongamos que un equipo acumula un 1% de Coste Basal por semana, lo que significa que, por cada 100 unidades de producto (código, características, mejoras), añade 1 unidad de Coste Basal. A medida que este se acumula, en 52 semanas (aproximadamente 1 año), el equipo solo podrá dedicar el 60% de su capacidad a nuevas funcionalidades/mejoras (innovación). En dos años, será apenas el 35%. Por supuesto, este ejemplo es una simplificación, pero destaca que negar este coste no es una opción viable.
Recordad: Simplificar — el arte de maximizar el trabajo no hecho — es esencial.
Relacionado y referencias
Exploring Basal Cost of Software Thoughtworks podcast, with Georgina Giannoukou and James Lewis
https://martinfowler.com/bliki/Yagni.html (concepto relacionado cost of carry)
Súper interesante 🔥
Me ha encantado. La forma en que se aborda el tema es increíblemente interesante, y me parece que tiene un potencial enorme para ser aplicado en otros contextos, no solo dentro del ámbito del software. Esta idea se puede extrapolar a una amplia variedad de organizaciones y sectores, lo que la hace aún más valiosa. Gracias por compartir.