Have you wondered when people talk about technical debts?
How can so many senior developers still write code that has technical debts?
You are proud of your work. But are you unsure if your code introduces technical debt?
Here is what all you need to know about “Technical Debt”
This post will cover:
- Origins
- Types of Technical Debt
- Causes
- Consequences
- Managing Technical Debt
Origins
The term “technical debt” was coined by Ward Cunningham, an American computer programmer who developed the first wiki and was a co-author of the Manifesto for Agile Software Development
In the early 1990s, Cunningham was working on the WyCash portfolio management system.
The project required rapid development and deployment to meet business demands.
Cunningham needed a way to explain to non-technical stakeholders, the long-term implications of making quick and dirty fixes or taking shortcuts in the codebase.
Cunningham drew an analogy between financial debt, and the quick solutions often implemented in software development.
He compared the shortcuts (such as writing suboptimal code to meet a deadline) to borrowing money.
Just as borrowing can accelerate financial growth temporarily but requires repayment with interest, quick coding solutions can speed up development but incur future costs in terms of increased maintenance and refactoring efforts.
The Famous Explanation
Cunningham famously explained the concept as follows:
“Shipping first-time code is like going into debt. A little debt speeds development so long as it is paid back promptly with a rewrite… The danger occurs when the debt is not repaid. Every minute spent on not-quite-right code counts as interest on that debt.”
Types of Technical Debt
Understanding the different types of technical debt can help you manage and mitigate its impact more effectively. Here’s a breakdown of the main types:
1. Deliberate vs. Inadvertent Technical Debt
Deliberate Technical Debt
This type of debt is incurred intentionally, often as a strategic decision to achieve a short-term goal, such as meeting a tight deadline or quickly delivering a feature.
The team is aware of the compromises being made and plans to address them later.
Example: You choose a simpler but less efficient algorithm to expedite a product release, to optimize it in a future iteration.
Inadvertent Technical Debt
This type of debt arises unintentionally, often due to a lack of knowledge, oversight, or miscommunication.
Your team might miss it in reviews.
Example: You might write a method to format a date. But it is already present in the code base.
2. Architectural Debt
Architectural debt involves compromises or flaws in the software’s overall structure and design.
These issues often stem from poor initial design decisions or evolving requirements that the current architecture can no longer support efficiently.
Example: An application initially built as a monolith struggles to scale and adapt to new features, requiring a costly and complex migration to a microservices architecture.
3. Code Debt
Code debt refers to poor coding practices, such as lack of refactoring, excessive complexity, code duplication, or non-adherence to coding standards.
This type of debt can make the codebase difficult to understand, maintain, and extend.
Example: A legacy codebase filled with “quick fixes” and workarounds, leading to a tangled mess of spaghetti code that hinders future development.
4. Documentation Debt
Description: Documentation debt occurs when documentation is incomplete, outdated, or missing altogether.
Proper documentation is essential for maintaining and extending a codebase, as it provides context and clarity for current and future developers.
Example: The team does not update the documentation to reflect new features, API changes, or architectural decisions.
New team members will find it difficult to understand and make changes to the system.
5. Testing Debt
Description: Testing debt arises when tests are inadequate, missing, or not maintained.
This can lead to reduced confidence in the software’s quality and increased risk of bugs and regressions.
Example: Flaky tests that fail now and then. It means teams have to spend additional time to determine if it is a false positive
Technical Debt Causes
Despite best intentions, there will always be technical debts in software projects.
It is like the internal resistance in a battery. So the output a battery delivers is always minus its internal resistance.
Technical debt can sneak into your projects in several ways:
Rushed Development
When deadlines are tight, you might prioritise shipping features over writing clean, maintainable code.
Example: You copy-paste code instead of refactoring it into a reusable function.
Example: You take an informed decision as there could be a scenario where a dependent system might not be ready and you need to get a tactical fix done.
Lack of Documentation
Skipping documentation can speed things up, but it makes it harder for others (or future you) to understand the code.
Example: You skip writing comments and documentation thinking you’ll “remember it later.”
Poor Testing
Insufficient testing can lead to hidden bugs that need to be fixed later, adding to the debt.
Example: Developers write tests towards the end of writing a feature. So developers can miss key tests in the excitement of coding the next feature.
Ignoring Refactoring
As new features are added, fixing the codebase to accommodate them cleanly can create a tangled mess.
Example: You keep adding if-else conditions to handle new cases instead of redesigning the logic properly.
Example: To implement certain features, developers need to write boilerplate code. This gets duplicated across the code base. Using inheritance or other techniques, you can remove this code. But until then it adds technical debt.
Avoiding version upgrades
You need to update the versions of libraries or the used frameworks. Each version upgrade also creates a deprecation of some features.
Example: Certain methods and classes get deprecated whenever Java releases new versions. You need to update them to ensure your codebase is robust, as this could have security implications.
Lack of Developer Tooling
Developer tools like sonar and spotbugs can detect potential code smells and technical debt.
Integrating them into the deployment pipeline can help you mitigate many issues, as these tools can raise flags for common issues.
Consequences of Technical Debt
Just like financial debt, too much technical debt can become overwhelming. Here’s what can happen:
Decreased Productivity
Developers spend more time fixing bugs and understanding convoluted code rather than building new features.
Increased Bugs
A messy codebase is more prone to errors, leading to a more unstable product.
Higher Costs
The longer you wait to address technical debt, the more expensive and time-consuming it becomes to fix.
Team Morale Working with bad code is frustrating and can lead to burnout among developers.
Security Risk
Lack of upgrading to newer versions of libraries and frameworks can lead to security risks that can expose you or your organization to external threats.
Managing Technical Debt
So, how do you manage technical debt? Here are some practical tips:
Prioritize Refactoring
Set aside regular time for refactoring and code cleanup. Treat it as an essential part of your development process.
Better still, add code clean-up time as a factor in project estimations.
Example: Allocate a day each sprint for code improvement tasks.
Write Tests
Invest time in writing unit tests and integration tests to catch issues early.
Example: Aim for a certain percentage (> 90%) of code coverage and stick to it. Block builds that do not meet the defined criteria.
Document as You Go
As a team, you need to find and stick to one approach of documentation that works for your team.
That way, documentation becomes every developer’s responsibility
Example: There are many ways to document. — code comments, using MarkDown files, an external document system such as Confluence, Notion
Review Code
Conduct code reviews to ensure quality and catch potential issues before they merge into the main codebase.
Example: You can implement code walkthroughs that will help all the developers in the team understand your code and also provide feedback.
Communicate with Stakeholders
Make sure everyone understands the impact of technical debt and the importance of addressing it.
Example: Include technical debt discussions in your project meetings and reports.
Conclusion
In conclusion, technical debt is a natural part of the development process, but it’s crucial to manage it effectively.
Key lessons from this post are:
- Technical debt is inevitable. It is the nature of the beast.
- Key types of technical debt are — deliberate and inadvertent technical debts, architectural debt, code and testing debts
- Key sources of technical debt include — rushed development, ignoring refactoring, lack of documentation, and lack of dev tooling
- Technical debt affects developer productivity, which slows down the team and product. It also affects time to market, which can affect the organization.
- You can minimize technical debt by keeping a close watch on the code, documentation, dev tooling, and tests.
- Involving stakeholders in technical debt decisions ensures everyone knows about the interest we will pay for it.
It is everyone’s responsibility in the team to keep the technical debt to a minimum.
You follow the boy scout’s rule.
Leave your code better than you found it.
In every functionality that you code for, remove some existing technical debt. It also generates satisfaction that you have improved the codebase.
Are you a developer who needs feedback on the code you write?
Or do you want someone to review your code so that you know that you are doing the right things?
I help people with free code review sessions. DM me on Twitter (X) to organise one for you.
Top comments (0)