New bookLeading Effective Software Teams — a systems thinking guide for engineering managers.Explore the book →

Writing Code is Easy. Delivering a Software Product is Still Hard

Why faster code rarely means faster delivery, and what to manage instead

All people leading engineering teams have the same evergreen challenge: how can you deliver more value to customers at a faster speed? It doesn’t matter if you are a founder, a product or an engineering leader.

This challenge is more urgent than ever at the moment, with AI revolutionizing how software is built. However, we seem to be living in a paradox. We have incredible tooling at our disposal that can accomplish in minutes tasks that would take days before, with faster coding, faster prototyping, and faster pretty much everything.

Even in that context, the gains in speed often don’t translate to the same gains in value delivery, as the speed has unintended consequences. It can create more issues, with quality decreasing. Or it can create misalignment, with people running in different directions. Or also lead to shallow decision making as everyone tries to keep the agents running.

Ultimately, anytime the process is changed, the bottleneck moves. And if you’re not aware of where it goes, efforts will likely go to waste.

While this is a pre-AI example, in a past role I was leading a small startup team with 10 engineers. The product was a growing leader in its market and the team had a culture of delivering extremely fast.

If you were on the team, it was obvious we were paying the price somehow. Engineers were constantly looking for quicker ways to get work done, often taking a shortcut at some level, even if unintentionally. But the company leadership didn’t see that, since value was being delivered from their point of view.

Until I had the opportunity to make it clear. We had a plan to deliver a major feature and the company wanted it done in 4 weeks, which was a very tight timeline from an engineering perspective. And it resulted in the situation demonstrated in the image below.

Share of features, tasks and bugs the team worked on each week over eight weeks. Features dominate weeks 1 to 4; in weeks 5 and 6 bugs take up a third to over 40% of the work.

We measured the number of bugs and tasks we were working on per week, and it was clear that the delivery crunch period in weeks 1-4 (when we “successfully” released the feature) resulted in two weeks of mostly bug-fixing after that. The pressure was not leading to faster delivery, it was leading to a worse customer experience.

The theory behind this is simple. The value stream of delivering a feature is not just about delivering the code. It goes from understanding the need, shaping the solution, prioritizing it against other solutions, then executing it through design and engineering without making any unintended changes, like bugs or changes in behavior to other parts of the system. And every step provides a feedback loop into the system, validating the previous step. For example, a user research interview validates the design without building it, and code reviews validate correctness without deploying the code.

The value stream from requirements to customer feedback, with feedback loops: user research confirms the design hypothesis, code review validates the code’s correctness, and customer feedback confirms the requirements were correct.

However, in practice it is not simple to make that value stream effective. Different people will have different incentives and opinions. As the product grows, so does its complexity, introducing a larger surface for issues. And a real company is not building one feature at a time, but many, which means there are multiple ideas and changes in motion concurrently.

Applying more speed to all of them, as AI does, won’t necessarily make it quicker, and in many situations can make it slower. And that is what many teams are seeing. The code changes or prototypes are coming out at lightning speed, but the end result, meaning customers obtaining value by using new features, not so much.

The next obvious step is to throw more AI (or tokens) at the problem. If more speed in product development means faster prototypes, create an eval or A/B test to decide which one is the best one. If faster code changes are generating more bugs, have an agent fix them as they are created. The problem is that it doesn’t add up. Each bug fix means another change in the code, with the risk of introducing more issues. Overall, more movement and options will just make the system more complex, and that complexity will be paid at some point, even if that point ends up being a customer that is overwhelmed by the options and constant change in a product.

That’s the deeper problem: making one part of the system faster can overwhelm the rest, effectively negating the feedback loops. That’s what some teams are seeing with code reviews right now. Reviewers receive more requests than they can handle, so they approve them without looking. The review still happens on paper, but it no longer catches anything.

The same value stream with many more loops between writing code and code review, overloading that part of the system.

The way forward, especially when everything is changing, is to understand the value stream end to end and resolve one bottleneck at a time.

That was the conclusion from the story above. I presented that chart to the CEO, explaining that demanding faster delivery was not giving us a speed advantage, since we were then spending time fixing issues after that. Beyond the speed, it was providing a worse experience to customers who had to deal with those issues, something that affects real business results. After that conversation, we started looking at the same data regularly on our weekly leadership syncs, to ensure quality was not being compromised. We ended up with a more sustainable process for the team, and a more successful result for the company and its customers.

If you want to do that, where should you start? There are three principles that I believe can get you to a much better state:

Firstly, define the outcomes for your team in terms of customer value, not software. In practice that means defining projects based on their business results, so that the team is aligned with the company. You should also define work items in terms of customer value (through user stories) so that it becomes clear that outcomes are the objective, not the number of pull requests.

Secondly, understand and manage the end-to-end flow of your team. Once you have the outcomes, then you can manage your team, and its execution, against them. The practical approach is that you should focus on the time each change/feature takes to get to production and adapt to that. For example, if planning takes longer than development, the team should focus on making planning faster, not running more coding agents. The goal is to reduce the idea to customer value cycle time for each feature, not to make everyone busy within their own roles.

Lastly, think about team productivity, not individual productivity. This relates to the above, but is worth highlighting since incentives are often misleading people. It doesn’t make a difference if an engineer can run 100 agents at the same time if that means it creates 100x more work for someone else down the line, increasing cycle time to delivery. The same applies to product and design decisions. The goal is for the team to deliver as fast as possible, not each individual separately.

If you do the above, then you are managing your team as a system. And once you do that, you can experiment within it. Maybe skipping code reviews makes sense, or maybe it doesn’t. It will depend on your context. But if you have the outcome in mind, you can assess that and make an informed decision.

Comments

  1. Loading comments…

Leave a comment