Saturday, November 09, 2024

Interesting Lean Concepts for Software Development

Translated from the original article in Spanish https://www.eferro.net/2024/04/conceptos-lean-interesantes-para.html

In this article of our series on Lean Software Development, we will focus on exploring a series of fundamental concepts that will not only enrich our dialogue throughout the upcoming articles but also aim to spark the reader's curiosity to delve into them independently. The objective is twofold: to establish a common language for discussing Lean principles and practices in software development, while also encouraging self-guided exploration to enhance our understanding and application of these concepts in the workplace.

Value

  • Value in Lean: It is defined from the customer's perspective. Any action that meets a customer need, solves a problem, or allows us to learn about that problem or about the solution's behavior is considered valuable. It is crucial to identify and focus on activities that generate value to increase efficiency. If a software change is not deployed and released to the client, its value is zero.
  • Value Stream: Represents the entire sequence of activities necessary to deliver a product or service to the customer. In our case, the value stream spans from understanding the problem until the solution (software) is in the hands of the customer. If multiple teams are involved in this stream, it is essential to identify them and optimize the entire value stream. It is not enough to be fast in development if we cannot properly deploy the solution, or if it is not the right one. We should always focus on optimizing the entire value stream.
  • Flow: Refers to the way work moves continuously through the value stream. An optimal flow is characterized by the constant movement of work, without interruptions, blockages, or bottlenecks. The goal is to maintain a continuous and sustainable flow over time, with a special focus on improving this flow throughout the entire value stream.

Efficiency

  • Flow Efficiency: This metric measures the proportion of time that a work item is actively processed compared to the total time from start to delivery. An efficient flow seeks to minimize idle time. In other words, it evaluates the relationship between the active work time on a change and the time that change remains blocked or waiting in a queue. The goal is to minimize these inactivity periods to speed up delivery.
  • Resource Efficiency: Unlike flow efficiency, resource efficiency focuses on maximizing the utilization of available resources, such as people and machines. This approach tends to keep employees as busy as possible, which often leads to task specialization and the formation of waiting queues before each specialty. This ensures that each area (front-end, QA, back-end, operations, product) always has work. However, it may result in each change taking longer to be available to the customer, countering the goals of Lean Software Development.
  • Efficiency in Lean: While Lean recognizes both types of efficiency, it prioritizes flow efficiency because it maximizes delivery speed to the customer. This does not mean that optimizing resource efficiency is ignored, but the initial priority is the customer through flow efficiency. Additionally, since Lean places a great emphasis on waste elimination, the overall efficiency achieved is usually very high. In software development terms, we first optimize the flow to release increments in the shortest possible time (flow efficiency), and once that process is functioning sustainably, we optimize resources and people's time (resource efficiency). In upcoming articles, we will see how to optimize that continuous flow of value delivery through Continuous Delivery / CD and multidisciplinary teams. This fantastic video perfectly explains the types of efficiency in Lean.




Lean Metrics

  • Throughput: Throughput in a value stream measures how much value is generated for the customer per unit of time. In a manufacturing context, this could refer to the number of pieces or items produced by a process in a specific time unit (day, hour, etc.). In software development, it refers to each increment we deploy that the customer can start using. Maximizing throughput is crucial for increasing efficiency in value delivery.
  • Lead Time: This term refers to the total time from identifying the need for a product or service until it is delivered to the customer. Reducing lead time is essential for customer satisfaction. In Lean Software Development, it corresponds to the time from detecting a need until the solution is available to the customer.
  • Cycle Time: Measures the time it takes to complete a specific work cycle within the production process. It offers insight into internal operational performance. For software development, cycle time is measured from when we start working on an increment until it is deployed and active for the customer. The goal is to minimize cycle time to obtain feedback more frequently, which is crucial for continuous improvement. For more information on the importance of working in small batches, you can check https://www.eferro.net/2021/01/small-batches-for-win-continuous.html.

  • Work in Progress (WIP): This term refers to all work items that have been started but are not yet completed. In the context of product and software development, WIP not only includes code in a branch being worked on, but also all work and context of any increment that has not been deployed and activated for the customer. This includes items awaiting code review, security validation, or tasks that are analyzed but not yet started. Essentially, any work from which the customer has not yet benefited but that we have begun in some way. When used as a metric, it represents the number of tasks in this state.
  • WIP Limit: Refers to the restriction set on the maximum number of tasks in progress allowed in a system. This limit is crucial for maintaining focus, reducing delivery time, and improving quality, as it prevents work overload and encourages the completion of tasks before starting new ones. Setting these limits helps the team improve their way of working, focusing on delivering value to the customer rather than starting new tasks when facing blocks. Maintaining a low level of work in progress minimizes the need to switch tasks, thereby reducing the waste associated with frequent context switching.


Work Organization / Flow

  • Just-in-Time (JIT): This principle focuses on producing and delivering exactly what is needed, when it is needed, and in the required quantity. In software development, this implies performing many activities only when necessary and in small batches. In terms of development, JIT involves working in very small steps and always in response to the current need. For example, we implement a feature only when there is a real demand, or propose an architecture improvement when business or customer requirements necessitate it. Working this way is very efficient but requires flexibility in the process and good technical practices that allow agility in work.
  • Pull Processing: This approach ensures that production is based on real demand, as opposed to Push Processing, which produces according to plan. In software development, working with a Pull approach means that we start tasks only when there is a specific customer demand or when based on unvalidated business hypotheses. This improves efficiency and reduces overproduction. In contrast to the Push method, which defines a work plan and pushes tasks regardless of actual demand.


Waste

  • Muda: This Japanese term means "waste." In Lean, seven types of muda are identified that must be eliminated to optimize processes: overproduction, waiting time, transportation, over-processing, inventory, movement, and defects. In the next article in this series, we will describe the main types of waste in software product development and explore practices that help us eliminate or minimize them.
  • Muri: Refers to the Japanese concept of overload or unnecessary effort. In the Lean context, muri implies excessive pressure on employees and processes, which can lead to inefficiency and employee burnout. The goal of eliminating muri is to ensure that work and resources are optimized without overloading the system, promoting an efficient and sustainable work environment. In our teams, muri can result from excessive cognitive load or pressure to complete tasks in a Push process. In a Pull process, muri is less likely to occur, as WIP is limited and tasks are only started when capacity is available after completing another.
  • Mura: Means inconsistency or irregularity. In Lean, mura is associated with variability in production processes that leads to inefficiencies and waste. The strategy for addressing mura includes standardizing processes and leveling the workload, resulting in a more consistent and predictable workflow, thus improving efficiency and quality of service or product. However, in software development, where high variability is inherent (irregular demand, uncertainty, high complexity, unknowns, emerging needs), it is more important to embrace change and be adaptable (using agile development practices) rather than trying to avoid it.

Image used with permission from author Jose Manuel Beas

