Showing posts with label lean software development series (en). Show all posts
Showing posts with label lean software development series (en). Show all posts

Sunday, October 05, 2025

Lean + XP + Product Thinking – Three Pillars for Sustainable Software Development

When we talk about developing software sustainably, we're not just talking about taking care of the code or going at a reasonable pace. We're referring to the ability to build useful products, with technical quality, in a continuous flow, without burning out the team and without making the system collapse with every change. It's a difficult balance. However, in recent years I've been seeing how certain approaches have helped us time and again to maintain it.

These aren't new or particularly exotic ideas. But when combined well, they can make all the difference. I'm referring to three concrete pillars: Extreme Programming (XP) practices, Lean thinking applied to software development, and a product mindset that drives us to build with purpose and understand the "why" behind every decision.

Curiously, although sometimes presented as different approaches, XP and Lean Software Development have very similar roots and objectives. In fact, many of Lean's principles—such as eliminating waste, optimizing flow, or fostering continuous learning—are deeply present in XP's way of working. This is no coincidence: Kent Beck, creator of XP, was one of the first to apply Lean thinking to software development, even before it became popular under that name. As he himself wrote:

"If you eliminate enough waste, soon you go faster than the people who are just trying to go fast." — Kent Beck, Extreme Programming Explained (2nd ed.), Chapter 19: Toyota Production System

This quote from Kent Beck encapsulates the essence of efficiency through the elimination of the superfluous.

I don't intend to say this is the only valid way to work, nor that every team has to function this way. But I do want to share that, in my experience, when these three pillars are present and balanced, it's much easier to maintain a sustainable pace, adapt to change, and create something that truly adds value. And when one is missing, it usually shows.

This article isn't a recipe, but rather a reflection on what we've been learning as teams while building real products, with long lifecycles, under business pressure and with the need to maintain technical control—a concrete way that has worked for us to do "the right thing, the right way… and without waste."

Doing the right thing, the right way… and without waste

There's a phrase I really like that well summarizes the type of balance we seek: doing the right thing, the right way. This phrase has been attributed to Kent Beck and has also been used by Martin Fowler in some contexts. In our experience, this phrase falls short if we don't add a third dimension: doing it without waste, efficiently and smoothly. Because you can be doing the right thing, doing it well, and still doing it at a cost or speed that makes it unsustainable.



Over the years, we've seen how working this way—doing the right thing, the right way and without waste—requires three pillars:

  • Doing the right thing implies understanding what problem needs to be solved, for whom, and why. And this cannot be delegated outside the technical team. It requires that those who design and develop software also think about product, impact, and business. This is what in many contexts has been called Product Mindset: seeing ourselves as a product team, where each person acts from their discipline, but always with a product perspective.
  • Doing it the right way means building solutions that are maintainable, testable, that give us confidence to evolve without fear and that do so at a sustainable pace, respecting people. This is where Extreme Programming practices come into full play.
  • And doing it without waste leads us to optimize workflow, eliminate everything that doesn't add value, postpone decisions that aren't urgent, and reduce the baseline cost of the system. Again, much of Lean thinking helps us here.

These three dimensions aren't independent. They reinforce each other. When one fails, the others usually suffer. And when we manage to have all three present, even at a basic level, that's when the team starts to function smoothly and with real impact.

The three pillars

Over time, we've been seeing that when a team has these three pillars present—XP, Lean Thinking, and Product Engineering—and keeps them balanced, the result is a working system that not only functions, but endures. It endures the passage of time, strategy changes, pressure peaks, and difficult decisions.

1. XP: evolving without breaking

Extreme Programming practices are what allow us to build software that can be changed. Automated tests, continuous integration, TDD, simple design, frequent refactoring… all of this serves a very simple idea: if we want to evolve, we need very short feedback cycles that allow us to gain confidence quickly.

With XP, quality isn't a separate goal. It's the foundation upon which everything else rests. Being able to deploy every day, run experiments, try new things, reduce the cost of making mistakes… all of that depends on the system not falling apart every time we touch something.

"The whole organization is a quality organization." — Kent Beck, Extreme Programming Explained (2nd ed.), Chapter 19: Toyota Production System

I remember, at Alea, changing the core of a product (fiber router provisioning system) in less than a week, going from working synchronously to asynchronously. We relied on the main tests with business logic and gradually changed all the component's entry points, test by test. Or at The Motion, where we changed in parallel the entire architecture of the component that calculated the state and result of the video batches we generated, so it could scale to what the business needed.

Making these kinds of changes in a system that hadn't used good modern engineering practices (XP/CD) would have been a nightmare, or even completely discarded, opting for patch upon patch until having to declare technical bankruptcy and rebuild the system from scratch.

However, for us, thanks to XP, it was simply normal work: achieving scalability improvements or adapting a component to manufacturer changes. Nothing exceptional.

None of this would be possible without teams that can maintain a constant pace over time, because XP doesn't just seek to build flexible and robust systems, but also to care for the people who develop them.

XP not only drives the product's technical sustainability, but also a sustainable work pace, which includes productive slack to be creative, learn, and innovate. It avoids "death marches" and heroic efforts that exhaust and reduce quality. Kent Beck's 40-hour work week rule reflects a key idea: quality isn't sustained with exhausted teams; excessive hours reduce productivity and increase errors.

2. Lean Thinking: focus on value and efficiency

Lean thinking gives us tools to prioritize, simplify, and eliminate the unnecessary. It reminds us that doing more isn't always better, and that every line of code we write has a maintenance cost. Often, the most valuable thing we can do is build nothing at all.

We apply principles like eliminating waste, postponing decisions until the last responsible moment (defer commitment), measuring flow instead of utilization, or systematically applying YAGNI. This has allowed us to avoid premature complexities and reduce unnecessary work.

In all the teams I've worked with, we've simplified processes: eliminating ceremonies, working in small and solid steps, dispensing with estimates and orienting ourselves to continuous flow. Likewise, we've reused "boring" technology before introducing new tools, and always sought to minimize the baseline cost of each solution, eliminating unused functionalities when possible.

I remember, at Alea, that during the first months of the fiber router provisioning system we stored everything in a couple of text files, without a database. This allowed us to launch quickly and migrate to something more complex only when necessary. Or at Clarity AI, where our operations bot avoided maintaining state by leveraging what the systems it operates (like AWS) already store and dispensed with its own authentication and authorization system, using what Slack, its main interface, already offers.

These approaches have helped us focus on the essential, reduce costs, and maintain the flexibility to adapt when needs really require it.

3. Product Mindset: understanding the problem, not just building the solution

And finally, the pillar that's most often forgotten or mentally outsourced: understanding the problem.
As a product team, we can't limit ourselves to executing tasks from our disciplines; we need to get involved in the impact of what we build, in the user experience, and in the why of each decision.

When the team assumes this mindset, the way of working changes completely. The dividing line between "business" and "technology" disappears, and we start thinking as a whole. It doesn't mean everyone does everything, but we do share responsibility for the final result.

In practice, this implies prioritizing problems before solutions, discarding functionalities that don't add value even if they're already planned, and keeping technical options open until we have real data and feedback. Work is organized in small and functional vertical increments, delivering improvements almost daily to validate hypotheses with users and avoid large deliveries full of uncertainty. Thanks to this, this approach allows adapting critical processes in just a few hours to changes in requirements or context, without compromising stability or user experience.

Not all pillars appear at once

