Two truths and a lie about software engineering
"Two Truths and a Lie" is a groovy icebreaker game that encourages players to share interesting details about themselves where two statements are true and one is a fib. In the game, the group guesses which of the remarks are true and which one of them is a lie. I love the game; unfortunately, I’m pretty bad at it as people quickly pick up on my fabrication. Nevertheless, I’m going to play with you and let’s see if you can find the lie:
- Developers spend more time reading code than writing code.
- More time is spent maintaining code than in initially writing it.
- Management accounts for the carrying cost of existing code when planning.
Which one of these statements is the falsehood? I imagine the lie is obvious, but strangely, as an industry, we consistently overlook a fundamental truth of software development and proceed to mislead ourselves, our customers, and stakeholders. We routinely overlook the costs of maintaining code. I categorize software maintenance as a carrying cost and if you do not appreciate its effect on a development team's capacity, you will routinely overpromise and underdeliver.
Carrying costs are recurring
We rarely account for how much time and effort is required to keep existing code running healthily. Healthy running code is not a point in time but rather a span of time. It's a recurring cost that starts once software is in the field and used by people and continues until that code is retired. Thus, because we don’t think in terms of carrying costs and fool ourselves into thinking maintenance-like efforts are one-time events, we overpromise. Once code is in the wild, time is required for the care and feeding of it. What’s more, the time required is proportional to the surface area of the software. The more code that exists, the more time will be spent keeping it running.
I say this with confidence because I’ve fallen into this trap! I’m guilty of underestimating how much work is required to keep the lights on. In doing so, I’ve overpromised and underdelivered to stakeholders. It’s an easy trap to fall into too: it’s far more fun to write new code and add new features. Delivering new functionality to stakeholders intuitively feels more exciting than telling them you fixed a series of bugs. Code maintenance has never been a fashionable activity one brags about.
Accounting for carrying costs
If planning and resource allocation were copasetic and easy in software development, projects wouldn't be late and stakeholders would never be disappointed. Figuring out how much time to allocate to software care and feeding is a mixture of science and art. In my experience, the time required to keep the lights on is far more than you initially think. Accordingly, my advice is to err on the side of conservativity.
The art aspect of this process is in recognizing a team's capacity encompasses more than new feature development. Their capacity must include the care and feeding of running software. From a scientific standpoint, there’s years of knowledge around the various techniques for ascertaining a software team’s velocity; suffice it to say, as a leader, you absolutely must get a feel for how much work a team can reasonably accommodate in a span of time. Once you realize that the vast majority of stakeholders believe work means new features, then you can address the problem by clarifying additional time is required for maintenance.
One way to reason about capacity planning and feature work is to think in terms of surface area. I tend to logically think of a software project as made up of domains. The more domains an initiative has, the larger the surface area. Surface area is at tension with capacity. Capacity is the maximum amount of work your team can do. If you have a lot of surface area, you need a lot of capacity. Capacity is almost always your limiting factor; accordingly, carrying costs tend to accumulate as domains proliferate.
Focus, focus, and more focus
Anyone who’s successfully built a software product from scratch intuitively appreciates the value of focus. A laser focus on creating value for specific users is another way of limiting surface area. As a product’s focus widens to accommodate more features, users, and use cases so too does the surface area of a product. You can hire to keep the pace of surface area, but this relationship is rarely in lock step. Hence, lean towards conservativity when accounting for your capacity to maintain what's going into the field.
There are certainly times when it pays to lean into racing out ahead (i.e. building this new thing right now will yield more users, etc). A bias to action can increase a product's surface area. In those instances, by all means, do it. Recognize that you'll have to pay down that veritable debt in some way either by temporarily slowing down, hiring, or cutting back on promises. Taking the initiative to pull head of the competition or capture a market in need is definitely part of the game! Understanding how and when to race ahead or pull back is how the game is won.
Acknowledge carrying costs and plan accordingly
I hope you enjoyed playing two truths and a lie! Indeed, the fib is that management properly accounts for carrying costs. Frequently this cost goes accounted in favor of new feature development. Sadly, by ignoring carrying costs, we continually overpromise and underdeliver. Acknowledging carrying costs will immediately make you a more effective planner.
An essential aspect of leadership is influencing and inspiring a group of people to achieve challenging goals. Tactically, you will absolutely need to understand your team's capacity and master stakeholder expectations. Software needs care and feeding. It needs maintenance to properly function. A healthy appreciation for the carrying costs of code in production will make you a more realistic and effective leader.
Dig it?