(https://jmbeas.es/presentaciones/valor-y-tipos-de-desperdicio/)

Change and Improvement

  • Kaizen: This Japanese philosophy, meaning "change for the better," is fundamental in Lean thinking. Kaizen promotes continuous improvement through small, gradual changes that, cumulatively, result in significant improvements in efficiency and customer satisfaction. It involves all levels of the organization and focuses on improving daily processes, encouraging each employee to actively suggest improvements. One way to implement this Kaizen process is through retrospectives and creating the space to implement improvements identified by the team.
  • Kaikaku: In contrast to Kaizen, Kaikaku means "radical reform." This approach seeks to implement large, disruptive changes that significantly improve performance and efficiency. It is generally initiated by company or unit management and can lead to revolutionary innovations. Although Kaikaku can result in substantial improvements, it also carries greater risks due to the magnitude of the proposed changes. It is important to note that Kaikaku does NOT refer to technical or organizational changes mandated by high-risk situations for the company's survival (technical bankruptcy, organizational chaos, production incidents, etc.).
  • A3 Thinking: This is a systematic and structured approach to problem-solving, used within the Lean methodology. A3 Thinking uses a document the size of an A3 sheet of paper to condense the problem, analysis, proposed solutions, and action plans into an integrated view. This process not only promotes critical thinking and collaboration but also improves communication among team members, allowing complex challenges to be addressed more effectively and efficiently. In my experience, A3 Thinking is a very good tool for significant changes in very small steps, especially when improvements require very detailed and continuous monitoring.

Highly Recommended Resources:

Some of the concepts described in this article, while fundamental, can be initially counterintuitive. Experience shows that they are often better understood through visual demonstrations. For this reason, I strongly recommend watching the following videos, most of which are quite short, to deepen and truly understand these principles practically:

Resource Efficiency vs Flow Efficiency

WIP Limits:

General processes / Team Work:

With these recommendations, we conclude this brief article on concepts that may be useful for understanding the rest of the articles in the Lean Software Development series.

In our next article, we will focus on Waste Elimination, one of the key principles of Lean Software Development.

See you in the next post!

Wednesday, November 06, 2024

Lean Software Delivery: Empoderar al equipo

Desde mi experiencia, un equipo de desarrollo de software realmente empoderado es aquel que tiene la capacidad de impactar de principio a fin en todo el Flujo de Valor (value stream). Este equipo, partiendo de una idea o de la identificación de un problema (incluso descubriendo el problema junto a sus usuarios), asume la responsabilidad de entregar valor a esos clientes. Este valor no se limita a crear nuevas funcionalidades, sino que también incluye proteger lo ya construido, minimizar el costo de desarrollos innecesarios y fomentar capacidades que ayuden a alcanzar los objetivos del negocio de manera eficiente y efectiva. La responsabilidad extremo a extremo implica evolución, operación y mantenimiento continuo de lo que hayan construido, tomando pleno 'ownership' de su desarrollo.

https://blackswanfarming.com/value-a-framework-for-thinking/ 

Los flujos de valor pueden dirigirse tanto a clientes externos como a otros equipos o departamentos dentro de una empresa. En Alea Soluciones mi equipo se hacia cargo del flujo de valor al cliente final (software de gestión/telefonía/tv/internet para operadores de telecomunicaciones) y el flujo de valor para herramientas que aceleraban y ayudaban a otro departamento encargado de los despliegues de redes y operación de la red física. En The Motion nuestro flujo de valor era para el usuario final de nuestro producto. Y tanto en Nextail, como actualmente en Clarity AI, mis equipos (team topologies: equipos de plataforma) son responsables de Flujos de Valor internos que tienen el objetivo de acelerar/habilitar otros equipos encargados de Flujos de Valor a nuestros clientes finales (team topologies: Stream Aligned teams). 

Por lo tanto, este empoderamiento, desde la idea hasta la entrega y el mantenimiento del valor, se realiza siempre dentro de un flujo de valor, ya sea interno o externo. En las empresas, suele haber una combinación de ambos tipos de flujos de valor.

Como bien describe John Cutler, estos son los llamados Product Teams.

https://amplitude.com/blog/journey-to-product-teams-infographic


Product Engineers / Orientación a impacto

La mentalidad y cultura que siempre he intentado promover ha sido la que ahora parece estar de moda: el pensamiento de "Product Engineers". Es decir, formar equipos que no se limiten a ejecutar tareas asignadas por un manager o por el área de Producto, sino que se conviertan en verdaderos buscadores de oportunidades, resolutores de problemas y generadores de valor. Estos equipos trabajan para, a partir de un problema identificado, o del descubrimiento de los problemas a resolver, ofrecer la mejor solución posible y evolucionando de forma progresiva mediante el feedback del usuario y del propio sistema desarrollado. Todo esto se hace siempre desde la perspectiva del cliente final, teniendo en cuenta tanto el corto como el largo plazo. Solo pensar en el corto plazo puede llevar a no proteger esa capacidad de seguir entregando valor indefinidamente con nuevas evoluciones sobre el software entregado. 

Por dar algunos ejemplos prácticos, en Alea Soluciones, todo el equipo se iba rotando para hablar con cliente, dar soporte, o tener reuniones con el CEO y los responsables de otros departamentos. Todos nos involucrábamos en entender por qué hacíamos las cosas, cual era el impacto que buscábamos, y con ese conocimiento, podíamos desarrollar de forma evolutiva las soluciones que se iban adaptando a ese impacto buscado. Esto nos permitía aprender de nuestro negocio, y cada vez dar mejores soluciones. 

En Clarity AI, dentro de los equipos de Plataforma, hemos hecho muchas entrevistas a usuarios internos, usado encuestas, desarrollos conjuntos con los equipos interesados o programas de betatesters y champions, cuando hemos desarrollado algún producto interno nuevo. También solemos hacer rotaciones para el soporte para que todo el mundo tenga contacto con el uso y las necesidades de nuestros clientes.

Como conseguir esta mentalidad

Al final se trata de conseguir moldear la cultura del equipo, que dicho a si suena fácil, pero que diría que es lo más difícil y da para multitud de libros.

En mi caso, y como estamos viendo en esta serie de articulos, el desarrollo de software lean, está orientado a conseguir esta cultura, que maximiza el valor (desde el punto de vista del cliente), minimiza el desperdicio y de forma natural se orienta el cliente.

Lo que me ha funcionado en el pasado para ayudarme a contruir este tipo de culturas ha sido:

  • Tenerlo muy en cuenta en la contratación, haciendo preguntas especificas sobre producto, impacto de lo que ha hecho en el pasado. Intentando identificar si ve el software como un medio o como un fin en si mismo.
  • También en el proceso de contratación intento identificar empatia por el usuario, y foco en la calidad y el testing, puesto que me parecen puntos muy relacionados con ese espíritu de aportar valor de forma sostenida.
  • Conseguir siempre una masa crítica de miembros que ya tengan esa mentalidad para que se pueda expandir. En algunos casos eso ha implicado tener gente temporalmente con esa experiencia o traer externos para conseguir esa masa critica.
  • Hacer trainings sobre vertical slicing, temas de producto, temas de DDD haciendo hincapié en lenguaje ubicuo, dominios, etc 
  • Coaching técnico, trainings y colaboraciones con gente que ya tienen esa mentalidad (en mi caso, he podido hacerlo con Carlos Ble, Modesto San Juan, Alfredo Casado, Xavi Gost, Aitor Sanz, Codium, etc) y tengo siempre en el radar ciertas empresas que se que pueden traer esa mentalidad por si se da la oportunidad de colaborar (Codium, 540, Leanmind, Code Sherpas, Codesai, tech93, etc). 
  • Y por último, lo que algunos consideran mi superpoder: Ser un puñetero PESAO, repitiendo todo el rato el mismo mensaje… sobre el foco en el usuario, la calidad sostenida, buscar el impacto, etc.

Equipos empoderados, el balance, y la confianza

Para que un equipo esté realmente empoderado, debe tener la capacidad de decidir en qué y cómo trabaja, siempre de forma alineada con las necesidades de la organización. Esto implica poder equilibrar adecuadamente el corto, medio y largo plazo, así como balancear cambios de comportamiento (funcionalidades) con cambios de estructura (mejoras de diseño o cambios de arquitectura).

Si el equipo no tiene esta capacidad y es un ente externo quien decide en qué deben trabajar en cada momento, sin una discusión real al respecto, lo más común es que surjan problemas como:

  • Una falta de equilibrio en las prioridades, que se traduce con el tiempo en una ralentización del equipo.
  • La acumulación de deuda técnica o una disminución en la calidad del código.
  • La incorporación de nuevas funcionalidades sin una evolución correspondiente en la arquitectura del sistema.

https://x.com/johncutlefish/status/1622093852969680896

En mi experiencia, la manera de conseguir que el equipo pueda tomar estas decisiones implica:

  • Ganarse la confianza de la organización trabajando de forma responsable y alineada con las necesidades del negocio.
  • Realizar entregas rápidas y en pasos pequeños, lo que permite mucho más fácilmente mezclar de forma continua pequeños cambios de comportamiento con cambios de estructura. De forma que el valor al usuario sea un flujo continuo, mientras protegemos la evolución y sostenibilidad del propio sistema.

Como estamos viendo en esta serie de forma natural Lean Software Development y Extreme Programming nos llevan a trabajar de estar forma (pasos pequeños, maximizando valor).

En última instancia, Lean Software Development busca crear bucles de retroalimentación positivos, donde:

  • El impacto generado en pequeños pasos refuerza la confianza de la organización.
  • Esta confianza se traduce en mayor autonomía para el equipo.
  • La autonomía permite tomar mejores decisiones y mejorar continuamente nuestra forma de trabajar (Kaizen).

Si el equipo inicialmente no está realmente empoderado, está en nuestras manos analizar la situación y proponer o implementar pequeños cambios para mejorarla. Lo más importante en este proceso es ganarse la confianza de la organización, ya que esto es lo que permite acelerar ese cambio hacia el empoderamiento. Al fin y al cabo, nadie va a dar más autonomía si el equipo no demuestra, paso a paso, que es responsable y que busca sistemáticamente lograr un impacto positivo.

Así como en Lean se crea un bucle de refuerzo positivo en el que:

  • Ganamos más autonomía, lo cual nos permite tener mayor impacto, y
  • Este impacto genera aún más autonomía para el equipo,

también existen bucle de refuerzo negativo. En este caso:

  • Si el equipo no logra entregar de manera frecuente o con la calidad necesaria, la organización confía menos en él.
  • Esa falta de confianza reduce la capacidad del equipo para tomar decisiones, disminuyendo su autonomía.
  • A su vez, esto reduce las oportunidades de mejorar la frecuencia de entrega y la calidad, perpetuando el ciclo negativo. 


Causal loop


Empoderamiento y arquitectura

Tal y como hemos comentado que un equipo esté empoderado implica que tenga responsabilidad extremo a extremo desde la idea hasta la puesta en producción y operación de las soluciones que desarrolle. Para que esto se produzca de una forma efectiva se tiene que conseguir:

  • No tener dependencias (o las mínimas) de otros equipos. Y en caso de tenerlas, que existan interfaces y reglas claras para coordinar el trabajo generando los menores bloqueos posibles.
  • Que trabajemos en un entorno y con una plataforma que nos permita de forma autónoma desplegar y operar en producción nuestras soluciones.

Por supuesto, todos sabemos que estos requisitos no es algo que nos suele venir dado sino que tenemos que invertir esfuerzo de forma consciente y continua en:

  • Eliminar dependencias con otros equipos y diseñar nuestras soluciones intentando no generar nuevas.
  • Que nuestra plataforma de desarrollo, despliegue y operación mejore de forma continua y sea robusta. Lo podríamos ver como un esfuerzo consciente en hacer que nuestro lugar de trabajo sea un sitio habitable, reconociendo al mismo tiempo que si no invertimos en ello, se degradaría continuamente.

Aquí comparto algunas prácticas concretas que me han funcionado en distintos equipos:

  • Mantener un ritmo sostenible (práctica de eXtreme Programming) y dar espacio para la mejora continua y la experimentación. Es fundamental evitar que el equipo esté siempre al límite de su capacidad, de modo que exista una cierta holgura para abordar mejoras internas o explorar nuevas ideas.
  • Aplicar los conceptos de "bounded context" de Domain-Driven Design (DDD) para dividir la arquitectura según criterios de negocio/dominio, en lugar de hacerlo por componentes técnicos.
  • Evitar equipos de QA separados y asignar la responsabilidad sobre la calidad externa al propio equipo. Es posible que haya perfiles especializados en calidad, pero la responsabilidad debe ser compartida por todo el equipo.
  • Introducir el principio de “You build it, You run it”, eliminando la necesidad de un equipo separado de operaciones. Esto fomenta que el equipo asuma la responsabilidad tanto de la creación de la solución como de su operación.
  • Invertir en observabilidad y monitorización, tanto desde la perspectiva de producto como desde el aspecto técnico, para asegurar un mejor entendimiento del rendimiento y los problemas.
  • Absorber el trabajo de otros equipos si esto permite eliminar dependencias. Esto debe hacerse considerando los conocimientos necesarios y el volumen de trabajo, pero en muchas ocasiones resulta más efectivo adquirir esos conocimientos y automatizar ciertas tareas para poder asumirlas y, así, eliminar la dependencia.
  • Trabajar en pasos pequeños (vertical slicing, automatización/optimización de pipelines de despliegue, pruebas exhaustivas, etc.), lo que facilita la entrega continua y reduce el riesgo en cada cambio.

Equipos NO empoderados

En algunos casos, la organización o la cultura de la empresa es el mayor impedimento para el empoderamiento de los equipos. Los escenarios más típicos que me he encontrado son:

  • División por especialidades y funciones: Cuando la organización se estructura en áreas separadas como frontend, backend, operaciones, QA, etc., lo cual dificulta la colaboración y el empoderamiento de los equipos.
  • Equipos como "feature factories": Aunque existen equipos multidisciplinares hasta cierto punto, a menudo se ven como simples ejecutores de las soluciones decididas desde Producto o Negocio, sin autonomía real en la toma de decisiones.
  • Equipos multidisciplinares con responsabilidades técnicas: A veces se intenta crear equipos multidisciplinares, pero sus responsabilidades se asignan en función de componentes divididos por criterios técnicos y no de negocio. Esto significa que cualquier funcionalidad requiere la coordinación de varios equipos, limitando la agilidad y la autonomía de cada uno.

Si este tipo de enfoques es el status quo en nuestra organización, no hay otra opción: debe cambiar. Afortunadamente, en nuestra industria ya existe mucho material sobre cómo organizar equipos, las ventajas e inconvenientes de cada tipo de estructura, la relación entre arquitectura de software y organización de equipos, y la gestión de equipos. En este sentido, considero interesantes conceptos como Team Topologies, los patrones estratégicos de Domain-Driven Design (DDD), la ley de Conway, el modelo de cambio de Kotter, el System Thinking o pensamiento sistémico, y los sistemas socio-técnicos.

Por experiencia, os digo que esperar a que los cambios sucedan mágicamente (spoiler: no suceden) no es buena idea. Cuando he visto la necesidad de un cambio, he sido yo mismo quien lo ha impulsado. Os sorprendería la cantidad de cosas que se pueden hacer sin pedir permiso.

Ejemplos y experiencias

Alea Soluciones

En mi experiencia en Alea Soluciones, tuvimos una autonomía prácticamente completa, no solo en cuanto a decidir en qué trabajar, sino también en cómo implementar nuestras soluciones. Coordinábamos directamente con el CEO y con los responsables de departamentos clave, y todas las decisiones sobre producto y organización recaían en nuestro equipo. Partíamos siempre de la estrategia general de la empresa, integrando el feedback continuo de nuestros clientes, tanto internos como externos. Además, nuestra metodología de trabajo era completamente nuestra, evolucionando constantemente dentro de un proceso de mejora continua. Incluso teníamos la libertad de decidir sobre la contratación de nuevos miembros del equipo, siempre dentro de los presupuestos acordados con la empresa.

Esa autonomía/empoderamiento fue completamente buscado, y para conseguirlo hicimos cosas como:

  • Ganar poco a poco la confianza del resto de la empresa mediante la entrega de soluciones de calidad que tenían un impacto significativo.
  • Asumir trabajo anteriormente de otros departamentos para poder quitar dependencias.
  • Promover la multidisciplinariedad de los miembros del equipo para evitar silos.

The Motion

En The Motion, aunque teníamos un alto nivel de empoderamiento, al inicio las funciones de producto y diseño/UX estaban separadas y trabajaban con varios meses de desfase respecto al resto del equipo. Este modelo, orientado principalmente al backend para la generación de videos publicitarios, funcionaba bien en un principio y no generaba fricciones. Sin embargo, a medida que el producto empezó a requerir interfaces de configuración y administración, este desfase comenzó a resultar problemático.

Con la evolución de la organización, incorporamos perfiles con habilidades en frontend y maquetación, con gran sensibilidad hacia el desarrollo de producto y el diseño visual. También se sumó un diseñador con experiencia en maquetación web, lo que facilitó una evolución progresiva del diseño. En la mayoría de los casos, trabajábamos en pairing entre el diseñador y un desarrollador frontend, permitiéndonos avanzar en pequeños incrementos y eliminar los retrasos iniciales. Este cambio nos dio mayor autonomía y mejor sincronización entre diseño y desarrollo.


Clarity AI

Los equipos que he creado desde que estoy en Clarity AI han nacido con la idea de responsabilidad extremo a extremo desde el principio, lo que me ha permitido disfrutar de las ventajas de equipos empoderados desde el inicio. 

Cuando me uní en 2020, ya estaba en marcha un proceso para crear equipos multidisciplinares, aunque las líneas de reporte seguían organizadas por perfiles (frontend, backend, data engineering y cloud/sistemas). Este cambio hacia equipos multidisciplinares resonaba con mi forma de pensar, y me uní al equipo de liderazgo en tecnología para acelerar esta transformación siguiendo las ideas de Team Topologies. En los meses siguientes, ajustamos las líneas de reporte, redefinimos las funciones de las guilds para que funcionaran más como comunidades de práctica y adaptamos el plan de carrera de ingeniería para alinearlo con la nueva estructura. Esta nueva forma de trabajar nos permitió soportar un crecimiento acelerado, en el que fuimos creando tanto equipos multidisciplinares alineados a flujos (stream-aligned) como de plataforma.

A día de hoy, aunque ha llevado tiempo, los equipos son muy autónomos y tienen una responsabilidad casi completa de extremo a extremo. En Clarity AI, varios de estos equipos necesitan conocimiento especializado sobre sostenibilidad, y estamos trabajando en incorporar ese conocimiento de la mejor manera posible para potenciar aún más su empoderamiento.

Además, hemos incluido en nuestros principios de ingeniería varios enfoques que refuerzan la responsabilidad extremo a extremo de los equipos: “You Build It, You Run It,” “Squad Autonomy Over Standardization,” y “Flow-Driven Product Delivery.”

Aplicación de los Principios Lean

Empoderar a los equipos no solo es una estrategia efectiva de organización, sino que también representa la esencia de los principios Lean. Al reducir aprobaciones y traspasos innecesarios, eliminamos el desperdicio, permitiendo que el equipo se enfoque en lo esencial y agilice su trabajo sin obstáculos burocráticos. Este enfoque refuerza el aprendizaje continuo, ya que el equipo puede experimentar y adaptarse rápidamente, desarrollando un conocimiento profundo y compartido de sus propios procesos y resultados. Al mismo tiempo, la autonomía facilita una toma de decisiones rápida, eliminando los cuellos de botella que suelen ralentizar el desarrollo, lo cual nos permite entregar valor de forma mucho más ágil. La calidad se convierte en una responsabilidad compartida y arraigada en el equipo, que adquiere la confianza necesaria para mantener altos estándares sin depender de revisiones externas.

Al aplicar estos principios Lean, se crea un entorno de desarrollo de software más eficiente, adaptable e innovador, que no solo acelera la entrega de valor, sino que también se adapta de manera natural al trabajo en pasos pequeños, permitiendo que la mejora continua y la excelencia técnica se conviertan en una parte inherente del trabajo diario del equipo.


Referencias y enlaces relacionados

Gracias:

Este artículo ha sido mejorado con el feedback de:

Friday, November 01, 2024

Product Engineering in the AI Era

When developing a product, we must ask ourselves what value we contribute if the roles of Engineering or Development are merely seen as implementers. What is the extent of this value contribution? Furthermore, how will this contribution change now that AI will assist us with a significant portion of this work?

Let's refer to the insightful diagram created by John Cutler, which classifies the different types of teams from Waterfall to Product teams.



It's straightforward to discuss the value each type of team contributes to the business. From a business perspective, Waterfall teams or "Agile teams capable of releasing" add limited value and are primarily optimized for software creation (not necessarily for creating impact). 

In contrast, Product Teams are designed for end-to-end impact and maximizing business value.

Considering that AI will automate a considerable amount of the more routine and specialized tasks, it's clear to see the potential impact on these teams.

Teams focused on creating impact, which view software as a means to an end, will be significantly accelerated by AI, while those that are merely "implementers" may find themselves replaced. 

In this context, the importance of product teams and product engineers becomes increasingly evident.

Why should we focus solely on technology and software, when we could instead concentrate on solving business and user problems?

Saturday, October 26, 2024

Introduction to Lean Software Development

Translated from the original article in Spanish https://www.eferro.net/2024/04/introduccion-al-lean-software.html

I discovered that embracing change (rather than resisting it), being flexible and adaptable (rather than adhering rigidly to a plan), allows me to make a significant impact on software development without experiencing constant anxiety. This discovery led me to enthusiastically adopt Agile Software Development (https://agilemanifesto.org/iso/en/manifesto.html). Over time, in this journey of continuous adaptation and improvement, the teams I’ve worked with have refined sustainable practices and discarded those that proved superfluous or unbeneficial. This process, enriched by Extreme Programming, Continuous Delivery, and especially by Lean principles, has allowed us to develop a highly effective work approach. However, in conversations with colleagues, I was surprised to discover that Lean principles and their application in software development are not widely known. Therefore, I decided to create a series of posts to share what Lean Software Development is and how we implement it in our teams.


An important clarification: I know that each context may require different approaches and that the agile/Lean approach is not the best option in certain contexts. However, since it’s the way I enjoy working, I almost automatically end up working in environments where it makes sense.

Origins of Lean Software Development

The concept of Lean Manufacturing, deeply rooted in the revolutionary Toyota Production System (TPS) of the 1950s, is a philosophy focused on maximizing value by eliminating waste. This approach not only transformed automotive manufacturing but also laid the groundwork for agile methodologies in software development.

Key Principles of the Toyota Production System:
  • Jidoka (Automation with a human touch): This principle emphasizes the importance of automation in production not only to reduce costs but also to prevent defects. It allows production to stop in case of any problem to avoid creating defective products.
  • Just-in-Time (JIT): Under this approach, each process produces only what is necessary for the next step, adopting a "pull" production process instead of "push."
  • Kaizen (Continuous Improvement): Kaizen promotes small, constant improvements in the process, facilitating continuous learning and adaptation, and enhancing the satisfaction of both customers and production participants.
These principles, along with others from TPS, focus on continuous improvement, waste elimination, and respect for the people involved in the processes.

The transition to agile software development was notably influenced by these ideals, especially regarding continuous improvement, process quality, and a workflow based on real demand rather than assumption-based production.

Historical Context and Evolution toward Agile Development:

After World War II, Japan faced severe economic constraints that made Western-style mass production unfeasible. In this context, the ideas of W. Edwards Deming on quality and continuous improvement found fertile ground. Deming, an American statistician and management consultant, emphasized quality and efficiency in production as means to rebuild the Japanese economy. These quality control principles, along with a focus on minimizing waste and maximizing efficiency, were adopted and adapted by Toyota, giving rise to TPS.
"Learning is not compulsory. Neither is survival." —W. Edwards Deming
In the early 2000s, as agile software development gained popularity, Mary and Tom Poppendieck, who had experience in Lean Manufacturing and were involved in agile software development, adapted Lean principles to software development. They published the first book in a series describing the Lean Software Development methodology (Lean Software Development: An Agile Toolkit), thus establishing a clear bridge between manufacturing practices and agile software development.

The principles of Lean Manufacturing, originating in TPS and reinforced by Deming’s influence, not only revolutionized manufacturing but also transcended industries, profoundly shaping agile software development. The adaptation of these principles to the software world by the Poppendiecks underscores the universality of continuous improvement, quality, and efficiency, demonstrating how approaches focused on waste elimination and respect for people can lead to significant innovations.

Fundamental Principles

Lean Software Development is based on seven fundamental principles aimed at eliminating waste and optimizing the development process:
  1. Eliminate Waste: Anything that doesn’t add value to the customer is considered waste and should be removed.
  2. Build Quality In: Quality should be an integral part of the development process, not an afterthought.
  3. Create Knowledge: Continuous learning and knowledge improvement are essential for software development.
  4. Defer Commitment: Make decisions as late as possible to maintain flexibility and adapt to changes.
  5. Deliver Fast: Fast delivery allows for early feedback and necessary adjustments.
  6. Respect People: A respectful and collaborative environment is essential for team success.
  7. Optimize the Whole: The development process should be considered as a whole to improve efficiency.

Benefits of Adopting Lean in Software Development

Implementing Lean in software development brings numerous advantages, including reduced costs and development time, increased product quality and customer satisfaction, as well as a more rewarding work environment for the team. Lean fosters a culture of constant improvement and adaptability, crucial aspects in times of high uncertainty and rapid market evolution.

For the first time in software development history, we have data that defines what a high-performance technology organization should look like, thanks to research conducted by the DevOps Research and Assessment (DORA) team. As detailed in the book Accelerate, the lean approach is fundamental in building and scaling technology teams and organizations.
Accelerate research

By integrating Lean Software Development, XP, or a combination of both with Lean Product Development and Lean Management, a high-impact team is formed, which also reduces stress and frustration—elements I consider as essential as the impact itself (https://www.eferro.net/2017/12/how-vs-what.html).

A brief preview of what will be covered in the series

In upcoming entries, we will detail each Lean principle, showing its practical application in the teams I work with. I will strive to share plenty of examples and explain our decision-making process.

As the various articles in the series are published, I will link them here:

  • Introduction to Lean Software Development (this article)
  • Interesting Lean Concepts for Software Development
  • Eliminate Waste in Software Development
  • Amplify Learning
  • Defer Decisions as Late as Possible
    • Defer Decisions as Late as Possible: Product Limits
    • Deferring Decisions and Working in Small Steps
  • Deliver as Fast as Possible
  • Empower the Team
  • Build Quality In
  • Optimize the Whole
  • Lean and XP - A Sustainable Approach for Agile Development
  • Other Lean-Influenced Concepts
While Lean has influenced various concepts and methods, such as DevOps, Lean Product Development, and Lean Startup, this series will focus on Lean Software Development. However, it’s worth mentioning that, at a team level, we seek to extend the application of Lean principles beyond development itself, encompassing product decisions and the management of other tasks and processes. You know, “Make the right thing, make the thing right.”

The next article will focus on explaining some Lean concepts that we will use throughout the series. Let’s keep learning and evolving together until then!

Resources:

Tuesday, October 08, 2024

Lean Software Development: Entregar lo más rápido posible

Uno de los principios fundamentales de Lean Software Development es entregar valor lo más rápido posible. Sin embargo, esto no significa simplemente aumentar el ritmo de trabajo, sino optimizar el flujo de valor, entregando con frecuencia, obteniendo feedback y usándolo para adaptarnos constantemente, mejorando así la eficiencia en la entrega de valor.

Vamos a explorar cómo lograrlo ajustando los procesos para movernos de manera eficiente y con confianza.

Velocidad y Dirección: No es sólo desarrollar más rápido

Ser más eficiente no se trata de construir más (ver The Building Fallacy), ni de estar siempre ocupados (eficiencia de recursos, ver The resource utilization trap). La velocidad, en términos físicos, tiene una dirección, y moverse rápido en la dirección equivocada es el mayor desperdicio que podemos tener (ver Eliminar desperdicios en el desarrollo). Para ir en la dirección correcta, necesitamos:

  • Entrega continua (Continous Deployment): para obtener feedback frecuente.
  • Humildad: para aceptar que no siempre sabemos lo que el cliente necesita ni cuál es la mejor solución técnica.
  • Adaptación: ajustando constantemente nuestra dirección técnica y de producto en función del feedback que recibimos.

Desarrollar rápido sin actuar ni decidir en base al feedback es correr en círculos. Nos agotamos mucho, pero no avanzamos nada. De hecho, es aún peor, porque acumulamos Coste Basal sin ningún beneficio (ver El coste basal del software).

¿Qué tan rápido es rápido?

Ir rápido, desde un punto de vista práctico, significa:

  •  Dar pasos muy pequeños en el desarrollo y la entrega (ver Desarrollando software posponiendo decisiones).
  • Asegurarse de que estos pasos duren entre unas pocas horas (segmentación técnica) y un día o día y medio (incrementos de producto).

Esto nos permite desplegar varias veces al día. Desplegar con esta frecuencia implica hacerlo bajo demanda, de forma segura y rápida. En un mundo ideal, estas entregas serían directamente al cliente final. Sin embargo, dependiendo del contexto, puede que haya entornos donde el despliegue a los clientes finales no sea posible, o solo se pueda hacer a ciertos usuarios (por ejemplo, en el desarrollo de sistemas embebidos o componentes de sistemas operativos). Estos entornos deberían ser la excepción (ver el modelo de Jocelyn Goldfin).

Hace unos 15 años, en Alea Soluciones, salíamos a producción entre tres y cinco veces por semana, lo cual no estaba mal, considerando que se trataba de sistemas instalados en los centros de datos de nuestros clientes. Posteriormente, en empresas como The Motion, Nextail y ClarityAI, todos entornos cloud multi-tenant, los equipos con los que he trabajado lograban desplegar varias veces al día.

En todos estos casos, alcanzamos esa velocidad de entrega utilizando Trunk-Based Development, TDD, Integración Continua y pairing/mob programming, es decir, aplicando prácticas de eXtreme Programming (XP).

Control y Velocidad: El Equilibrio Correcto

Tal como dijo el piloto canadiense de Fórmula 1, Gilles Villeneuve: "Si todo parece bajo control, es que no vas lo suficientemente rápido" ;)

Tener todo bajo control puede parecer ideal, pero en realidad, es una señal de que podrías ir más rápido. ¿Cuánto más rápido? Hasta que comiences a notar algunos fallos. Esta idea es clave cuando hablamos de desarrollar software a alta velocidad.

En mi experiencia, al trabajar con TBD, TDD y entrega continua, la confianza en el proceso crece y empiezas a acelerar, entregando cada vez más rápido. Inevitablemente, cometerás un error. Sin embargo, al trabajar en pequeños incrementos, el riesgo es bajo y corregir el fallo suele ser rápido. Tras corregir el error, es común que el equipo reduzca ligeramente la velocidad. 

Este ciclo de acelerar, aprender y ajustar es normal y, en mi opinión, es un signo de un equipo saludable que busca mejorar continuamente mientras mantiene un alto ritmo de entrega.

Necesidad de solidez en los pipelines de despliegue

En Lean, el flujo de valor comienza con una idea o experimento que queremos implementar o validar, y finaliza cuando hemos entregado el incremento, obtenido feedback y estamos listos para evaluarlo y decidir los próximos pasos.  

Aunque en la manufactura Lean se promueve la reducción de la variabilidad como una forma de estandarizar y optimizar el flujo, en Lean Product Development o Lean Software Development no todas las fases deben tener baja variabilidad. Por ejemplo, al inicio del flujo de valor, se requiere experimentación y creatividad para generar ideas y enfoques variados. Lo mismo ocurre al final del ciclo, cuando analizamos el feedback para decidir qué ajustes realizar.

Por otro lado, en la fase correspondiente al proceso “sistemático” de poner en producción, debemos asegurarnos de que haya mínima variabilidad. Esta etapa debe ser rápida, sólida y confiable, algo en lo que podamos confiar sin problemas.  

El objetivo es que los despliegues sean aburridos, una tarea rutinaria que se ejecute en piloto automático.

Para ello, los pipelines de despliegue deben cumplir con las siguientes características:

  • Ser rápidos (<10 minutos) y confiables.
  • Ofrecer un alto grado de confianza en la solución mediante buenos tests automáticos.
  • No permitir la existencia de tests inconsistentes (flaky tests).

Esto solo es posible si consideramos a los pipelines de despliegue como ciudadanos de primer nivel dentro de nuestro sistema, desarrollándolos y manteniéndolos con el mismo nivel de calidad que el resto del sistema.

Entrega continua (Continuous Deployment): El método más eficiente

Idealmente, los cambios que realizamos deberían desplegarse a producción de forma individual, uno detrás de otro. Esto nos permite obtener feedback rápidamente y entregar incrementos de valor de manera continua. Como mencionamos en este artículo y en algunos anteriores, esta forma de trabajar ofrece grandes ventajas:

  • Ajustamos continuamente el rumbo basado en el feedback.
  • Si cometemos un error, el impacto es bajo, ya que se trata de un cambio pequeño y es fácil recuperarnos al revertir a la versión anterior.
  • Además, entender cualquier error es más sencillo, porque el cambio que lo pudo haber causado está más acotado.

Sin embargo, desplegar no es gratis, tiene un coste asociado. Requiere inversión en:

  • Desarrollo de pipelines de despliegue automáticos.
  • Tiempo de espera cada vez que ejecutamos el pipeline.

Este coste se denomina "coste de transacción", y cuando es alto, limita la velocidad con la que podemos entregar.

Mantener bajos los costos de transacción

Lean Software Development recomienda mantener el coste de transacción lo más bajo posible. Para conseguirlo, es fundamental:

  • Invertir en pipelines de despliegue automáticos.
  • Usar pruebas automatizadas que nos permitan ir a producción directamente si los tests pasan.

Esto nos brinda la confianza necesaria para desplegar sin intervención manual y garantiza la calidad en cada entrega a producción.

En los equipos en los que he trabajado, nuestra opción por defecto es el Deployment continuo. Esto significa que cualquier cambio que subimos a la rama principal va directamente a producción. Para lograrlo, separamos el deployment del release mediante feature toggles, lo que nos permite liberar nuevas funcionalidades de forma controlada, sin que estén visibles inmediatamente para los usuarios finales.



Además, hemos logrado mantener los tiempos de ejecución de nuestros pipelines de despliegue entre 10 y 15 minutos, asegurando que el ciclo de entrega sea ágil y no interrumpa el flujo de trabajo. Si algo falla, solemos implementar un rollback automático cuando los smoke tests post-despliegue detectan un problema. Esto minimiza el impacto y asegura que podemos recuperar rápidamente el control.

Como destacan Jez Humble, Joanne Molesky y Barry O'Reilly en Lean Enterprise:

"The goal of continuous delivery is to make it safe and economic to work in small batches. This in turn leads to shorter lead times, higher quality, and lower costs."

Este enfoque nos permite trabajar con confianza en cambios pequeños, acelerar los tiempos de entrega, y reducir el riesgo y coste de errores.

Conclusiones

Para entregar valor rápidamente en Lean Software Development, es crucial optimizar el flujo de trabajo sin comprometer la calidad. La velocidad no implica trabajar más rápido sin control, sino ajustar continuamente en función del feedback, permitiendo una adaptación constante. Los equipos que siguen esta filosofía logran entregas continuas y seguras, minimizando riesgos y maximizando valor.

Los principios clave son:

  • Pasos pequeños y frecuentes: Entregar en incrementos pequeños reduce el riesgo y facilita la corrección rápida de errores.
  • Costes de transacción bajos: Invertir en pipelines de despliegue rápidos y fiables permite mantener la velocidad sin afectar la calidad.
  • Feedback continuo: Entregar rápidamente permite recibir retroalimentación constante, lo que facilita mejorar y adaptar el producto.
  • Confianza en la calidad: Con pruebas automatizadas y un flujo de entrega sólido, los equipos pueden estar seguros de que cada iteración libera un producto robusto.

En resumen, este enfoque no solo optimiza la entrega de valor, sino que también permite a los equipos operar de manera sostenible, con ajustes rápidos y minimizando el impacto de los errores.


Recursos relacionados

Sunday, September 29, 2024

The Building Fallacy: A Misleading Metaphor in the Age of Continuous Evolution

The "building fallacy" in software development refers to the flawed comparison between creating software and constructing a physical building. While this analogy may seem intuitive at first glance, it breaks down when we consider the nature of software and the realities of today's technology landscape. This misconception leads to inefficient processes, increased costs, and diminished capacity for innovation.


Here's why this metaphor is misleading and why it's more critical than ever to abandon it:

Why the Building Metaphor Doesn’t Hold Up

Software is Evolutionary, Buildings are Static

Buildings are designed with a fixed endpoint in mind. Once constructed, they primarily require maintenance to address wear and tear. Software, however, exists in a state of constant change. To remain relevant, it requires ongoing adaptation in response to evolving user needs, market demands, and technological advancements. Software development is more like tending a garden than erecting a structure—it requires continuous nurturing, pruning, and adjustment to thrive.

Overemphasis on Upfront Design

The building analogy often leads teams to prioritize extensive upfront planning and design, mirroring the blueprints used in construction. However, in software development, excessive upfront design can stifle innovation and lead to the dreaded "waterfall" model, where changes become costly and time-consuming. A more iterative approach allows teams to learn and adapt based on real feedback, making decisions at the last responsible moment and incorporating the latest insights.

Ignoring the Long Tail of Costs

The building fallacy promotes the misconception that most of the effort lies in the initial "construction" phase. In reality, maintaining, evolving, and supporting software systems far outweighs initial development costs.

A crucial concept that challenges the building analogy is Basal Cost—the recurring expense of maintaining features or code regardless of usage. This includes updates, security patches, and the cognitive load of supporting the code. Ignoring these costs leads to an incomplete understanding of the true financial and resource implications of software development. See more at Basal Cost of software.

Why the Building Fallacy is More Problematic Than Ever

In today's rapidly evolving tech landscape, clinging to the building fallacy is more detrimental than ever. Here's why:

The Pace of Change is Relentless

Technology, user needs, and market dynamics are shifting at an unprecedented pace. Software must evolve quickly to stay relevant. A rigid, building-centric mindset makes it hard to adapt to new challenges or opportunities.

Increased Reliance on External Systems

Modern software heavily depends on external APIs, cloud services, and open-source libraries, all of which evolve constantly. The building analogy fails to account for the complexity of managing these dependencies and ensuring seamless integration over time.

The Rise of Continuous Delivery

Continuous delivery and DevOps practices emphasize frequent releases, rapid iteration, and tight feedback loops, clashing with the notion of a fixed endpoint or grand unveiling. These practices underscore the need for flexibility, not rigidity.

Shifting to an Evolutionary Mindset

To succeed in today's environment, teams must abandon the building fallacy and embrace software as a living, evolving system. This shift involves:

Prioritizing Learning and Adaptation

Software development should be seen as a continuous process of experimentation. This means adopting agile methodologies, prioritizing validated learning over feature delivery, and delaying decisions to incorporate the latest insights.

Designing for Change

Architectures should be modular, flexible, and loosely coupled, making it easier to adapt to new requirements, integrate evolving external systems, and experiment with new technologies.

Managing Basal Costs

Teams must actively manage Basal Costs throughout the software lifecycle by:

  • Regular Maintenance: Keeping dependencies updated, applying security patches, and fixing bugs.
  • Cognitive Load Management: Simplifying the codebase and documentation to reduce the mental effort for developers.
  • Eliminating Unused Features: Removing unnecessary features to lower maintenance costs.
  • Investing in Code Quality: Refactoring, code reviews, and automated testing to improve maintainability and manage Basal Costs over time.

Lean Software Development and Extreme Programming (XP) practices are particularly effective in keeping Basal Costs low. Lean principles focus on eliminating waste, such as unnecessary code or features that add no value, which reduces maintenance burdens. XP practices, like Test-Driven Development (TDD), continuous integration, and pair programming, promote clean, maintainable code and help prevent technical debt.

By adopting these practices and embracing a mindset of continuous evolution, teams can break free from the outdated building fallacy and create software that thrives in today's dynamic technology environment.


Related content:

Sunday, September 22, 2024

Desarrollando software: posponiendo decisiones y trabajando en pasos pequeños

En esta entrega de la serie sobre Lean Software Development, después de haber explorado prácticas para posponer decisiones en el producto, hoy hablaremos sobre cómo desarrollar software dando pasos muy pequeños, aplazando decisiones, y haciendo solo lo necesario en cada momento.

Este enfoque está alineado con los principios de Lean Software Development y eXtreme Programming (XP), siendo clave en el desarrollo ágil.

Por qué trabajar en pasos pequeños

Trabajar en pequeños pasos es esencial en entornos de incertidumbre. Tanto nosotros como el cliente no siempre sabemos con exactitud qué se necesita para lograr el impacto deseado. Al avanzar en incrementos reducidos, obtenemos feedback valioso tanto del sistema —sobre su funcionamiento y comportamiento— como del cliente. Este enfoque nos permite aprender y ajustarnos constantemente, evitando decisiones prematuras que podrían limitar nuestras opciones o ser difíciles de revertir.

Es un proceso de aprendizaje continuo donde evitamos el diseño especulativo y las funcionalidades innecesarias. Al avanzar poco a poco, aceptamos que no sabemos todo desde el principio y optamos por experimentar y validar de forma constante.

Beneficios de trabajar en pasos pequeños

Trabajar en pasos pequeños con feedback continuo ofrece numerosos beneficios. Geepaw Hill, en su artículo "MMMSS: The Intrinsic Benefit of Steps", describe de forma brillante los efectos de esta práctica en los equipos. A continuación, hago un resumen, aunque recomiendo leer el artículo completo o la serie "Many More Much Smaller Steps".

Geepaw menciona ocho beneficios de trabajar en pasos de menos de un par de horas:

Beneficios en la capacidad de respuesta:

  • Interruptibilidad: Puedes manejar una interrupción o cambiar de tema sin romper el flujo de trabajo.
  • Capacidad de maniobra (Steerability): Después de cada pequeño paso, puedes reflexionar, incorporar feedback y ajustar la dirección si es necesario.
  • Reversibilidad: Si un paso no cumple con las expectativas, revertirlo implica una pequeña pérdida de tiempo.
  • Paralelismo controlado (Target Parallelism): Al avanzar en pequeños pasos consistentes, es posible trabajar en diferentes áreas del sistema o para distintos stakeholders sin dejar tareas a medias.

Beneficios humanos:

  • Alcance: Obliga a reducir la carga cognitiva, limitando las combinaciones y casos que debemos considerar.
  • Ritmo: Establece un ritmo constante en el equipo, con ciclos de recompensas rápidas (tests exitosos, commits, despliegues, etc.).
  • Seguridad: Los cambios pequeños conllevan menos riesgo que los grandes. Con tests frecuentes y despliegues diarios, el riesgo máximo es revertir el último cambio.
  • Autonomía: Permite que el equipo tome decisiones continuas, lo que requiere un esfuerzo constante por comprender y empatizar con el usuario para abordar problemas o implementar mejoras.

Trabajando en pasos pequeños y posponiendo decisiones

Desde aproximadamente 2009-2010, he intentado aplicar la práctica de trabajar en pasos muy pequeños en todos los equipos con los que colaboro. Estos pasos suelen durar pocas horas, permitiendo lanzar a producción varias veces al día y lograr cambios visibles para el cliente en uno o dos días, como máximo. Este enfoque ágil minimiza el riesgo y maximiza la capacidad de respuesta, pero requiere disciplina y la aplicación rigurosa de las prácticas de desarrollo ágil que propone eXtreme Programming (XP).

Prácticas y tácticas para trabajar en pasos pequeños

A continuación, presento algunas prácticas y estrategias que nos permiten trabajar de esta manera. A veces es difícil separarlas, ya que están estrechamente interrelacionadas y se complementan entre sí.

Desarrollo Iterativo e Incremental

La técnica más importante que usamos es también la más sencilla y, al mismo tiempo, la menos común. En lugar de partir de una solución completa y dividirla para implementarla en pasos, hacemos crecer la solución progresivamente hasta que sea suficientemente buena y podamos pasar a invertir en otro problema. Es decir, nos centramos en, teniendo la solución y el problema en mente, poner en producción (al cliente final) incrementos que estén alineados con la idea de la solución que buscamos. Usamos el feedback para asegurarnos de que vamos en la dirección correcta. Además, no tener miedo a iterar en base a este feedback nos permite trabajar en pasos pequeños y de bajo riesgo.


Por ejemplo, en este caso, partiendo de un problema inicial con una potencial solución, vamos generando los incrementos (Inc 1, Inc 2, etc.) de menos de un día. Cada incremento se entrega al usuario para obtener feedback, lo que nos ayuda a decidir el siguiente paso y si la solución ya es suficientemente buena. Así, evitamos desperdicio (zona gris) al no hacer tareas innecesarias, lo que reduce el coste basal del sistema.



Segmentación vertical (Vertical Slicing)

La segmentación vertical consiste en dividir las funcionalidades y soluciones de manera que podamos tener una aproximación incremental al desarrollo y que cada pequeño incremento aporte valor por sí mismo. Este valor puede reflejarse en mejoras para el usuario, aprendizaje para el equipo, reducción de la incertidumbre, entre otros. En lugar de dividir las historias por capas técnicas (infraestructura, backend, frontend), las dividimos por incrementos que aportan valor y, por lo general, requieren trabajo en todas las capas.

En mis equipos, aplicamos esta segmentación de manera rigurosa, procurando que ningún incremento tome más de dos días y, preferiblemente, menos de un día. Utilizamos diversas heurísticas y procesos para realizar el vertical slicing (https://www.humanizingwork.com/the-humanizing-work-guide-to-splitting-user-stories/), como el método de la hamburguesa de Gojko Adzic, que describiré más adelante.

Aunque usemos esta segmentación vertical para dividir en incrementos lo que queremos implementar, esto no implica que siempre implementemos todos los incrementos identificados. Al contrario, el objetivo es siempre hacer crecer la solución lo mínimo posible para conseguir el impacto deseado.



Abraham Vallez describe muy bien como hacer crecer progresivamente una solución en esta serie de posts 

Segmentación técnica

Como complemento a la segmentación vertical (Vertical Slicing), en mis equipos también dividimos esos incrementos que aportan valor al usuario en tareas más pequeñas que igualmente ponemos en producción. Estas tareas tienen un enfoque más técnico y suelen durar menos de dos o tres horas.

Desplegar estos incrementos técnicos nos permite obtener feedback principalmente del sistema: ¿sigue funcionando bien nuestro pipeline de CI?, ¿genera algún problema evidente el código que hemos desplegado?, ¿afecta de alguna manera al rendimiento?

Esta práctica nos obliga a mantener un coste de despliegue bajo (en tiempo y esfuerzo) y nos permite garantizar en todo momento que el flujo de trabajo sigue funcionando correctamente. Es posible porque contamos con un sistema de pruebas automatizado sólido, pipelines de CI rápidos y trabajamos con Integración Continua/Trunk-Based Development, como explicaremos posteriormente.

Poder aplicar esta segmentación técnica también es esencial para hacer cambios en paralelo, realizar modificaciones importantes en pasos pequeños y seguros, y así reducir significativamente el riesgo.

Generación de opciones

Generar opciones es fundamental para tomar decisiones bien fundamentadas. Cada decisión debe considerar múltiples alternativas; nosotros solemos intentar tener al menos tres o cuatro. Para facilitar la generación de opciones, podemos plantearnos preguntas como:

  • ¿Qué otras opciones podrías considerar si tuvieras la mitad del tiempo?
  • ¿Qué opciones requieren nuevas dependencias?
  • ¿Qué soluciones has implementado en problemas similares en el pasado?
  • ¿Cuál es el mínimo grado de sofisticación necesario para la solución?
  • ¿Quiénes podrían beneficiarse del cambio? ¿Podríamos entregarlo a cada grupo de usuarios de forma independiente?

Estas preguntas nos ayudan a generar opciones que luego el equipo puede evaluar, intentando siempre seleccionar aquellas que aporten valor rápidamente (aprendizaje, capacidad, eliminación de incertidumbre, etc.) comprometiéndonos lo mínimo posible.

Esta forma de trabajar nos permite avanzar en pequeños pasos, teniendo siempre visibilidad sobre distintas opciones que podemos tomar para continuar con el problema o redirigirlo si los pasos dados no están logrando el impacto esperado. Como ves, todo converge en trabajar con pequeños avances, aprendiendo, tomando decisiones lo más tarde posible e intentando que las soluciones sean lo más simples posibles.

Una herramienta que usamos mucho para generar opciones y realizar la segmentación vertical (vertical slicing) es el método de la hamburguesa (https://gojko.net/2012/01/23/splitting-user-stories-the-hamburger-method/) de Gojko Adzic (https://gojko.net/).

Con este método, intentamos dividir una funcionalidad o solución en los pasos necesarios para aportar valor al usuario. Esos pasos los visualizamos como “capas” de la hamburguesa, y para cada una de ellas nos forzamos a generar al menos tres o cuatro opciones. Luego seleccionamos al menos una opción de cada capa para decidir cuál será el primer incremento a implementar. Una vez implementado y entregado ese primer incremento, y con el feedback del usuario en mano, repetimos el proceso para implementar alguna de las otras opciones.

Este proceso continuo no termina cuando implementamos todas las opciones identificadas, sino cuando la funcionalidad es lo suficientemente buena o hay otra funcionalidad o problema más prioritario en el que invertir. Es decir, invertimos en lo más importante hasta que el usuario está satisfecho o hasta que surge una nueva prioridad.


Simplicidad

La simplicidad es uno de los valores fundamentales de XP y, por lo tanto, de la agilidad bien entendida. Un mantra del desarrollo ágil es “haz la cosa más simple que pueda funcionar”. Esto significa empezar con la solución más sencilla y mínima que funcione, iterando y mejorando en base al feedback.

No siempre la solución más simple es la más fácil de implementar. A veces, evitar la complejidad innecesaria requiere un esfuerzo significativo. La verdadera simplicidad es el resultado de un diseño consciente que evoluciona gradualmente.

Desarrollo en dos pasos

Kent Beck nos aconseja hacer “la cosa más simple que pueda funcionar”, pero esto a menudo se confunde con “lo primero que se me ocurra” o “lo único que sé hacer”. Una forma efectiva de asegurarnos de que estamos eligiendo la opción más simple posible es dividir cualquier cambio o incremento en dos partes:

  1. Preparación: Ajustar la base de código actual para que la nueva funcionalidad sea fácil de introducir.
  2. Implementación: Introducir el cambio real.

https://x.com/eferro/status/1810067147726508033

Esta separación evita el diseño especulativo y garantiza que solo se realicen los cambios mínimos necesarios para integrar la nueva funcionalidad, siguiendo el principio de Kent Beck: “Haz que el cambio sea fácil, y luego haz el cambio fácil”.


Principio YAGNI (You Aren't Gonna Need It / No lo vas a necesitar)

Relacionado con el punto anterior, el principio YAGNI nos recuerda que muchas ideas que se nos ocurren probablemente no serán necesarias, y que debemos enfocarnos solo en lo que necesitamos ahora. Nos ayuda a evitar el diseño especulativo y a centrarnos en lo que es realmente relevante en el momento. Incluso si identificamos algo que podríamos necesitar en el futuro cercano, YAGNI nos insta a cuestionarnos si es realmente relevante para las necesidades actuales, recordándonos que deberíamos posponerlo. Si el sistema es sencillo y fácil de evolucionar, más adelante será fácil introducir esos cambios.

Desarrollo Dirigido por Pruebas (TDD) y Outside-In TDD

El Desarrollo Dirigido por Pruebas (TDD) es una práctica que consiste en escribir primero una prueba que defina el comportamiento deseado de una funcionalidad, antes de escribir el código que lo implemente. A partir de ahí, el desarrollador escribe el código mínimo necesario para que la prueba pase, seguido de un proceso de refactorización para mejorar el diseño del código sin cambiar su comportamiento. Este ciclo se repite continuamente, lo que garantiza que cada línea de código tiene un propósito claro y definido, evitando el código innecesario o superfluo.

Outside-In TDD es una variante de TDD que comienza desde los casos de uso de negocio más amplios y se adentra en la implementación del sistema. Al partir de las necesidades del negocio y escribir únicamente el código necesario para pasar cada prueba en cada nivel (desde el nivel funcional más alto hasta las piezas individuales de código), se asegura que solo se crea el código esencial. Este enfoque previene la creación de código innecesario o la introducción de características que no son requeridas en el momento, evitando así el diseño especulativo y asegurando que se sigue siempre el principio YAGNI (You Aren't Gonna Need It).

En nuestro equipo, usamos Outside-In TDD como la forma de trabajo predeterminada para todo el código nuevo, excepto en aquellos casos donde este flujo no resulta beneficioso (spikes, algoritmos complejos, etc.). Esto implica que aproximadamente un 5-10% del código puede ser experimental para aprender, el cual es desechado posteriormente y no suele tener pruebas. Otro 10% del código corresponde a tareas donde las pruebas se escriben después (por ejemplo, integración de librerías o algoritmos complejos). El resto, la mayoría del código, se desarrolla con TDD Outside-In.

Este enfoque minimiza el desperdicio y, por defecto, sigue el principio YAGNI, ya que no se puede crear código ni diseño que no corresponda al incremento actual. Dado que el incremento actual está definido mediante una segmentación vertical radical, trabajamos en pasos pequeños, con poco desperdicio y tomando decisiones lo más tarde posible.

Como ventaja adicional, este proceso facilita la resolución rápida de errores, tanto en el código como en el diseño, ya que se verifican constantemente los avances paso a paso. Cuando se detecta un error, lo más probable es que esté en la última prueba o en el último cambio realizado, lo que permite una recuperación rápida y sin estrés.

Integración Continua, también conocida como Trunk-Based Development

Si hay una práctica técnica que obliga y ayuda a trabajar en pasos pequeños, con feedback constante, permitiendo decidir lo más tarde posible mientras aprendemos y nos adaptamos a la máxima velocidad, esa es la Integración Continua.

Primero, es importante aclarar que la Integración Continua es una práctica de XP (eXtreme Programming) que consiste en que todos los miembros del equipo integren su código en una rama principal de forma frecuente (al menos una vez al día). En otras palabras, esta práctica es equivalente a hacer Trunk-Based Development, donde solo existe una rama principal sobre la que todos los desarrolladores realizan cambios (normalmente en pareja o en equipo).

Esta práctica no tiene nada que ver con ejecutar tests automáticos en feature branches. De hecho, diría que es directamente incompatible con trabajar en ramas separadas para cada funcionalidad.

Desafortunadamente, este enfoque no es el más común en la industria, pero puedo asegurar que, junto con el TDD, es una de las prácticas que más impacto tiene en los equipos. En todos los equipos en los que he trabajado, la introducción de Integración Continua/TBD ha provocado un cambio espectacular. Nos ha obligado a trabajar en pasos muy pequeños (pero seguros), dándonos la agilidad y adaptabilidad que buscábamos.

Evidentemente, como cualquier práctica, requiere esfuerzo y el aprendizaje de una serie de tácticas para poder desplegar a producción de forma muy frecuente sin mostrar funcionalidades incompletas al usuario. Es necesario dominar estrategias que separen el deployment (decisión técnica) de la release al usuario (decisión de negocio). Las más comunes son:

  • Feature toggles: Permiten activar o desactivar funcionalidades, realizar pruebas A/B o mostrar nuevas características solo a ciertos clientes (internos, beta testers, etc.).
  • Despliegue gradual: Métodos como Canary releases o Ring deployments, que permiten realizar un despliegue progresivo del cambio.
  • Dark launches: Lanzar una funcionalidad sin que esté visible al cliente, solo para realizar pruebas de rendimiento o compatibilidad.
  • Shadow launches: Ejecutar un nuevo algoritmo o proceso en paralelo al anterior, pero sin mostrar los resultados al cliente final.

Diseño evolutivo

Esta práctica central de eXtreme Programming (XP) nos permite desarrollar software de manera incremental, refactorizando continuamente el diseño para hacerlo evolucionar conforme a las necesidades del negocio. En la práctica, consiste en crear el diseño más simple posible que cumpla con los requisitos actuales, y luego evolucionarlo en pequeños pasos a medida que aprendemos y agregamos nuevas funcionalidades.

Dentro del diseño evolutivo se pueden aplicar tácticas como:

  • Desarrollo en dos pasos.
  • Refactorización continua en el ciclo de TDD.
  • Refactorización oportunista.
  • No crear abstracciones demasiado pronto (Ver https://www.eferro.net/2015/05/aplicacion-del-principio-dry.html).
  • Cambios paralelos para mantener los tests en verde todo el tiempo mientras efectuamos cambios en varios pasos.
  • Ramificación por abstracción (Branch by abstraction) y el patrón Expandir/Contraer para facilitar cambios en paralelo.

Es importante destacar que, más allá de las tácticas que utilices para guiar el diseño en pequeños pasos, es fundamental desarrollar una sensibilidad por el diseño en el equipo. Ninguna de estas prácticas, por sí sola, enseña diseño orientado a objetos. Por lo tanto, el equipo no solo debe aprender a realizar cambios incrementales en el diseño, sino también adquirir un conocimiento profundo de los principios del diseño orientado a objetos.

Diseño evolutivo diferenciado

En general, en mis equipos intentamos siempre trabajar en pasos pequeños, enfocándonos en lo que necesitamos en ese momento y dejando que las nuevas necesidades guíen los cambios de arquitectura y diseño. Al mismo tiempo, reconocemos que la facilidad de evolución y la fricción generada ante el cambio dependen mucho del tipo de código afectado. Sabemos que no es lo mismo modificar código que implementa reglas de negocio, un API interno entre equipos o un API para el cliente final.



Cada uno de estos casos tiene más o menos fricción al cambio (es decir, distinta facilidad de evolución). Por tanto, aplicamos un diseño evolutivo diferenciado según el tipo de código.

Para el código con mayor fricción al cambio, como un API destinado al cliente final, dedicamos más tiempo a un diseño robusto que permita evolucionar sin necesidad de cambios frecuentes. En cambio, para el código interno de lógica de negocio, que solo se usa en casos específicos, aplicamos una aproximación evolutiva más flexible, permitiendo que el diseño emerja del propio proceso de desarrollo.


Otras tácticas y prácticas

Por supuesto, estas no son las únicas tácticas y prácticas a tener en cuenta, pero sí considero que son las que más nos ayudan. Existen algunos trucos y heurísticas que, aunque no los considero prácticas en sí mismas, contribuyen a la toma de decisiones y, en general, facilitan trabajar en pequeños pasos, posponiendo decisiones lo más tarde posible:

  • Priorizar librerías antes que frameworks, para no cerrar opciones y mantener mayor flexibilidad.
  • Enfocarse en hacer el código usable (y entendible) antes que en hacerlo reusable (a menos que tu negocio sea vender librerías o componentes para otros desarrolladores).
  • Usar tecnología aburrida y sólida, que esté ampliamente aceptada por la comunidad.
  • Crear wrappers ligeros sobre componentes/librerías externas para definir claramente qué parte de ese componente usamos y facilitar el testing. Puedes consultar más sobre este enfoque en https://www.eferro.net/2023/04/thin-infrastructure-wrappers.html.
  • Separar la infraestructura del código de negocio mediante Puertos y Adaptadores u otra arquitectura que permita diferenciarlos claramente.
  • Aplicar Arquitectura evolutiva para partiendo de una arquitectura mínima ir adaptandola a las necesidades de negocio, posponiendo al máximo posible las decisiones difíciles de revertir.


Conclusiones

En el desarrollo de software, la clave está en adoptar un enfoque consciente respecto a nuestras decisiones, trabajando en pasos pequeños y de bajo riesgo, y centrándonos únicamente en lo que necesitamos ahora. La simplicidad y la claridad deben ser prioridades para maximizar la eficiencia y minimizar el desperdicio.

Las prácticas de eXtreme Programming (XP), junto con los principios de Lean Software Development, nos proporcionan una guía clara para evitar el desperdicio y la sobreingeniería. Al entender que no podemos predecir el futuro con certeza, nos enfocamos en construir sistemas que sean fáciles de entender y de evolucionar, evitando la complejidad innecesaria. Trabajar de esta manera implica huir de soluciones sobredimensionadas o altamente configurables, que a menudo se convierten en obstáculos para la evolución del sistema.

Finalmente, se trata de ser humildes: asumir que no tenemos todas las respuestas y que la única forma de encontrar la solución correcta es mediante la experimentación y el aprendizaje continuo. En resumen, la simplicidad, la agilidad y la capacidad de respuesta son fundamentales para desarrollar software de manera efectiva en un entorno siempre cambiante.

Si tuviera que elegir las técnicas y prácticas que más impacto tienen en mis equipos para trabajar en pasos pequeños, seguros y posponiendo decisiones, diría que son:

  • Segmentación vertical
  • Integración Continua / Trunk-Based Development
  • TDD

Todo ello con un foco constante en la simplicidad.

Cada una de las prácticas y tácticas mencionadas en este artículo es amplia y podría explorarse en mayor profundidad. Me encantaría saber si hay interés en conocer más detalles sobre alguna de ellas, ya que sería muy enriquecedor profundizar en aquellas que resulten de mayor utilidad o curiosidad para los lectores.


Referencias y notas