One of the things I've learned over time is that teams don't start from balance. Sometimes you inherit a team with a very solid technical level, but with no connection to the product. Other times you arrive at a team that has good judgment about what to build, but lives in a trench of impossible-to-maintain code. Or the team is so overwhelmed by processes and dependencies that it can't even get to production smoothly.

The first thing, in those cases, isn't to introduce a methodology or specific practice. It's to understand. See which of the pillars is weakest and work on improving it until, at least, it allows you to move forward. If the team can't deploy without suffering, it matters little that they perfectly understand the product. If the team builds fast but what they make is used by no one, the problem is elsewhere.

Our approach has always been to seek a certain baseline balance, even at a very initial level, and from there improve on all three pillars at once. In small steps. Without major revolutions.

The goal isn't to achieve perfection in any of the three, but to prevent any one from failing so badly that the team gets blocked or frustrated. When we manage to have all three reasonably present, improvement feeds back on itself. Increasing quality allows testing more things. Better understanding the product allows reducing unnecessary code. Improving flow means we can learn faster.

When a pillar is missing…

Over time, we've also seen the opposite: what happens when one of the pillars isn't there. Sometimes it seems the team is functioning, but there's something that doesn't quite fit, and eventually the bill always comes due.

  • Teams without autonomy become mere executors, without impact or motivation.
  • Teams without technical practices end up trapped in their own complexity, unable to evolve without breaking things.
  • Teams without focus on value are capable of building fast… fast garbage.

And many times, the problem isn't technical but structural. As Kent Beck aptly points out:

"The problem for software development is that Taylorism implies a social structure of work... and it is bizarrely unsuited to software development." — Kent Beck, Extreme Programming Explained (2nd ed.), Chapter 18: Taylorism and Software

In some contexts you can work to recover balance. But there are also times when the environment itself doesn't allow it. When there's no room for the team to make decisions, not even to improve their own dynamics or tools, the situation becomes very difficult to sustain. In my case, when it hasn't been possible to change that from within, I've preferred to directly change contexts.


Just one way among many

Everything I'm telling here comes from my experience in product companies. Teams that build systems that have to evolve, that have long lives, that are under business pressure and that can't afford to throw everything in the trash every six months.
It's not the only possible context. In environments more oriented to services or consulting, the dynamics can be different. You work with different rhythms, different responsibilities, and different priorities. I don't have direct experience in those contexts, so I won't opine on what would work best there.

I just want to make clear that what I'm proposing is one way, not the way. But I've also seen many others that, without some minimum pillars of technical discipline, focus on real value, and a constant search for efficiency, simply don't work in the medium or long term in product environments that need to evolve. My experience tells me that, while you don't have to follow this to the letter, you also can't expect great results if you dedicate yourself to 'messing up' the code, building without understanding the problem, or generating waste everywhere.

This combination, on the other hand, is the one that has most often withstood the passage of time, changes in direction, pressure, and uncertainty. And it's the one that has made many teams not only function well, but enjoy what they do.

Final reflection

Building sustainable software isn't just a technical matter. It's a balance between doing the right thing, doing it well, and doing it without waste. And for that, we need more than practices or processes. We need a way of working that allows us to think, decide, and build with purpose.

In our case, that has meant relying on three legs: XP, Lean, and Product Engineering. We haven't always had all three at once. Sometimes we've had to strengthen one to be able to advance with the others. But when they're present, when they reinforce each other, the result is a team that can deliver value continuously, adapt, and grow without burning out.

I hope this article helps you reflect on how you work, which legs you have strongest, and which ones you could start to balance.

Monday, August 11, 2025

Optimize the Whole: From Lean principle to real-world practice

Introduction

"Optimize the Whole" is one of the fundamental principles of Lean and Lean Software Development. It means setting aside local improvements to look at the entire system, from the idea to the user—people, processes, and technology—and aligning everything toward a common goal: delivering value to the user quickly and sustainably.

In my experience, many teams believe they are being efficient because no one ever stops working, but in the end, the user waits weeks (or months) to see real value. I’ve learned that true improvement comes from a systemic vision, working together so that the flow of value moves without friction from end to end.

Fragmentation: The Legacy of Taylorism

The paradigm of software development has often—consciously or unconsciously—inherited principles rooted in Taylorism and Fordism, conceiving the creation of digital products as a fragmented "assembly line." Under this view, each phase (analysis, design, development, QA, operations) becomes a functional silo, where teams specialize in specific tasks and focus on optimizing their local efficiency.

However, what in physical goods manufacturing could generate economies of scale for mass production has over time also shown its limits by sacrificing flexibility and the ability to quickly adapt to changes in demand or user needs.

In software, this translates into chronic bottlenecks, costly handoffs, and a fundamental disconnect between those who define needs and those who implement them. This fragmentation breaks the flow of value, fosters the accumulation of "inventory" in the form of work in progress, and hinders rapid adaptation—resulting in features that don’t solve real problems or that take months to reach the user, undermining the promise of agility and continuous value.


Comparison of Taylorism, Fordism, and Lean Thinking


What Happens When We Don’t Optimize the Whole?

Over the years working with different teams, I’ve observed that when we don’t optimize the whole, we fall into the trap of optimizing locally—almost always with good intentions but with unintended consequences. Teams may become very “efficient” in their own internal metrics, but if they are not aligned with user value, the overall flow slows down. Bottlenecks appear, handoffs multiply, and work gets stuck somewhere waiting.

I’ve seen this especially when engineering or development is seen as a feature factory that “executes” or “implements” what others decide. The team just implements, without understanding the problem, the priority, or the real impact on the user—and without contributing their technical knowledge to decisions. The result: solutions disconnected from real needs, slow feedback, and features that don’t solve the problem.

In my experience, the root cause is usually a functional and fragmented organizational structure inherited from “assembly line” thinking. But I’ve learned that software doesn’t behave like a linear factory. Software needs product teams with end-to-end responsibility, without silos (backend, frontend, QA, ops, design, etc.), and with real autonomy to make decisions and operate what they build.

I’ve found that this is not just a matter of motivation: it’s the only way to optimize the total flow and deliver real value to the user quickly and sustainably.


Bottlenecks and the Theory of Constraints

The Theory of Constraints (TOC) reminds us that in any system there is always at least one constraint that determines the maximum delivery capacity. Identifying and managing that bottleneck is essential to improving the overall flow.




For example, at ClarityAI, in the beginning, features were released to production but could remain behind a feature toggle for weeks, waiting for product to decide when to expose them to users. Even though they were technically ready, value wasn’t flowing.

Another example: when workflows were separated by functions (frontend, backend, data engineering), any change could take weeks because each group optimized its own flow or backlog instead of thinking about the overall flow from the user’s perspective.

(Fortunately, these specific problems at Clarity AI were solved long ago, but they serve as an example of what can happen when we don’t optimize the whole.)

In my experience working with different teams, I’ve learned that speeding everything up indiscriminately only makes the backlog pile up and creates frustration. 
A necessary condition to identify real constraints is that all work is visible—not only development tasks, but also testing, operations, support, automation, documentation, analysis, coordination, etc. If a significant part of the team’s effort is hidden (because it’s not recorded, visualized, or considered “technical work”), it’s very easy for the real constraints to go unnoticed. As Dominica DeGrandis points out in Making Work Visible, what you can’t see, you can’t manage or improve. Making all work visible is a key step for informed decision-making, reducing work in progress, and better focusing continuous improvement efforts.

