Blog September 27th, 2023
by Priit Liivak, Chief Architect
The relevance of managing minor technical debt
I have experienced a wide variety of technical debt and have seen many attempts to tame the accumulation and the existence of technical debt in large number of projects. For seamless developer experience it is vital to value the importance of noticing even small technical debt and appreciating the time developer invests in reducing it.
Imagine a software system with 30+ microservices, several development teams, and multiple parallel feature streams. A developer is assigned a brand-new story to implement: technical design must be done, modifications made in multiple microservices, and rollout to production planned. Before any code changes can be made, a developer usually explores the code to identify the areas requiring modifications. When the developer encounters a feature flag during this exploration it is necessary to understand the current state and purpose of the found feature flag.
There are a few possible options why this feature flag is set in place:
- The feature flag can disable some functionality and the code behind it is essentially unused once the flag is enabled. In this case the developer might have a chance to ignore the code behind the flag.
- The feature flag can enable some functionality and the code behind it is relevant even if the flag is not enabled at current time. In this case the developer might need to do additional modifications within the code behind the feature flag.
- The feature flag might also act as a dynamic toggle that is switched at certain times to temporarily enable or disable a feature. In this case the developer must consider the long-lasting presence of the feature flag and its impact on the new development.
It doesn’t take long to investigate the purpose and relevance of this flag for the new feature but often this is not the only question that needs investigation before production deployment. For me such a feature flag is technical debt. As an individual instance, it has a minimal impact and cost but often the individual cases of technical debt add up and influence the efficiency of a development team in various ways.
The developer started the exploration with a specific feature in mind and discovering a simple feature flag acts as a cognitive distraction. Regardless of the relevance to the change at hand the distraction has an impact on the developer. The discovery of a feature flag increases the complexity of the solution and thus decreases comprehensibility. Again, with one feature flag the impact is minimal but forgotten feature flags are not the only technical dept with such an impact.
Technical debt sources
Overengineering solutions and incorrect use of design patterns has the same influence. If a developer discovers a Factory pattern that builds Strategies it may have a far more significant impact than code duplication or lengthy nested control flows. I am not promoting bad code over patterns but rather asking to consider if the impact of increasing complexity is justified. Sometimes it is better to prioritize ease of understanding and comprehensibility over extensibility. Such a mindset allows a developer to consider a wider range of implementation options and not be polarized towards “the best” solution. Sometimes the second best proves to be much more valuable in the long run. These issues often start appearing after someone has said the words “but in the future we may need” at the design meeting. As the future may be difficult to predict and we often overestimate the probability of future events we should always prefer other arguments while making design decisions.
A far more frequent technical debt is confusing execution flow. For example, a set of records are retrieved from the database, the set is split to two parts based on some characteristic and then these parts are processed in a specific sequence. Even if I trust the implementation correctness and it is extensively covered by tests, this kind of implementation detail often hides a technical debt that makes me ask “why is it implemented like this”. And because I ask this, I’ll go and search for the answers. Finding the cause may require just a few minutes but this acts as a cognitive distraction that adds to all the previous occurrences of me asking “what is going on here”. Does this sound familiar?
Tracking the impact
It is difficult to measure the cost and impact of an individual feature flag or an overly complex solution, but it should be easy to measure the volume of cognitive distractions over the period of time. By just asking a subjective opinion from the developers about the quality of code we gain some understanding of the amount of “what is going on here” moments they encounter in the codebase. We can also compare the initial estimates (either time or complexity) with the actuals to identify potentially problematic components within the system. In addition to these commonly used approaches, I’d like to present a few that I have not tried myself but would very much like to.
Sometimes it is better to prioritize ease of understanding and comprehensibility over extensibility.
I’d like to try out an idea of a Story Retrospective. During the final build or deployment pipeline a developer can summarize the experience of implementing the story: the complexity, comprehensibility, discovered potential technical debt, the level of enjoyment while writing code, new learnings and cut corners. This idea cannot work as an ideology or a rule, not all stories need such analysis, but I believe a developer can easily sense if such a closure would benefit the technical lead or the next person working on this code. Such retrospective remarks can be then summarized for the sprint retrospective and analyzed with the whole team if needed.
Another approach that I’d like to test somewhere would be to enable developers to flag certain areas in the code. A way to easily mark an area of code without an explanation or accompanying comment and a way for a developer to add +1 to an existing remark. After some time, I expect to see patterns of these tags and clusters that emerge around the code that is most difficult to understand. Combining this information with the upcoming feature design, we could estimate the complexity with higher accuracy. What I’d like to avoid with implementing such an approach is littering the code with marker comments although it seems to be the only viable option for applying this idea.
We often look for significant examples of technical debt and forget that many minor instances also add up to have a big impact. Cleaning up the feature flags or improving the documentation of a confusing execution flow may not be the most exciting work a developer could do but nevertheless it is an important one. It is just as important to work on these topics constantly and not put them aside for the times when the development team has less features in their backlog. For seamless developer experience it is vital to value the importance of noticing even small technical debt and appreciating the time developer invests in reducing it.
Get in touch
Let us offer you a new perspective.