In my opinion, the development cost of each feature in a product can be divided as follows:
- The direct development cost of the feature or initial cost.
- A weekly or monthly cost only associated with the existence of this feature in our system. Making a comparison with the Basal Metabolic Rate of a human body, we can call this second cost the Basal Metabolic Rate of a feature or Basal Cost of a feature.
The Basal Cost of a feature is composed of two different parts:
- The direct impact in the team capacity of the added complexity of this feature (new dependencies, more code to understand, more possibilities for bugs to hide, etc).
- The impact on the cost of the development or evolution of other features due to potential incompatibilities, coupling, etc.
The Initial cost of a feature
This is the cost/expense incurred by the team during the initial development of the functionality. It includes the cost since the team starts working on the feature until the customer has it available and starts using it. Of course, this process should consist of multiple deployments and partial releases to obtain feedback and make the necessary adjustments…
The Basal Cost of a feature
After the initial development cost, we had this continuous cost that reduces the team capacity for new features development (innovation).
This cost, which continues over time and only ends with the removal of the feature or the end of life of the product, is the cost incurred by the team for the existence of that code and that feature in the product.
It is important to keep in mind that this cost does not refer to the cost of making modifications to the feature or fixing bugs, it refers to the cost of simply having the code there...
Why does this cost happen? Why is there an ongoing cost for a feature that is not evolving?
- The team has to know that code (where it is, what dependencies it has, who interacts with it, how it is designed...).
- The team's knowledge and techniques are continuously evolving. When the team improves in any of these aspects, the team should update the feature's code.
- When the team designs a new feature, the code should be design in a way that is compatible with all the previous features and doesn't generate problems or regressions. And of course, this cost is proportional to all the features we had in the system.
- Something similar happens when the team has a new member. This member should learn about all the features, so the cost is proportional to the number of features.
And worst of all, this overhead is continuous until the "death" of the feature, i.e., the end of life of the product, until no user uses it, or until the end of the world (whichever comes first).
Evolution of the Cost of a feature
As we saw, the Basal Cost is more or less constant during the life of the feature. But each language, dependency, or technology can reach a point that is no longer usable for whatever reason (obsolete dependencies, security problems, normal evolution that deprecate the language version we used, etc). From this moment, the cost can skyrocket because we are forced to update it even if we don't want to evolve the feature.
So in summary, the real cost of a feature can be represented by the following graph:
A common mistake is to neglect the Basal Cost and consider that if no changes are made to a feature, the feature's ongoing cost is zero.
I guess this comes from using metaphors from other professions such as construction that are not suitable for software development.
Although the capacity changes (positively or negatively) over time due to different factors (knowledge of the business, techniques, stress...), the team capacity is finite.
The accumulated Basal Cost of all the features that the teams own reduces the capacity available to develop new features (innovation).
Over time we can see how the team capacity for innovation shrinks very fast.
To a point where capacity is exhausted, and the team finds itself in a situation where it is not possible to innovate and spends all its time "maintaining" the functionalities it already owns.
The above sections highlight several principles that we must consider to improve our product teams' efficiency.
- We should minimize the Basal Cost as much as possible, achieving the desired impact with as little code as possible.
- If possible, even achieving the impact without the need to develop code.
- Iterating the functionality to adapt it as much as possible to the user's needs and making the solution minimal.
- Make the software/design as simple as possible. Easy to evolve, but without over-engineering (YAGNI).
- Eliminate or remove any feature from the product that does not have the desired impact, thus eliminating its corresponding Basal Cost.
- Monitor continuously the team’s code to detect obsolescence of dependencies, languages, and technologies and avoid the Basal cost to skyrocket.
Remember: “Simplicity --the art of maximizing the amount of work not done-- is essential.
Related & References
- https://martinfowler.com/bliki/Yagni.html (related concept cost of carry)