The key steps are:
  • Identify the constraint. Make it visible and prioritize it.
  • Exploit the constraint. Keep it focused, avoid distractions, and ensure it’s always working on the highest-value items.
  • Subordinate the rest of the system. Adjust rhythms and priorities so as not to overload the constraint.
  • Elevate the constraint. Improve its capacity through automation, training, or process redesign.
  • Review and repeat. There will always be a new constraint after each improvement.

Over the years, I’ve noticed that the more separate stages with queues there are between the idea and the value delivered to the user, the greater the chances of bottlenecks forming. Furthermore, if each stage belongs to a different group (organizationally speaking) that may even have its own agenda, there is likely little interest in optimizing value for the user. In these cases, each group may focus solely on improving its part of the process—or on avoiding being perceived as the bottleneck.



End-to-End Teams and Real Optimization

In every team I’ve built, I’ve insisted that they be product teams with end-to-end responsibility, without silos for QA, operations, security, or design. The reason is simple: if the team doesn’t control or understand the entire flow, it can’t optimize the whole, and it also risks not taking full responsibility for the complete product.

When the same team is in charge of conceiving, building, deploying, and operating, it eliminates the waste that arises in each handoff and accelerates learning. Every member understands the impact of their work on the end user and the value actually being delivered. End-to-end responsibility not only improves technical quality but also strengthens the focus on user value, avoiding unnecessary investments in local optimizations that don’t contribute to the overall goal.

In this work environment, more generalist profiles—often referred to as "T" (deep in one area and broad in others) or "M" (with several deep specializations and wide versatility)—are especially valued. These professionals, being able to contribute in multiple phases of development and having a more holistic view, are key in contexts where flow efficiency is a priority.

While pure specialists, also known as "I" profiles, are still needed in very specific domains, their role changes: they become enablers and educators, helping to scale their knowledge and train the rest of the team in various facets of the product.

In addition, keeping options open, working in short cycles, and making small deliveries allows for better adaptation to what the business and data reveal. Simplicity and vertical slicing reduce risk and facilitate rapid learning, freeing the team from the mental load of large, fixed plans and fostering autonomy.

This way of working is unfeasible when the product must pass through different silos before reaching the end user.

End-to-End Teams in Practice: Real Experiences

When I formed the team at Alea Soluciones, from the very beginning I pushed for us to take on all possible tasks and functions. I remember both the CEO and the CTO offering me the option to take on fewer responsibilities “to have less pressure and less work.” But I refused that proposal: I knew that if we only kept part of the scope, we would lose the global vision and the ability to optimize the entire system.

By taking on all areas—development, support, product ideas, operations—we always maintained a holistic view of the product. This allowed us to identify at any given moment where the real bottleneck was and act where we could have the greatest impact, without depending on other teams or functional silos. This meant we could decide whether, at a given moment, it made more sense to focus on support, to speed up key developments, or to think about new features—always optimizing the total flow of value to the user.

At Nextail and Clarity AI, together with others, I worked to evolve teams toward an end-to-end model, avoiding QA, operations, or product silos. In these cases, we applied ideas from Team Topologies to transform the structure: we moved from a function-based organization (infrastructure/operations, frontend, backend, data engineering, product) to a matrix of autonomous product teams.

The goal was always the same: to shorten the lead time of any change, from the idea to when the user can actually use it. With autonomous, end-to-end responsible teams, we could deliver value faster, learn continuously, and improve the product in very short cycles.

In all these contexts, I’ve found that the end-to-end approach has not only been a technical or organizational improvement—it has been the key to maintaining a user-centered mindset, reducing waste, and optimizing the whole, every day.


How Optimizing the Whole Helps Eliminate Waste

One of the fundamental principles of Lean is eliminating waste. When we focus only on optimizing local parts, it’s very easy to accumulate work that doesn’t add value, create unnecessary features, or generate invisible delays. In contrast, by optimizing the whole and looking at the complete system, we naturally reduce multiple types of waste.

In my work with different teams, I’ve observed that when we don’t think about the global flow and focus on optimizing “my part,” incentives appear to produce more features even if they don’t provide real value to the user. The priority becomes “staying busy” and completing tasks, rather than questioning whether they are necessary.

I’ve seen how, without early feedback and working in long cycles, teams advance without validating and end up building features nobody asked for or that don’t solve any important problem.

Moreover, when decisions are fragmented (product defines large packages, engineering executes without questioning, and QA validates afterward), the vision of impact is lost, and the backlog tends to swell with features that remain blocked behind a feature toggle or are never released at all.

By optimizing the whole and aligning each step with the complete flow of value to the user, each feature is reviewed with key questions:
  • Does it solve a real, high-priority problem?
  • Can we release it in a small version to learn quickly?
  • How will we know if it truly adds value?
In this way, we build only what’s necessary, learn early, and avoid turning effort into waste.
  • Avoid unnecessary features (overproduction): By prioritizing real user value and working in small deliveries, hypotheses are validated quickly. This avoids building large features that nobody uses or that the business decides not to launch.
  • Reduce waits and blockages (waiting times): By eliminating silos and working end-to-end, work doesn’t get stuck waiting for another team or function to pick it up. This speeds up flow and eliminates idle time.
  • Less rework and late fixes: Delivering in short cycles and validating early allows problems to be detected quickly and corrected at low cost. Otherwise, small local decisions can lead to large refactorings later.
  • Avoid useless local optimizations (unnecessary movement): Optimizing your own “department” or backlog can create a false sense of efficiency, but doesn’t generate value if it doesn’t move the complete flow forward. Looking at the global system avoids this kind of waste.
  • Reduce hidden inventory: Limiting work in progress and prioritizing constant flow minimizes the inventory of half-built or unreleased features, which consume energy and create confusion.
  • Lower opportunity waste: By having a clear view of the whole and being aligned with the business, we avoid investing in the wrong directions and respond quickly to new opportunities. This reduces the risk of missing the right moment to impact the user.

In my experience, when we optimize the complete system, every decision is made with the flow of value to the end user in mind. This way, every line of code, every validation, and every deployment helps reduce waste and maximize impact.

How to Optimize the Whole: Lessons from Practice

  • End-to-end vision: From the business problem to the running software operated by the team itself. Without fragmenting or handing responsibility over to “others.”
  • Flow over utilization: We stop measuring how much each person works and start measuring how much value flows to the user.
  • Enabling practices: Pair programming, TDD, CI/CD, limiting WIP, and visualizing flow… These are key tools to keep the system healthy, adaptable, and ready to learn quickly.
  • Small deliveries and immediate feedback: Every delivery is a learning opportunity. Working in vertical slices helps prioritize what truly matters, encourages simplicity, and reduces the fear of making mistakes.
  • Collaboration and psychological safety: Transparency, trust, and shared responsibility. Encouraging questioning, proposing improvements, and experimenting without fear.
  • Conscious empowerment: Teams take on more decisions as they demonstrate capability, always aligned with the business and focused on real impact.

Why "Optimize the Whole" Matters

Optimizing the whole is crucial because it addresses a fundamental contradiction in many organizations: the pursuit of resource efficiency versus flow efficiency. Traditionally, incentives have pushed for each person, team, or stage of a process to be as “busy” as possible, aiming to maximize individual resource utilization. However, this obsession with local resource efficiency (making sure no one is idle) is often catastrophic for flow efficiency—that is, the speed and smoothness with which value moves from the initial idea to the hands of the end user.

When each component of the system focuses on its own optimization, bottlenecks, waiting queues, and handoffs arise, breaking the continuity of flow. Paradoxically, when “everyone is very busy,” it’s often a clear sign that there is a serious problem with the flow of value to the user. Work piles up, deliveries are delayed, and the organization is investing significant effort in activities that don’t quickly translate into real value.

