All of us who make a living out of developing software know how hard it is to produce high-quality applications. The day-to-day life of an engineer features many hard decisions and trade-offs, often with less information than we'd like. Trying to reduce technical debt often seems like an uphill battle, but it's doable with the right attitude and set of practices.
In this post, we'll share seven suggestions for reducing technical debt in your organization. They're simple—though not necessarily easy—and highly effective, and they'll generate more value, especially if adopted together.
Table of Contents
- Technical Debt Fundamentals
- Reducing Technical Debt: 7 Practical Steps
- Reduce Technical Debt or Lose Real Money
Technical Debt Fundamentals
What is technical debt after all? Why should you reduce it? Let's tackle these fundamental questions before sharing our list of tips.
What Is Technical Debt?
Technical debt was originally a metaphor coined by Ward Cunningham. The idea is that we acquire debt in a similar way to which we acquire financial debt. If I take a loan to start a business, that loan accelerates the launch of my business, but I'll have to pay off interest later.
Technical debt is similar. I can deliberately postpone performing a task that I know will result in higher quality in order to gain speed to market. I pay that interest later in the form of additional work while working on my application due to the less optimal solution taken.
According to Cunningham's definition, it becomes obvious that technical debt can't mean bugs I introduced due to a lack of knowledge or experience—after all, contracting a debt is something you do willingly. Also, technical debt doesn't mean doing things poorly.
However, languages are alive and always changing. As I understand, most people use "technical debt" differently than was originally intended. The term has seemed to generally morph into a synonym of "problems in the code." So, for all purposes, this is the meaning of technical debt I'm using for this post.
Why Is It Important to Reduce Technical Debt?
Unpaid technical debt accrues more and more interest. The longer it takes you to correct the suboptimal approach—in the original definition—or to fix the defects and problems accumulated—in the colloquially used definition—the harder it will become to fix those in the future, to add more features, and to further improve the application.
Technical debt makes your team go slower, increases the likelihood of them introducing defects, and makes "doing the right thing" even harder in the future.
Reducing Technical Debt: 7 Practical Steps
With the fundamentals out of the way, let's walk you through our list of steps to reduce technical debt.
1. Embrace Automated Testing
Automated testing is a staple of modern software development practices, and it's a tried-and-true way to identify defects in the code as early as possible. More importantly, a suite of automated tests with comprehensive coverage can work as a regression suite, letting engineers know when things that were previously working break.
Automated testing—especially unit testing—is like a gift that keeps on giving. Not only can it enhance quality on its own, but it also serves as a backbone for other practices that further improve quality, such as code refactoring.
2. Adopt Code Review Practices
Nowadays, it looks like defending code reviews is anathema in some software development circles. Well, I'm prepared to die on that hill: code reviews are awesome, and you should do them.
Just to make things clear, when I say code review, I mean a process in which changes are only accepted into the mainline after an asynchronous review where one or more engineers evaluate the code and give feedback on it. Nowadays, reviews usually happen during the pull request process, but reviews are certainly possible even in scenarios without PRs.
In my experience, code reviews can bring many benefits:
- exchange of knowledge and experiences—Code reviews are perfect opportunities for discussion and learning, and often even the reviewer will learn something.
- collective ownership—Knowledge about specific classes and modules gets spread easily, facilitating collective ownership of code.
- fewer bugs and performance issues—An attempt reviewer, having fresh eyes, can catch things that the original author couldn't see.
I've written professionally, and I say having an editor is great. It improves my writing in ways I wouldn't be able to do alone. The same goes for having a great reviewer.
3. Adopt Pair Programming/Mob Programming When It Makes Sense
An alternative to code reviews many teams adopt is pair programming, or mob programming when it's performed by more than two engineers. Some people go as far as saying this is the only correct way to produce high-quality software as a team. I'm not that dogmatic, but I can see plenty of benefits in pair/mob programming:
- very short feedback cycles—Your pair is right there with you and can give you immediate feedback.
- simple design—A pair can keep you on track and prevent over-engineering, especially if you also employ test-driven development, which has an emphasis on tiny steps and simple design.
- exchange of knowledge and experience—Similar to code reviews but in a more immediate fashion, pair programming enables the sharing of experience, knowledge, productivity tips, and more. It's particularly effective to transmit knowledge from senior to more junior members of a team.
4. Create a Culture of Constant Refactoring
Refactoring means changing your code in a way that doesn't change what it does. It's another cornerstone of modern software development and is a key practice in agile methodologies such as extreme programming.
LinearB tracks how much effort your team is putting into new work, refactoring and rework, so you ensure you're addressing technical debt regularly.
If you refactor your code constantly, it'll become easier to maintain, understand, and change. Refactoring reduces the complexity of the code, getting rid of duplication and other issues. However, refactoring can be risky. After all, you're changing code that already works.
How can you do it safely?
The answer is automated tests. That's why automated testing is the first practice on our list: it enables other practices and, as such, is essential on a journey toward CI/CD/DevOps.
5. Track and Improve the Right Metrics
Engineering metrics are important, but teams often get sidetracked chasing the wrong ones. If your organization tracks and improves the correct set of metrics, it will be empowered to acknowledge, track, and pay off its technical debts. But which metrics are the correct ones?
Here's a set of five suggestions:
- defect escape rate
- lead time
- cycle time
- cumulative flow
- deployments per day
6. Understand and Treat the Root Cause (at the Organizational Level)
This is certainly the hardest of all items on this list. You see, all of the practices we've been listing so far are important and really effective. You should adopt them even—or maybe particularly—when starting a pristine new team and codebase.
However, they reside at the tactical level. They address symptoms. If you want to really address technical debts and quality issues in general, you have to go deeper and find the root cause of these issues. And the root cause is usually organizational.
- The sales department makes wide promises to clients and then causes pressure on the development team.
- Management doesn't allow engineers to adopt best engineering practices such as testing/code review/source control—believe me, this happens!
- Lack of prioritization—everything is always urgent, causing developers to be constantly switching contexts, which only adds to the overall cognitive load of software development.
7. Listen to Your Engineers
Finally, if you want to improve the quality of your application, listen to your engineers! They are the ones who actually develop the application, which means they have a really good grasp of what's wrong with it and what could be done to improve it. Listen to what your engineers have to say, and consider actually taking their suggestions!
The vast majority of engineers are smart, caring people who truly believe in doing high-quality work. In our free time, we read books and papers, write blog posts, and contribute to open source. We give and attend talks on topics we're passionate about, and we mentor newcomers to the field.
Your developers want to do great work. Let them.
Reduce Technical Debt or Lose Real Money
As I said earlier, technical debt makes your team go slower in the future. It can result in more bugs being introduced, it makes adding new features harder, and it also demotivates engineers.
Technical debt might be a metaphor, but the money you lose because of it is very real. Reduce technical debt, make your engineers happier, and stop wasting value.