It is a well known fact that time is a driving force in multiple industries. For software development, it is a constant challenge, to be able to reconcile the fulfilment of stipulated deadlines with other needs of the project (or product).
In this battle against time, often we are faced with the tough decision on making sacrifices in order to meet the deadlines. Sacrifices such as taking small shortcuts, implementing suboptimal solutions, and others.
Although it is natural to think of quick solutions that are not always ideal, it bears to the developer (or team) to ponder and understand the boundaries between speed and quality. We need to take a pragmatic look at how we manage the quality of the project so that we don’t lose speed in the long run, after all, if we always take shortcuts, the code tends to become increasingly difficult to maintain over time, which, in consequence, slows down the team.
In this article, I’ll discuss about the theory and application of the technical debt: a technique created so that software development teams can have this pragmatic strategy of managing the quality of the code.
What is technical debt
Tech debt is a software development concept that represents the implicit cost of an implementation or solution that is suitable for the present moment, not considering an approach that might take more time but could be more sustainable for the project.
This concept can be compared to a financial debt (hence the name), which, by not being cleared, generates interest over time, making it harder to be paid.
Besides that, on a software level, this debt generates difficulties to maintain the code and to add new functionalities, leading to delays and possible alterations on the final product.
So in practical terms, let’s illustrate a scenario where a developer makes a decision to go with an approach that solves the problem, but it’s not optimal. Therefore, they are left with an obligation to repay it sometime in the future (coming back to that piece of code and fixing it). It’s a conscious decision made with the awareness of the need to come back to it.
Why does tech debt happen
There are different types of tech debt, and a wide range of reasons behind their occurrence.
Classifying the different kinds of tech debt is helpful to address the identified problems, come up with solutions for them, and to communicate these thoughts. Also, having them well defined is very handy for communicating to non-technical people. According to Martin Fowler, these are the types of tech debt one can encounter when developing software:
Inadvertent - Prudent:
A wrong choice regarding technology was made or there was a lack of iterative development methodology.
Inadvertent - Reckless:
There is a lack of technical knowledge to perform a given task or design a project.
Deliberate - Prudent:
There was not enough time to consider a proper approach to solve a problem, and the developers are aware of the need to deal with consequences.
Deliberate - Reckless:
There was a lack of time and a choice to ignore proper design choices was made.
How to eliminate tech debt
Believe it or not, eliminating tech debt completely is just not possible. Tech debt is unavoidable, which means it will always exist, but it does not mean it cannot be managed. It is up to us to control it and make sure it doesn’t grow.
It is so unavoidable that even if you can make the perfect decision on a project today, down the road, eventually the code will become suboptimal. There will be changes to the operating system, or the framework, for example. So your perfect decision may last for a while, but eventually there will be some work required to make sure these choices continue to work, or to add a new feature to this project.
Eventually, the time to address the tech debt accumulated over time comes, and then, the developer either has to fix the old code before adding new code, or they have to fix it because a feature stops working due to system/framework/language changes.
You owe the software you’ve written in the past a little bit of attention.
Software development is, among many other things, making trade-offs between choices. Tech debt, will come in the future, and it is directly based on the choices made today. And these choices can be anything from frameworks you chose, to UI design choices, to architectural choices.
Since there’s no ultimate way to eliminate it completely (as far as I know), let’s think about minimising it. There are a few tips to help:
- Don’t write spaghetti code
- Well defined architecture
- Take some time to evaluate/fix current code
- DRY, clean coding
- Have someone else look at your code (code reviews)
Identifying tech debt
Tech debt presents itself in multiple shapes and sizes. It could be a poorly designed architecture, or just a method or class that does too much, for example. Due to its broad scope, sometimes, identifying tech debt might be a bit subjective, but by no means impossible.
One important thing to have in mind is that shortcuts can lead to tech debt, so it is important to take note of why the decision to take this shortcut is being made and register somewhere that this needs to be addressed in a future moment.
At other moments, tech debt might appear in response to a lack of technical knowledge on the developer side. In these scenarios, a more adequate decision could be made by asking the colleagues in the team what approach they would recommend.
Apart from these mentioned ways, there are other cases where the code is already written. Then, identifying tech debt can be done by observing some traces. Those traces are often called code smells, and they symbolise how a piece of code could ‘smell bad’, because it is hard to understand or modify, for example.
A code smell is a surface indication that usually corresponds to a deeper problem in the system. — Martin Fowler
Here are some examples of tech debt in iOS Development:
- Massive View Controller
- Pyramid of doom
- Chain of calls
- Untested components
- Global mutable state
- Methods with many parameters
- Long methods or methods that do too much
Why should you care about it
Even if we recognise tech debt as unavoidable and not inherently bad, it is hard for us to ignore the disadvantages it could bring to a product or a team, especially if neglected. This is because choosing the easier option, going for the quick fix leads to weaker software. And a weak software might not be able to support the business as it should, and, in a worse scenario, harm the business.
In the words of Ward Cunningham, who popularised the metaphor:
The danger occurs when the debt is not repaid. Every minute spent on not-quite-right code counts as interest on that debt. Entire engineering organisations can be brought to a stand-still under the debt load of an unconsolidated implementation, object-oriented or otherwise.
A well known example of the impact of technical debt is the Y2K crisis. The decision to define years only by their last two digits (e.g., 1998 as “98”) was a remarkable space-saver in the early decades of computer processing when the price of digital storage was still astronomical. But when the world reached the new century, programs were still unable to distinguish between 1948 and 2048. Over $300 billion was spent worldwide to fix the problem before it was too late. By many estimates, that cost would have been significantly lower if the software had been updated over time. There was ample opportunity, as a man named Bob Bemer first called attention to the problem back in the ’50s.
If not handled, tech debt can bring other undesired consequences to the business, apart from the financial consequences such as the example cited above. It could cause low team moral, because dealing with a messy code base can be really stressful and frustrating. Also, tech debt can accumulate in such a drastic way that it reaches a point where a rework is necessary in some parts of the code, or even a complete rewrite of the whole software.
Wrapping it up
As we discussed, there’s no escape from it. From the very first line of code written, tech debt can appear. What will make a difference, however, is how it is managed. Accepting it is the first step, and recognising that eventually it will need to be settled, is equally important.
For this, it is essential to discuss with the team and the management, quantify and prioritise it, and draw a plan to deal with it.
From then on, we can be more demanding in terms of the quality of our code, set standards, write even more tests, and do our best to guarantee a high level of code quality.