By optimizing the whole, we achieve:
  • Avoiding invisible bottlenecks that block value to the user, by having a global view of the system.
  • Drastically reducing waste: unused features, endless waits, unnecessary rework, and the false sense of productivity.
  • Enabling faster learning and the ability to build only what truly matters, as the feedback cycle is accelerated.
The true goal is not for everyone to look busy, but for the flow of value to the user to be constant, predictable, and sustainable.

Maximum busyness, minimal progress. Optimizing the whole means avoiding this.


Conclusion: The Transformative Impact

After years of experimenting with teams that truly optimize the whole, I can say you never want to go back. Teams become more resilient, grow faster, and find deeper meaning in their work.
I’ve learned that optimizing the whole is not just a principle—it’s a way of working that transforms teams and individuals, and above all, maximizes real impact for the user.
Are you ready to start optimizing the whole in your team? The first step is to dare to look at the entire system and stop obsessing over local metrics.

Related Articles and Other References



Sunday, June 08, 2025

Lean Software Development: Overcoming resistance and creating conditions for quality

Fifth article on quality in Lean Software Development. In previous posts, we talked about how to build with quality through mistakes, technical design, collaboration, and visibility. Now we address a key topic: why many organizations still don't work this way, and what we can do to change that.

In the world of software development, there is a persistent myth: that quality and speed are opposing forces, and that one must be sacrificed to obtain the other. However, the reality, as demonstrated by the DORA reports and the experience of high-performing teams, is that quality is the most direct and sustainable path to the highest possible speed.

There is a fundamental paradox: the more we obsess over immediate speed at the expense of quality, the slower we become. Teams that accumulate technical debt, unresolved bugs, or hard-to-maintain code make each new feature exponentially more expensive. What seemed like a "pragmatic" decision becomes a burden that slows down the entire system.

