The Economic Rationale For Close Collaboration Part 2: Cost of Rework
You open the pull request, and within minutes it becomes clear that the work veered off course early on. The product is built on the assumption that each user has a single email address for correspondence, yet this change attempts to break that assumption. Some team members were aware of this constraint, but the person doing the work was not. Now the team is facing significant rework to make everything fit. If only this had been clear from the start.
Here’s another example. During stand-up, someone asks a seemingly innocent question: “Do we really need to keep the write model and the read model in sync, or can we just query the write database directly?” The system was deliberately designed with a strict separation between writes and reads, and bypassing it would undermine performance guarantees and consistency assumptions across the platform. The question makes it clear that this architectural decision wasn’t understood when the work started. A day of implementation is now built on the wrong foundation.
In the first part of this series, I focused on Cost of Delay: the cost of when. There, I assumed that close collaboration improves cycle time, without explaining why. This piece picks up that thread. Improvements in cycle time come in two forms: reducing waiting time and reducing effort lost to rework. The reduction of waiting time (inventory) is a classic topic in lean manufacturing and is discussed extensively by Reinertsen. Here, however, I want to focus on the reduction of rework: an effect that is particularly relevant to software engineering, and one that both examples illustrate.
Known versus unknown unknowns
Software engineering is knowledge work, and knowing the right things at the right time and in the right place is essential. There is also a great deal to know. Building a software product requires understanding product strategy and tactics, frontend and backend frameworks, programming languages, domain concepts, user experience and UI design, databases, infrastructure and cloud platforms, monitoring, security, testing patterns, code design and architecture, and much more.
Modern product teams are responsible for all of these areas. That is a significant cognitive load, one that requires team members with diverse expertise and experiences. When teams are formed, this diversity of knowledge is often a key consideration. Yet once the team starts working, it faces a different challenge: ensuring that all this knowledge is applied at the right moment and in the right place.
A common fallacy in cross-functional teams is treating software development too much like a manufacturing process, assuming that for any given piece of work it’s always clear which knowledge is missing. These are known unknowns: “I need to do X, but I don’t know enough about it, so Pete should take a look.” This closely resembles manufacturing, where items move along a production line and it’s always obvious which step comes next. With only known unknowns, everything is just about reducing waiting and making sure someone with the right knowledge is available. In other words, the only cost is the Cost of Delay.
The two examples I started with illustrate that in software engineering we often deal with unknown unknowns: critical knowledge is missing, but the people doing the work don’t realize it. Unknown unknowns are more costly than known unknowns, because the waste isn’t just waiting time, but also wasted effort. Work gets done on the wrong assumptions and must later be undone or redone. With unknown unknowns, the cost is not just Cost of Delay, but also Cost of Rework.
Rework happens at every scale. It’s most visible when we delete or rewrite large chunks of code, sometimes because a feature isn’t needed or we’ve gone down the wrong path. But it also occurs constantly at a micro level: fixing naming mistakes, correcting syntax errors, or tweaking logic that doesn’t quite work as intended.
Rerouting leads to more unknown unknowns
In manufacturing, queues can grow indefinitely. Parts pile up in front of a machine, waiting for their turn, and the work itself remains unchanged while it waits. Software development rarely behaves like that. When work gets blocked or delayed, teams almost never let it sit idle indefinitely. Instead, they reroute. A developer picks a different approach, works around a limitation, or makes a local design decision to keep moving. There is almost always an alternative path forward.
This flexibility is often seen as a strength of software engineering, but it comes with a hidden cost. Rerouting shifts decision-making to whoever happens to be available, not to whoever has the most relevant knowledge. A backend engineer makes frontend choices, an application developer adjusts infrastructure, or someone with limited domain understanding fills in the gaps just well enough to proceed. Each of these decisions embeds assumptions that go unexamined. The result is not visible delay, but invisible risk: progress that looks productive in the moment, yet quietly accumulates unknown unknowns. This only amplifies the problem of unknown unknowns, quietly compounding risk even as work appears to move forward.
This may sound like technical debt, but I deliberately don’t use that term here. I reserve technical debt for conscious decisions: cases where a team knowingly accepts a suboptimal solution to gain something in the short term, making an explicit trade-off. What I’m describing is different: these decisions aren’t deliberate trade-offs, but unintended choices made on the fly.
Heavy tailed distributions
One reason unknown unknowns are easy to underestimate is that most of them don’t stay unknown for very long. Many are discovered quickly, which can create the impression that the problem is limited. But some unknowns persist for a long time. In statistical terms, the time an unknown unknown survives follows a heavy-tailed distribution. Long-lived unknown unknowns are rare, but when they do occur, their impact is so large that they are very dominant in the total cost.
Close collaboration can only address unknown unknowns that exist within the team due to knowledge asymmetry: it cannot eliminate gaps in knowledge the team does not collectively possess. One could argue that this subset of unknowns should have a lighter tail, since knowledge can, in principle, travel easily within a team. I believe that is only true when the team is actively working together through pairing or mobbing.
When people work individually, even while sitting next to each other, the heavy-tailed behavior remains. How often do you later discover that a teammate was operating under a different assumption? Or that a design decision had already been made, but never communicated? Probably not very often. But when it does happen, you are usually surprised by how long it had been true. That surprise is exactly the phenomenon this model is meant to explain.
Closing The Knowledge Gap
When pairing or mobbing, you can often pinpoint the exact moments when these unknowns are “resolved.” Sometimes it’s obvious, like someone saying, “Ah, I didn’t know that.” Other times it’s more subtle, such as realizing, “I would have gone completely off course if I’d been tackling this alone.” These moments can range from small discoveries, like catching a syntax error, to major realizations, such as recognizing you were heading in the wrong product direction, or simply sharing a newly gained insight from the problem domain or feedback received.
The reduction in Cost of Rework grows as the differences in knowledge within the team increase. In these situations, the risk of long-lived unknown unknowns is highest, and that risk is significantly reduced by working closely together in real time. As noted earlier, modern product teams require a wide spectrum of knowledge, so this situation is more common than the exception.
A common reaction from new team members when starting with pairing or mobbing is that they feel less productive. This makes perfect sense. Close collaboration reduces long-lived unknown unknowns, which has a significant impact when they occur. However, as mentioned earlier, these are far less common than short-lived unknowns. As a result, there are periods when multiple people are working together even though each could make progress independently.
The challenge with unknown unknowns, of course, is that we never know when they will appear. One moment, everything seems to be running smoothly without your input, and the next, a crucial piece of information arises that only you can provide. It’s a bit like playing team sports: much of the game might feel like running up and down the field without touching the ball, but then suddenly, a critical pass comes your way, and your input becomes the turning point in the play. And sometimes it takes a shift in mindset to stop focusing on your own productivity and start focusing on the team’s.
Make the economic trade-off through a WIP limit
I’ve explored the economic rationale for close collaboration by examining the trade-off between resource costs on one hand and the costs of delay and rework caused by knowledge asymmetry within the team on the other. But how do we navigate this trade-off in our daily practice? My answer: WIP (Work-In-Progress) limits.
A WIP limit is a team agreement on how many things the team works on at the same time. Higher WIP limits allow for more solo work, while lower limits encourage closer collaboration. There are two key reasons I see WIP limits as a powerful tool for managing this trade-off.
First, WIP limits make collaboration gradual rather than binary. Collaboration isn’t a switch you simply turn on or off. You can do it more or less often, with more or fewer people. Adjusting the WIP limit by even a single item can nudge the team toward more or less collaboration.
Second, WIP limits offer flexibility in how collaboration happens. For example, with a WIP limit of 2 in a team of 4, the team might work as two pairs, or one person could work solo while the other three focus on the second item. Team members can move freely between items in progress, and when several people work on the same item, they can divide tasks or roles however they see fit.
Using a WIP limit is a no-brainer and something I would universally recommend to any team. It makes the team’s current position in the trade-off explicit and highly visible, allowing everyone to clearly see it and discuss when an adjustment is needed.
In my experience, WIP limits in software teams should almost always be set well below the team size. I often use N/2 as an initial rule of thumb. From that starting point, teams can experiment and adjust the limit based on the trade-off they are willing to make between the cost of resources on the one hand, and the cost of delay and rework on the other. A higher WIP may keep more people “busy,” but it typically leads to longer cycle times and increased rework, while a lower WIP moves the system in the opposite direction. Finding the right balance is less about theoretical optimization and more about learning, through practice, what actually works for your team in its specific context. That said, understanding the underlying economic trade-off as I have outlined, can help you recognize much earlier when you’ve ended up on the wrong side of it.



