Technical debt management requires a balance between quality and speed. Technical debt needs to be understood, used and managed from a long-term perspective.
The metaphor of “technical debt” in regards to unclean code was originally suggested by Ward Cunningham.
Technical debt — also known as tech debt or code debt — is what happens when a development team speeds up the delivery of a project or functionality that will require refactoring later on. A quicker development process becomes the priority instead of high-quality code.
Technical debt is the measure of the cost of reworking a solution caused by choosing an easy yet limited solution.
You can temporarily speed up without writing tests for new features, but this will gradually slow your progress every day until you eventually pay off the debt by writing tests.
The most significant consequence of a complex technical debt is that it hinders a company’s ability to compete and innovate. It robs you of resources, time, energy, and the ability to innovate, adapt, and grow. And it's one of those things that's hard to identify and manage and even harder to avoid.
Sometimes business expects new functionalities to be delivered as soon as possible, often before all project-related activities are carried out even at the lowest acceptable level. In this case, patches and kludges will appear in the code to hide the unfinished parts of the project.
Lack of understanding of the consequences of technical debt
Sometimes your employer might not understand that technical debt has “interest” insofar as it slows down the pace of development as debt accumulates. This can make it too difficult to dedicate the team’s time to refactoring because management doesn’t see the value of it.
Failing to combat the strict coherence of components
This is when the project resembles a monolith rather than the product of individual modules. In this case, any changes to one part of the project will affect others. Team development is made more difficult because it’s difficult to isolate the work of individual members.
Lack of tests
The lack of immediate feedback encourages quick, but risky workarounds or kludges. In worst cases, these changes are implemented and deployed right into the production without any prior testing. The consequences can be catastrophic.
For example, an innocent-looking hotfix might send a weird test email to thousands of customers or even worse, flush or corrupt an entire database.
Lack of documentation
This slows down the introduction of new people to the project and can grind development to a halt if key people leave the project.
Lack of interaction between team members
If the knowledge base isn’t distributed throughout the company, people will end up working with an outdated understanding of processes and information about the project. This situation can be exacerbated when junior developers are incorrectly trained by their mentors.
Long-term simultaneous development in several branches
This can lead to the accumulation of technical debt, which is then increased when changes are merged. The more changes made in isolation, the greater the total technical debt.
No refactoring in the initial stages (subsequent refactoring will be more problematic).
The project’s requirements are constantly changing and at some point it may become obvious that parts of the code are obsolete, have become cumbersome, and must be redesigned to meet new requirements.
On the other hand, the project’s programmers are writing new code every day that works with the obsolete parts. Therefore, the longer refactoring is delayed, the more dependent code will have to be reworked in the future.
Lack of compliance monitoring
This happens when everyone working on the project writes code as they see fit (i.e. the same way they wrote the last project).
Lack of adaptation to standards and good practices in the initial stages. Adaptation to standards in later stages may be significantly difficult or even impossible
This is when the developer just doesn’t know how to write decent code and manage it effectively.
Problems with the solutions architecture, functionalities are not properly designed and separated. They are not flexible enough to easily adapt to the changing business environment and requirements.
Reduced Forward Motion
The problem with technical debt is when it gets out of control and development teams spend the greater part of their time paying off the “interest payments” from past projects, instead of working on new features or critical new updates.
The technical debt must eventually be repaid and that comes at the cost of adding new features or working on other updates that move the product forward. Progress becomes slowdown.
Instead of tackling new change requests, the development team spends hours, maybe days, even weeks, wrangling the quick-and-dirty code to get the product to a state in which future modifications won’t be a painful effort or crash the system.
Poorly Designed Code
If taking the quick and dirty route to reach a deadline, developers could skip the traditional protocol of writing clean, organized, code. Thus, the code structure would be messy and with potentially low readability.
Some red flags of poor design are:
- Using a code style once and not repeating the same behavior throughout
- Leaving out descriptive names for each of the modules
- Loosely stringing together code modules
These design practices may help get the project completed in time, but will make it challenging for different programmers to work on the project in the future. The technical debt associated with poor design will be a backlog of items related to cleaning up the code.
Launching an imperfect product can be a benefit for your business, but it can also have challenging consequences.
Technical debt is related to a product showing unstable performance related to fixing bugs, system crashes, and engineer bandwidth.
Bugs in the code can appear after rapid production release, so you'll have to pay technical debt to fix them.
If your engineering team is spending a lot of time implementing simple improvements in a code module, or they are asking for a schedule extension, you will incur technical debt to fix an area.
The code is written in a hurry. And, if your system crashes due to high volume traffic or code editing, then you just need to add to your technical debt to find the cause.
Technical debt drains your productivity, leading to slower and slower output.
Poorly designed code will require more time and effort for your team to understand and maintain.
If you ask your team to make a simple improvement to code that normally takes 3 weeks, you might be surprised to learn that it will actually take 6 weeks and they will have to postpone other projects.
Technical debt will not only lead to your team slowing down their build times, but also slowing down your overall production-test-release cycle.
You will request new features, but such changes will add new bugs to poorly written code and make it even more difficult for your QA team to test.
And, when you finally navigate the code and are ready to release, your team will likely be stressed due to the lengthy schedule and you will most likely incur profit and loss due to lost time in the market.
With the launch deadline approaching, the QA team will be forced to speed up their process and possibly miss the tests or deal to solve them later and technical debt will have to go back and carry out the skipped tests. This has the negative effect that you're releasing code that's been rushed-tested or only minor-tested.
As the old adage goes, you get what you pay for. If that means taking advantage of a huge market opportunity, taking on technical debt can sometimes be the optimal decision. But the problem arises when the number of snowballs becomes a huge number, which can consume your resources. Too much technical debt can reduce team agility, produce poor code, stress the testing team, and eventually reduce your company's overall productivity.
Since we’ve learned what technological debt is, let’s now distinguish its different causes. The ability to categorize tech debt is extremely important for its accurate analysis, because the estimation of technical debt depends on its nature (and the time it was incurred). Martin Fowler distinguished four types of tech debt (technical debt quadrant).
Going further, we can identify each quarter by assigning a color that matches its purpose, where red-orange means warning colors, and green-blue indicates desirable colors.
Technical Debt Quadrant - Martin Fowler
Four types of tech debt:
Let’s start with red – reckless and accidental debt. It is the least desirable type as the team has no choice and either did not recognize the moment when the debt was incurred or there is no way to eliminate the existing debt. This quarter is red because in an ideal scenario the team should stop and self-improve (through training, employing a competent person or other available means) so that they can at least see the moment and recognize the type of incurred debt. At this point, there is space for deciding whether to further increase technological debt or pay it off.
Orange is a reckless and deliberate debt. This type of debt occurs when the team has the appropriate knowledge to implement the task, yet consciously decides to go with a quick and poor quality solution, usually for the benefit of quick implementation. This type of debt mainly relates to long-term behavior, where the decision to incur debt was not caused at all by a short deadline.
Green represents prudent and deliberate debt. It involves cutting some corners in terms of quality as a part of decisions taken by the team due to the looming deadline or simply no need to use a better quality solution. The team recognizes the problem and its consequences, but must currently provide the functionality in time and there is no way to focus on quality at this point or the team sees that the implementation of the issue in an ideal way is not a value in this case, so calmly and consciously gives up.
Unintentional debt is represented by the blue color – it happens despite a careful approach to design and, as it might seem – care for quality. It represents the team’s actual learning process while working on the project. Often, it is only after the functionality is implemented or the project is done when the team realizes that if they had designed the individual components of the application differently, they would probably end up with a better quality solution. We can treat unintentional technical debt as accidental as the team did not incur it on purpose.
- Fragments of code and functions that cover excessive areas and seem to do everything.
- Lack of consistency in implemented and used patterns / architecture.
- The presence of a large amount of dead – unused code.
- "Spaghetti code": code that’s difficult to use, and complex logic that cannot be easily changed or developed.
- Unreadable code.
- Duplicate code, breaking the DRY rule.
- Slow tools or no tools at all.
- Loss of productivity, including loss of motivation and decrease in team morale.
- An increase in the amount of work associated with tests, especially manual tests.
- Postponed implementations.
- Increase in the number of errors and bugs.
- No tests, no test environment.
- Inefficient test environment with a lack of full functionality of the production environment
- High level of stress in the team associated with the upcoming implementation.
- Fear of any changes in the code.
- Falling velocity of the team.
It is necessary for the business side (our clients) to clearly understand the effects of tech debt.
When talking about the technical debt, you need to ensure the balance between time, quality, and cost. But keep in mind the governance model, toolkit, right technology and mindset of the people who are building software. It is important to make the right combinations in this equation.
Software quality and performance are paramount to a good user experience - and speed is essential to meeting business goals on time. Technical debt management requires a balance between quality and speed. Quick workarounds might mean you meet deadlines, but make sure you know the costs. Technical debt may look harmless, but if left unchecked, you will find that at some point, speed and agility are no longer an option.
Younger developers may be lack of experience and may be tempted to ignore the debt until it piles up. Or they can be difficult to identify and fix. As organizations focus on accelerating time to market and empowering non-professional developers to create business applications, the risk of technical debt increases.
Whatever the case, there are ways to reduce and manage technical debt.
Think of technical debt as a tool that can make or break your product release cycle - understand it, use it, and manage it with a long-term perspective.
- Dissemination of awareness – the more programmers and clients are aware of the effects of technical debt, the higher priority should be given to debt elimination.
- Avoiding technical debt from the moment of writing the first line of code – using thoughtful solutions.
- Reducing technical debt should be part of your work ethic or the culture of the company you work for.
- Clean code.
- Code review.
- Pair programming.
- Caring for (personal) education and learning the techniques that help maintain high quality code.
- Maintaining high readability of the code (measured for example by understandability for other team members) as one of the important quality criteria.
- Compliance with good code writing practices.
- Refactoring as an effective tool to fight technical debt.
- Regression tests – help detect when errors are entered into the code, allowing them to be immediately removed or undone.
- Software architect/ leader in the team is responsible for managing the team through technical decisions, especially at the architecture level.
- Automatic code analysis and technological debt measurement.
- Regular investments in technologies (e.g. ongoing software updates) are associated with a significantly lower total investment cost and less organizational effort than in the case of one-off repayment of significant technical debt.
- Accept technical dept:
- Sometimes accept a small technological debt.
- Technological debt should not be incurred in relation to the most important areas and processes in the enterprise – it is only allowed for less important topics.
- It is worth taking it only when it is necessary (i.e. economically viable) – for a short, predetermined period, in a planned and predictable way, treating the introduced solutions as temporary.
- Before incurring a technological debt, determine its size, impact on our business and the analysis of the risk associated with it.
- Build to validate the outcome: in the early stages, solutions are "quick and dirty", meaning at the moment technical debt is high. Investing as little time initially as possible helps us avoid waste because many solutions will not achieve the expected results.
- Eliminate wrong solutions: not all solutions will work as expected. Therefore, we should remove them from the product.
- Elevate promising solutions: once we identify solutions that lead to the expected results, it's time to improve on them and demonstrate whether they can match the expectations.
- Once the outcomes are achieved, pay off the technical debt: make your codebase scalable. Once you have a solution that achieves the desired outcome, you should settle the technology debt.
Technical debt is a basic component of the cost of owning an application and sometimes its incurrence can be profitable, but it is the responsibility of the modern developer to at least maintain the debt level at the current level without increasing it. Raising awareness about technical debt on both the business side and the software development company side is able to significantly improve technical debt management and related costs. It is necessary for the business side (our clients) to clearly understand the effects of technical debt.
For programmers, the important information is that our code is never perfect and technological debt is incurred along with writing the first line of code – what is currently acceptable in a year or more may turn out to be an outdated and unused solution. The most important thing is that our code is good enough when it is created.
Technical debt is not profitable in the long run. We will always have to pay back more than we incurred – and the larger the debt, the more “interest” to pay.
However, if it occurs, technological debt should be properly managed – so as to minimize the associated risk.