True pragmatism aligns with Lean principles: postponing decisions until sufficient information is available, applying YAGNI (You Aren't Gonna Need It), keeping design simple, and constantly iterating to have the simplest version of the system that meets current needs. That is being truly pragmatic.

It’s important to understand that in the age we live in—of continuous change and software adaptation—when we talk about the “medium term” we actually mean a few weeks. We are not talking about months or years to see the benefits of quality. The effects of working with quality are noticed very quickly, and that supposed short-term trade-off only makes sense for throwaway software.

In Lean thinking, the way to have more impact is to minimize waste, with lack of quality being one of the main wastes in software. So the winning combination in software development is to maximize impact, minimize the amount of software generated, and do it with quality in the process. The approach is not to do things worse or faster, but to be smart and disciplined to achieve more impact with less, and with quality. This is the true way to go fast, achieve maximum impact, and be a sustainable high-performing team.

Common reasons for not working this way (frequent resistances)

Pressure for short-term speed

"We don't have time to write tests," "it has to be delivered now." This is the classic one. However, as we've already seen, well-integrated tests in the development flow allow faster progress at lower cost in the medium term.

In environments where immediate output is valued, investing in quality at the start may seem slower, but it prevents a greater slowdown even in the short term. We're not talking about benefits that take months to arrive—in a matter of weeks you can notice the difference when technical debt doesn't accumulate and waste is kept under control. Lean practices are often misinterpreted as an initial brake, but their true value becomes clear when the system starts to fail and the real cost of not having invested in quality becomes evident.

Misalignment between business and technology

If the business only measures visible deliveries (features) and does not understand the value of refactoring, tests, or simple design, perverse incentives arise that push to avoid everything that isn't “visible.”

Here it's necessary to align incentives, showing with data that investing in quality generates higher returns. Moreover, the waste of building unnecessary or misunderstood features skyrockets when this alignment is missing. Let’s not kid ourselves: the fundamental waste in product software development is implementing what’s not needed, and maintaining it for the lifetime of the product. We already know that the basal cost doesn't apply only to features that are used.

Lack of training or experience

For many people, this way of working is new. They haven’t seen environments with trunk-based development, TDD, or real automation. If they haven’t experienced the benefits, it’s normal for them to distrust or underestimate them. Some of these practices require a significant mindset shift and specific technical skills that take time to develop. Investment in training and mentoring is key to overcoming this initial barrier and building the confidence needed in these methods.

Fear of change

Fear of the unknown is a natural human response. Many teams feel comfortable with their current processes, even if they are inefficient. Changing established routines generates uncertainty and resistance. This fear can manifest as skepticism ("this won’t work here") or even passive sabotage. The transition requires effective leadership, clear communication of expected benefits, and the creation of a safe environment where experimenting with new methods is valued and supported.

Lack of structural quality

Some teams want to work with quality, but they already have a system full of debt, without tests, without confidence. Changing it requires an investment that the organization is often unwilling to make. Here improvement must be incremental, with visible wins: reducing deployment time by 10%, fixing the 3 most critical bugs, etc. Establishing “clean zones” in the code and gradually expanding them can be an effective strategy to regain ground without needing a full rewrite.

Organizational inertia and rigid structures

If teams lack autonomy, if decisions are made top-down without technical feedback, or if release, QA, or security processes are outside the team, it’s hard to apply jidoka or react quickly to problems.

The system inhibits quality, and the waste of time and resources increases exponentially while problems persist.

Culture of blame and punishment

If the organization doesn’t tolerate mistakes, if it looks for culprits instead of causes, or if incidents generate fear instead of learning, errors are hidden instead of made visible. And without visibility, there is no improvement, nor can waste be reduced.

Fear paralyzes innovation, delays problem identification, and hides waste at all levels.


Even if it sounds exaggerated, many organizations face this dilemma when they realize that their way of working is no longer sustainable. Improving requires effort, but not improving has inevitable consequences.

Improve or die meme


Create the conditions to build with quality

Working with quality, as we've seen throughout this series, does not depend only on tools or individual talent. It is a direct consequence of the environment (system) we build. Quality does not arise spontaneously: it needs space, alignment, and a culture that values it.

From Lean Software Development, we start from one premise: people want to do good work. But if incentives, habits, and culture don’t support that, even teams with the best intentions will fall into practices that sacrifice quality in favor of urgency, volume, or the appearance of productivity. And this inevitably leads to generating a lot of waste.

“A bad system will beat a good person every time.”
—W. Edwards Deming

As product development leaders, we have a clear responsibility: create the right conditions so that quality is not only possible, but inevitable. This involves intervening in three key dimensions: incentives, work systems, and culture.



Quality doesn’t improve by acting only on the visible. As Donella Meadows well summarized, there are many levels from which to intervene in a system. The deeper the intervention point (mindset, culture, structure), the greater its impact. This framework reminds us that if we want sustainable quality, it's not enough to tweak metrics: we have to transform how we think and how we work.

Places to Intervene in a System by Donella Meadows
Places to Intervene in a System by Donella Meadows

Redefine success

Instead of celebrating only the number of features delivered or apparent speed, let's focus on real impact, system sustainability, and the team’s ability to adapt confidently.

Quality is not about delivering more, but about delivering better: with less risk, maintaining a sustainable pace, continuously learning, and better anticipating changes.

Make space for learning and continuous improvement

One of the most common mistakes is to think that Kaizen time is dispensable. But setting aside time to refactor, automate, review processes, or simplify is not a luxury: it’s part of the team’s job and an investment in the system’s health.

To make it possible, we need to introduce intentional slack: planned space to observe, learn, and improve. Without that margin, all the time is spent delivering, and there’s no energy or focus left for Kaizen.

Continuous improvement requires time, attention, and a sustainable rhythm. It's what allows consistent waste reduction.

Take care of team culture

Psychological safety is key. If there is fear of making mistakes or pointing out problems, there will be no jidoka, kaizen, or visibility. Only in an environment where it’s safe to question, explore, and learn without punishment can we detect errors in time and improve together, reducing the waste they generate.

We must also avoid encouraging heroic work: when good outcomes depend solely on someone’s extraordinary effort, it's a sign that the system is failing.

Instead of heroes, we need teams that work sustainably, with processes that ensure continuous and predictable quality. Heroic work is often a chronic waste generator.

Moreover, real autonomy must be granted: choosing technologies, designing testing processes, having a voice in planning, etc. A team with no control over its technical environment, workflow, or how it validates what it builds will hardly be able to guarantee quality.

Autonomy, combined with shared responsibility, is one of the strongest pillars of quality in Lean.

Finally, incentives must be aligned with quality. Recognize and make visible the work that keeps everything flowing: not just new features, but also when technical debt is reduced, the testing process is improved, a production incident is prevented, or a critical system component is simplified.

All of that is also delivered value. And it’s often the most enduring.

How to make quality inevitable: leadership in practice

Making quality possible is not about demanding more effort from teams. It's about changing the system so that working with quality becomes the most natural, simplest, and fastest path. Over the years, I’ve tried to systematize this approach with very concrete decisions. Here are some of them:

  • Reserve space for learning. Actively decide what portion of time is invested in learning. Sometimes it’s training, other times it’s simply asking: “What have you learned? What can you share?”
  • Turn mistakes into collective learning. Introduce blameless postmortems. Lead the first ones, define the process, normalize that errors are not blame, but opportunities for improvement.
  • Lead by example. Apply TDD, evolutionary design, pairing. Be the first to document and act on incidents. Don’t demand what you don’t practice.
  • Introduce Technical Coaching. Learn alongside those who already master practices like TDD or Pair Programming. If possible, bring in experts with real experience.
  • Change the hiring process. Evaluate how people work, not just what they know. Introduce TDD, pairing, collaborative design as part of the process.
  • Reward and make structural improvements visible. Explicitly value what improves quality: debt reduction, better test strategies, simplifications, etc.

This type of leadership, which seeks to change the system to make quality inevitable, is not an isolated intuition. Studies such as those from the DORA report show that transformational leadership, together with Lean practices, has a clear impact on team performance, well-being, and business results.

Transformational Leadership Impact Model by DORA
Transformational Leadership Impact Model by DORA / Accelerate


Lead to make quality inevitable

Building with quality is not just a matter of technical practices: it is, above all, a matter of leadership. Our role as leaders is not to demand quality as if it were an optional extra, but to understand that it is the foundation for sustainable speed, for reducing waste, and for maximizing real impact.

Quality is not a goal or an option: it is the operating system upon which everything else relies. If that system fails, any attempt to move fast leads directly to collapse.

Our job as leaders is to create the conditions where quality does not depend on individual will, but becomes the easiest, fastest, and most natural path. Where building with quality is not a heroic act, but the inevitable one.

Saturday, May 24, 2025

Lean Software Development: Quality through Collaboration and Visibility

Fourth part of the series on quality in Lean Software Development. In the previous post, we discussed how internal and technical quality is key to sustaining external quality and accelerating development.

Quality through collaboration and shared design

An essential part of quality, often underestimated, doesn’t lie in the code or the tools, but in how we work together. In Lean Software Development, errors are not just seen as technical failures, but also as failures in understanding. Many of the defects that reach production aren’t due to poorly written code, but because the code doesn’t solve the right problem, or doesn’t do so in the right way.

That’s why one of the key mechanisms to build with quality is close and continuous collaboration among all the people involved—those who design, develop, test, or speak with users. The earlier we share an understanding of the problem and align expectations, the fewer errors will be introduced into the system. Once again, quality from the start.

Practices like pair programming, ensemble work, using concrete examples in conversations with the business, or co-designing solutions are mechanisms that allow us to detect errors—technical and conceptual—as soon as they appear. In doing so, they enable early intervention aligned with the spirit of jidoka. And we do this naturally, because there are many eyes on the problem, many opportunities to surface misunderstandings.

This collaborative approach also reinforces kaizen, as it facilitates continuous improvement. Ideas are challenged, explained, and refined. The system evolves more coherently because it doesn’t rely on isolated individual decisions, but on shared and distributed knowledge.

Furthermore, collaboration reduces waste: we build what is actually needed, avoid incorrect assumptions, and minimize rework. Solutions tend to be simpler because they’ve been discussed and refined from different perspectives.

Ultimately, if we understand building with quality as avoiding defects, reducing waste, and maintaining a healthy system we can evolve with confidence, then collaboration is not optional. It is one of the most powerful ways to prevent errors before they become code.

The value of making quality (or its absence) visible

One of Lean’s fundamental principles is to make problems visible. If we can’t see a problem, we can’t improve it. And if quality isn’t visible to the team, to decision-makers, or to those supporting the product, then it’s unlikely to become a priority.

That’s why, in Lean Software Development, it’s essential to make the real state of quality visible at all times. Not only through technical metrics, but also with mechanisms that make it obvious when something is failing, when we’re accumulating waste, or when we’re risking system stability.

This connects directly to jidoka: any signal of a problem, no matter how small, should stop the flow or at least get our attention. Whether it's a failing test, a monitoring alert, a drop in coverage, or an increase in average bug resolution time—everything should turn on a warning light. The goal is that nothing goes unnoticed so we can act in time.

It’s also a constant reinforcement of kaizen: what isn't seen can’t be improved. Making quality—internal and external—visible allows us to make informed decisions about where to focus our improvement efforts. If we notice production defects always come from a certain part of the system, we probably need to strengthen our testing there. If the pace of change slows down, maybe complexity is growing out of control.

There are many ways to make quality visible: from continuous integration dashboards to production alerts, from physical boards with open bugs to regular incident review meetings. The important thing isn’t the tool, but the habit of looking honestly at the state of the system and the process.

Making quality (or its absence) visible also has a cultural impact: it reinforces shared responsibility. If everyone sees there’s a quality issue, it’s easier for everyone to participate in solving it. The invisibility of decay is eliminated, resignation is avoided, and an environment is fostered where problems are tackled as soon as they appear.

Because ultimately, building with quality also means building with transparency.

Quality as an organizational habit

Building with quality isn’t a phase of the process, a task assigned to a specific person, or something you “add at the end.” It’s a way of working, a habit cultivated daily and embedded in everything we do: how we design, how we write code, how we collaborate, how we solve problems, and how we learn.

In Lean Software Development, quality is non-negotiable because it is the foundation of everything else. Without quality, flow breaks down, learning slows, the cost of change rises, and trust disappears. That’s why quality isn’t pursued for technical idealism, but because it’s the most effective way to deliver value continuously and sustainably.

The principles of jidoka, poka-yoke, and kaizen are present in every practice we've mentioned: in automated tests that stop the flow upon failure, in processes that prevent human errors, in the constant improvement of our tools and processes, and in how we treat incidents as learning opportunities.

But none of this works unless it becomes part of the team’s culture. Quality doesn’t emerge by chance or good intentions—it arises when there are concrete practices that support it, when there are shared agreements on how to work, and when the environment reinforces these behaviors over and over. In other words, when there are habits.

And like any habit, it must be trained. It starts with small actions: writing a test before fixing a bug, stopping development to investigate an error, reviewing the design with someone else before implementing. Over time, these actions become the natural way of working. The team gains confidence, the system remains healthy, and problems are addressed quickly and calmly.

In the next and final post, we’ll explore why many organizations still don’t work with quality—even knowing its benefits. We’ll look at the most common resistances and how we can create an environment where quality doesn’t depend on heroic efforts, but becomes a natural consequence of the system.

Friday, May 16, 2025

Lean Software Development: Quality as the Foundation of Sustainable Development

Third part of the series on quality in Lean Software Development. After exploring how to detect errors early and learn from them, in this entry we explore how technical and internal quality is key to sustaining external quality, why less is more, and how to create a culture where working with quality is not the exception but the norm. In this entry, we explore how this foundational quality is not a luxury but the essential hygiene of a well-crafted software product.

Quality as a Development Accelerator

One of the most widespread beliefs, especially in organizations that haven’t yet adopted Lean approaches, is that working with quality slows down development. It’s assumed that writing tests, automating validations, or refactoring consumes time that could be spent "delivering faster." However, from the perspective of Lean Software Development, this view is not only wrong—it perpetuates waste.

In reality, well-understood quality is an accelerator. When the system is healthy—with reliable automated tests, simple design, and robust processes—every step we take has less friction. The team’s confidence in its ability to change the system grows, feedback is faster, and the cost of change drops dramatically. In other words, we go faster not despite quality, but thanks to it.

This is fully aligned with the Lean principles we've been explaining in this series (poka-yoke, jidoka, kaizen).

Moreover, when the team trusts its system—because it knows that errors are detected in time, that the design allows for easy evolution, and that experiments can be conducted without breaking anything—it dares to innovate, try new ideas, and quickly adapt to what it learns from the user. In short, it enables the continuous delivery of value.

In my experience, teams that invest in quality from the beginning and incorporate it as part of their way of working progress much more steadily, quickly, and with less emotional cost. They don’t have to constantly stop to "fix the system" because they never let it deteriorate. And that’s possible because they understand that quality isn’t inspected at the end—it’s built into every step.

Internal Quality as the Foundation of External Quality

In Lean Software Development, external quality—the one users or clients directly perceive—is a priority. However, to sustain that quality over time, solid internal quality is essential: a well-designed, understandable system that can be maintained and evolved without fear.

Many times, the defects visible to users originate from invisible problems within the system: tightly coupled code, unreliable tests, contextless technical decisions, or fragile processes. These issues not only cause errors, they slow down the team, hinder adaptation to change, and raise the cost of delivering value. They are a silent but very real form of waste.

Lean encourages us to see these structural problems as improvement opportunities (kaizen) and to address them systematically. It's not about “beautifying the code” or following arbitrary rules, but about building a solid technical foundation that reduces everyday friction and enables rapid, confident progress.

We also apply jidoka in this context: when a flaky test, opaque dependency, or hard-to-deploy system blocks progress, we flag it as a system problem, not an individual weakness. We stop, analyze, and improve the technical infrastructure to prevent recurrence. Every small change counts. For example, if a deployment fails repeatedly, we don’t just try again—we investigate the root cause and automate a solution, like a script that checks database availability before deployment.

Moreover, poka-yoke principles also apply to internal quality. Using strong typing, simple design patterns, proper encapsulation, and tools that make the right thing easy to do without constant effort are ways to prevent technical errors and ease system evolution. The easier it is to do the right thing, the less likely it is to introduce debt or accidental errors. For example, using a code linter to catch syntax errors before they reach production, or configuring version control to prevent unreviewed code from being committed.

In summary, internal quality isn’t an end in itself—it’s a means to sustainably ensure external quality. When the system is easy to understand, test, and change, the team can focus on delivering value, learning faster, and better adapting to customer needs. That structural simplicity is what allows us to build with quality—and move fast without breaking things.

Graph showing internal quality as the foundation upon which external quality is built.

Quality is Not Complexity or Sophistication

A common confusion in software engineering is associating quality with technical sophistication. “Beautiful” code, elegant solutions, or complex designs anticipating future needs are often praised. However, from a Lean Software Development perspective, this view is fundamentally flawed. In reality, quality is not a luxury—it is a basic necessity, the fundamental hygiene of a well-built software product.

Lean doesn’t reward unnecessary complexity or overdesign. Quite the opposite: it promotes deliberate simplicity as a way to reduce waste, ease system evolution, and ensure reliability. Quality in this context is not measured by how many design patterns we apply or how "intellectually interesting" the design is, but by how well it solves the current problem with the least effort and risk. It’s like cleanliness in a home: not an ornament, but the minimum necessary for healthy living.

Every line of code we don’t need right now is a potential source of error. It not only adds maintenance cost, but also complicates understanding, slows evolution, and can mislead decisions.

"The best code is no code at all" — Ward Cunningham

From Lean’s perspective, anticipating features that don’t yet exist or designing systems beyond current needs is a form of waste. It’s also a kaizen failure, as it prevents learning and iteration step by step. And it breaks poka-yoke by introducing optional, unvalidated paths not covered by tests. Instead of preventing errors, we’re planting them.

Quality in Lean is built with clear, tested, understandable code, limited to what is strictly necessary. We care about design not to make it more complex, but simpler, safer, and easier to evolve. We rely on tests, continuous feedback, evolutionary design, and constant refactoring to keep the system healthy without falling into the trap of planning the future from the present.

So no: technical beauty or overdesign is not quality. Often, it’s its enemy. Building with quality in Lean is, above all, having the humility to do just enough, do it well, and prepare to improve based on what we learn tomorrow.

Conclusions: Quality as Basic Hygiene

In summary, quality in Lean Software Development is not an optional feature or a sophisticated extra. It is the base, the foundation, the essential hygiene of a sustainable and valuable software product. It’s not about seeking complexity or elegance for their own sake, but about building a system that is clear, simple, tested, and easy to maintain.

Quality is like the air we breathe: we don’t always notice it, but its absence quickly suffocates us. Software without quality is software doomed to fail—full of bugs, hard to change, and expensive to maintain. That’s why investing in quality from the start, and seeing it as an essential practice rather than a luxury, is the best way to ensure long-term success.

Quality is not a bonus—it’s the bare minimum. And simplicity is its best ally.

In the fourth part, we’ll see how collaboration and visibility are also essential to maintain this hygiene and build sustainable quality software.


Friday, April 18, 2025

Lean Software Development: Detect errors before they hurt

Second part of the series on how to build with quality from Lean Software Development. If you haven't yet read the first part of this series, where we explain the fundamentals of Lean Software Development, you can find it here: https://www.eferro.net/2025/04/lean-software-development-building-with.html. After understanding why quality is not just the final result, in this installment we focus on how we detect errors as early as possible, stop the flow when they appear, and learn from them to improve the system.

To avoid confusion, we will use "error" to refer to any deviation from the expected result, and "defect" for errors that impact the customer in production.

Detecting errors as early as possible  

In more traditional approaches, defects are often prioritized based on their criticality, which sometimes determines whether they are fixed and within what timeframe. However, in Lean Software Development, which considers quality a fundamental part of the product and focuses on the continuous improvement of processes and systems, it is more common to classify errors (potential defects) based on where and when they were identified within the process.

Having this information allows us to identify the stages where errors are most common, helping us focus our improvement efforts on detecting them as early as possible (shift left), reducing the likelihood of them becoming defects.

In my experience, it is very useful to classify error detection depending on the stage where they are identified. I usually use the following classification:

  1. Local machine (pair or ensemble work)
    1. Development cycle (including TDD cycle, continuous style verification (linting), type checks, errors, etc.)
    2. Pre-commit
    3. Pre-push
  2. CI Pipeline:
    1. Checks
    2. Tests (classified from fastest to slowest)
    3. Deployment (including smoke tests and validations during rollout)
  3. Production environment
    1. Pre-release (deployed but not activated for the client)
    2. Client activation

When a feature is already activated for the client, it is also useful to classify errors or defects based on who or what detected them:

  1. Automatic system, before it impacts the client (Monitoring)
  2. Internal user, such as a team member running tests or someone from another department
  3. Final user, reporting the defect to support

Regardless of the stage or who detected it, the main goal is always the same: detect (and fix) the error as early as possible, ideally before it is identified by an end user in production (when it is already considered a defect).

Lean Software Development accepts that we make mistakes continuously and understands that the cost (waste generated) increases the later the error is detected and fixed.

To illustrate how this progressive error detection is structured and visualized, I will show two real examples of pipelines we use. In both cases, the various steps (checks, tests, publishing, deployment, production validations, rollback, etc.) are organized to easily detect any error as soon as possible, stop the process, and fix it. This visualization not only helps structure the workflow better but also ensures that the entire team clearly understands at what stage each type of error can appear.

In this first pipeline, each component (webapp, API, event processor…) has its own checks, unit, integration, and acceptance tests, as well as differentiated publishing and deployment processes for different environments (dev and mgmt). Additionally, end-to-end tests are automated in production before activating changes, and a rollback logic is included if something fails. This structure reinforces the principle of automatically stopping the flow when errors occur and facilitates traceability at each stage.

In this second example, more focused on structural validations and specific testing of certain technologies (argo workflows in this case), additional phases such as static checks, cleanup tasks before publishing the image to ECR, and integration tests with different configurations are highlighted. This type of pipeline shows how even auxiliary tasks like configuration validation or environment cleanup are an integral part of an approach that seeks to detect errors before they hurt.

Stop and fix policy

Jidoka, also known as "autonomation" or "automation with a human touch," is a key principle of Lean Software Development. It’s not just about automating processes but doing so in a way that they automatically stop when a problem is detected, allowing teams to investigate and fix the root cause before continuing. Applying the Jidoka concept, teams working with Lean Software Development design development processes that make it very easy to detect errors—either automatically in most cases, or manually thanks to a development process that facilitates identifying those errors.

For continuous quality improvement to work, we not only need to detect those errors but it is crucial to stop immediately (See https://en.wikipedia.org/wiki/Andon_(manufacturing)) and have a policy that forces us to prioritize their immediate resolution. This way of working may seem too radical at first and might give the impression that it slows the team down. However, my experience is quite the opposite. If you adopt a working approach where, upon detecting an error, you analyze it, learn from it, and fix it at the root—by, for example, adding a test to prevent it from happening again—you soon achieve a process that resolves errors as early as possible. This eliminates a lot of rework and potential problems for the end customer while generating great confidence within the team to move quickly, take risks, and experiment.

In fact, I believe it’s the best way to move fast sustainably, and the DORA Reports studies and the book Accelerate confirm that the best way to be fast is to build with quality—at least when it comes to product development.

In my case, this application of the Jidoka approach is reflected in:

  • Automatic tests that, upon detecting a failure, temporarily interrupt the development flow to prevent the error from propagating.
  • Git hooks (pre-commit, pre-push, etc.) that interrupt the flow if an attempt is made to push code with errors.
  • Working with trunk-based development, a strategy where all developers integrate their changes into a single main branch. In this setup, we run all validations on the main branch. When a test fails in continuous integration, we stop to fix it immediately. This is crucial in trunk-based development because any failure blocks the ability to integrate new changes, ensuring the main branch is always stable and ready for deployment. This discipline is fundamental to maintaining quality and avoiding error accumulation, allowing us to move fast with confidence.
  • Automatic prioritization in the workflow for resolving bugs detected in production, following an incident management process with postmortems for production incidents. This automatic prioritization is based on the severity of the error and its impact on the customer, determining which bugs are addressed first. (https://www.eferro.net/2024/12/using-blameless-incident-management-to.html).
  • Pair programming or ensemble programming, where multiple people work together on the same task, allowing misunderstandings or potential errors to be detected from the start. This intense collaboration acts as a continuous review that prevents many errors, both in understanding the problem and in implementing the solution.

Dealing with defects and learning from them

Accepting that we are going to make mistakes—and that some will become defects—is a fundamental part of the Lean approach. Instead of denying or hiding it, we embrace it as something natural. We work in complex environments, with multiple dependencies, constant uncertainty, and, moreover, we are human. We are fallible by definition.

That does not mean we don't try to avoid errors or defects. On the contrary, we put a lot of effort into preventing them with techniques like poka-yoke, automated testing, pair programming, evolutionary design, and many other practices that are part of our daily work. Even so, we know they will happen. And since we know it, we prepare to minimize their impact and recover as quickly as possible.

This shift in mindset is key: we move from an obsession with avoiding mistakes at all costs to a more robust and sustainable strategy based on fast recovery (resilience) and learning capability. Because when a defect reaches production, the first objective is to restore service as quickly as possible. And immediately after, to learn.

Over the past years, in several teams I've worked with, we've refined and applied a blameless incident management approach. The idea is simple: when an incident occurs, we don't look for someone to blame. We focus on understanding what happened, how the system contributed to the error, and what we can do to prevent it from happening again or reduce its impact next time.

This type of approach, simple as it may seem, has had a huge impact on team culture. It brings psychological safety, builds trust, promotes transparency, and encourages people to make problems visible without fear. At TheMotion, Nextail, and ClarityAI, we used it not only to manage incidents but also as a major lever to evolve the culture toward one that is more collaborative, learning-oriented, and focused on continuous improvement.

For example, in a recent incident where a service failed, we applied the 5 Whys technique and discovered that the initial problem (a configuration error) had triggered a cascade of events due to the lack of error handling in another service. This led us to add more robust integration tests and improve the resilience of the second service.

Our blameless incident management process relies on several principles:

  • Stay calm. Don’t panic. We even value this ability during interviews as a sign of professional maturity.
  • Assign an Incident Commander to coordinate the response and ensure no one is left alone firefighting.
  • Restore the service as soon as possible. Sometimes this means disabling a feature, communicating with customers, or applying a temporary mitigation. The important thing is to stabilize the system.
  • Analyze what happened in depth without seeking a single “root cause.” We understand that incidents usually stem from a combination of causes and circumstances. We use techniques like the 5 Whys, asking “why?” starting from the first visible symptom. Doing this in a group allows us to uncover the various factors that contributed to the incident. Often, we find flaws in the process, assumptions, communication, or even data interpretation.
  • Define corrective and preventive actions that not only avoid the problem but also reduce future recovery time and increase system resilience.
  • Integrate these actions into the normal workflow so they don't just remain on paper.
  • Use a blameless incident report, public within the company and collaborative, as the basis for collective learning. This continuous analysis and learning process is an example of Kaizen—continuous improvement applied to incident management, where we constantly seek ways to improve our processes and prevent future errors.

These reports include summaries, timelines, causes, actions, and learnings. Sharing them openly reinforces the message: errors are not hidden, they are learned from. And when the whole team internalizes this, the organization improves faster.

In the end, the message is clear: incidents are inevitable, but how we respond to them truly defines our culture. We can hide them, blame, and move on... or we can use them as catalysts for improvement and continuous learning. In Lean Software Development, we choose the latter.

In the next article...  

Once we have established how to detect and respond quickly to errors, it is crucial to build a solid foundation. In the third part, we will delve into internal quality as the basis for sustainable development. We will see why less is more, how simplicity and well-thought-out design accelerate development, and how a good technical foundation allows us to move fast without breaking things.


Related content

Monday, April 14, 2025

Lean Software Development: Building with Quality

Part one of a series on how to build with quality based on Lean Software Development principles. In this article, we explore what “quality” really means, how it differs from the traditional approach, and what principles help us prevent errors from becoming defects.

Lean Software Development: Building with Quality

One of the main differences between Lean Software Development and traditional approaches lies in the treatment of quality—both in the resulting software or product and in the work environment (collaboration, tools, communication).

Lean Software Development considers quality as an integral part of the product’s value, essential to deliver value to the customer as efficiently as possible. From this perspective, all quality issues are considered one of the main wastes to eliminate.

In contrast, traditional approaches usually focus primarily on the quality of the final product. Lean, on the other hand, focuses on the continuous improvement of the processes and the system that generates the final product.

Quality control, which traditionally focuses on inspecting the product at the end of the process, becomes in Lean a fundamental part of each stage. In other words, instead of inspecting and validating quality at the end, quality is built in from the beginning and maintained throughout the process. This involves moving from a reactive approach to problems to a more proactive one, making any issues visible with the goal of solving them immediately and addressing root causes (system or processes).

We can summarize these differences as follows:

Focus

  • Traditional: Mainly on the quality of the final product.
  • Lean Software Development: Continuous improvement of processes and systems.

Quality Control

  • Traditional: Inspection at the end or at specific production checkpoints.
  • Lean Software Development: Built-in quality at every step (jidoka).

Problem Solving

  • Traditional: Reactive, addressing problems after they occur.
  • Lean Software Development: Proactive, highlighting and solving problems immediately.

In the end, Lean Software Development considers that all rework (incidents, bug fixing, repeated processes, etc.) caused by problems and defects is one of the major wastes to eliminate. Therefore, it focuses on introducing quality at every step of the process, minimizing waste, and fostering a habit of working with quality and continuously improving.


Quality is a broad concept that can cover various aspects: external quality (how it is perceived by the customer or end user), internal quality (related to the ease of evolution and maintainability of the code or system), and even the quality of our processes and work environment.

In this article series, we will place special emphasis on external quality, as perceived by customers or users. However, we will also address aspects of internal and process quality, since they are fundamental for sustaining high external quality in the long term. In addition, since Lean Software Development places strong emphasis on continuous improvement of both the processes and the system to ensure such quality, we will also address aspects related to process and work environment quality.

Basic Concepts

Let’s start with some concepts and definitions that will be useful throughout the rest of the articles:

  • Error: We consider an error as any deviation from the expected result. For example, in our application’s execution, an error may be a failed test, code that functionally does not meet expectations, among others.
  • Problem: In the context of Lean Software Development, a problem is any obstacle that prevents us from delivering value to the customer in the most efficient and effective way. This includes the gap between the current state and the desired state, but also covers situations such as
    • Ambiguous requirements leading to incorrect implementation.
    • Communication problems within the team that result in misunderstandings and delays.
    • Technical limitations preventing desired performance.
    • Inefficiencies in the deployment process causing delays.
  • Defect: A defect is an error that prevents the product from fulfilling its function or purpose, directly affecting customers. Defects are one of the main sources of waste identified in Lean Software Development (see “Eliminating Waste in Development”). Therefore, it is a priority to avoid them whenever possible and eliminate them immediately when detected. The linked article explores how Lean Software Development adapts the seven types of waste from Lean Manufacturing to software, focusing on eliminating activities that do not add value to the customer and promoting practices like reducing “Partially Done Work” and avoiding “Extra Features and Code.” See article for more details.

It is important to remember that defects are a subset of errors, and problems can include multiple errors and defects.

Although this classification may seem confusing at first, distinguishing between problem (a general situation that includes errors and defects), error (any deviation from expectations, like a failed test, an incorrect variable name, or a poorly implemented feature), and defect (an error that affects the customer or the system’s purpose and is a fundamental source of waste) helps us better understand the situation and define appropriate processes to address each case.

Lean Approach to Reducing Errors and Defects

  • Jidoka: Also known as "autonomation", this principle seeks to detect and correct defects automatically, stopping production as soon as an error is identified. A classic example in Lean Manufacturing is the use of “Andon Cards,” visual signaling systems that allow any worker to stop the production line upon detecting a problem. In our software development context, this translates to automated testing at different stages, alarms and monitoring, the use of a CI pipeline that stops deployment if tests fail, among other mechanisms.
  • Poka-Yoke: Translated as "mistake-proofing", this approach relies on systems and designs that prevent human errors. In software development, Poka-Yoke applies both to our internal processes and the user's interaction with the product.
    • In the development process, we implement Poka-Yoke through practices such as using strongly typed programming languages, tools with optimal default settings, automated tests, ValueObjects, and preconditions. These techniques help prevent coding and design errors.
    • In software usability, we apply Poka-Yoke through error-prevention systems, effective contextual help messages, and intuitive designs. This is where User Experience (UX) plays a crucial role. Although UX is not a direct Poka-Yoke mechanism in the technical sense (like a physical sensor stopping a machine), it shares the fundamental goal of preventing user errors. Good UX design anticipates possible mistakes, guides the user clearly, and provides immediate feedback, significantly reducing the chances of user error. In this sense, UX complements and reinforces Poka-Yoke principles, ensuring the software is as intuitive and error-free as possible for the end user.
  • Kaizen: This is the principle of continuous improvement applied to our processes and systems. As one of the pillars of Lean, it promotes the constant search for ways to reduce errors and improve quality, both in the product and in the processes and tools we use.
Majo statt Senf, CC BY-SA 4.0
  https://commons.wikimedia.org/w/index.php?curid=38767688

Avoiding Errors from Becoming Defects

In all the teams I’ve worked with, we make mistakes continuously. Some are just development process errors, while others are misunderstandings. The latter arise because we’re constantly learning about the problem. This is the reality: we make mistakes constantly, and everyone I know in this profession makes them. Some more, some less, but no one is error-free.

If we acknowledge and accept this reality, the important thing is to understand that the goal is not to completely avoid errors (something impossible), but to ensure that those errors don’t become defects that impact the final user experience. The article “Be humble, no rockstars allowed” advocates for humility and teamwork in software development, emphasizing that continuous learning and effective error management are crucial—rather than relying on individual “rockstars.” See article for more.

This is Lean Software Development’s approach: acknowledge and accept that we make mistakes, and focus on having a process that allows us to detect them, protect ourselves from them continuously, resolve them, and prevent them from becoming defects.

How do we achieve this?

  1. Incorporating quality in every phase of development, automated whenever possible (Jidoka + Poka-Yoke).
  2. Continuously improving the mechanisms and techniques that ensure quality at each stage (Kaizen).

In the next article, we will explore how to detect errors as early as possible, stop the flow when they appear, and learn from them without pointing fingers. This could mean stopping the CI/CD pipeline if an integration test fails, blocking code commits if unit tests don’t pass, stopping a pairing session if a critical error is discovered, or even temporarily halting new feature development to address a severe performance issue. Because working with quality also means having a strategy so that errors don’t become defects.