Technical debt, or tech debt, is a development concept that applies when a team chooses the easiest-to-implement solution for short-term simplicity instead of choosing the better solution that requires more work up-front. Over time, every organization accumulates tech debt, likely because of budget and resource constraints, but it can't go unaddressed for too long.
When you decide that it's time to start eliminating some of your technical debt there's one rule you can't break: fixing technical debt can't create new problems.
Businesses that decide to reduce tech debt are paying an opportunity cost. They're giving their team the go-ahead to refactor existing code for long-term improvement.
Oftentimes, feature requests and existing bugs get pushed aside while technical debt is being addressed. That's okay because taking care of technical debt is necessary and unavoidable.
However, you absolutely must avoid creating new bugs or breaking features along the way.
In the medical field, professionals must take an oath to do no harm. While you might be working with code instead of a living patient, it still applies.
It can be hard enough to convince a business to take care of its technical debt. Start cleaning up code, which is in many ways an "invisible" activity to those outside of your department, and see how they respond when you start creating problems.
If a business gives their team a chance to improve code, and what they get in the process is user-facing defects, they aren't going to be so quick to give their team that opportunity again.
The million-dollar question is this: How can you address tech debt without killing quality?
In other words, how can you hold to your oath to "do no harm" when performing code surgery?
Let's explore the best ideas.
The best way to avoid bugs and problems when addressing tech debt is simple on paper: create a safety net.
Your team needs to consider what tests or processes they have in place to help catch mistakes that your team misses. At the very least, you need to make sure that any changes you make will not impact the major use cases you've identified.
For instance, if most people use your budgeting application's automatic sync, you need to make sure that stays functional.
On the other hand, if very few people use the app's obscure interest reporting feature, then it's lower on the priority list. Clearly defining the major use cases of your application is essential to making sure that you can adequately test the app as you clean up the code.
While your team is creating a safety net, you need to think about what "pinning tests" you can identify. Pinning tests clearly define the behavior of your system, whether it works correctly or incorrectly.
You'll use these pinning tests to track how the changes you're making impact the behavior of the code.
Snapshot testing, UI testing, Fiddler, Postman, and regression testing are all valuable when it comes to designing your safety net before you move forward with changes.
Your safety net contains many basic tests that ensure you aren't accidentally altering the behavior of your application.
The next step is to prepare your application so that isolated testing is easier. Tightly coupled applications pose a major challenge for development teams that are looking to clean up code.
Dependencies with databases, file systems, web services, and elsewhere might be completely unrelated to what you want to test, but they can still get in the way.
If you have a tightly coupled application, consider encapsulating calls inside of a mock implementation of your interface and do your testing there.
This makes it easy to test the correct logic while isolating the code itself from the rest of the app.
Look more closely at the inversion of control (IoC) or Dependency Injection if you're unfamiliar with this method.
Once you have created your safety net to help prevent problems from slipping by your team and prepared your application to make testing as easy as possible, it's time to get to the good part.
Refactoring code without killing quality means employing a few fundamental techniques.
Intelligent tooling is the first thing you need to implement and many modern dev environments have these tools built right in.
Ideally, you'll use these tools to automate mistake-prone processes, like extracting variables or pulling up methods.
Feature flags are also valuable and they allow you to keep a backup version of the original logic, then route the application to either the new or old version based on what's working.
Generally, you'll route all traffic to the new version unless you start running into issues.
Once you verify the new version works as you want it to, you can remove the flag in your next release.
Whatever methods you decide to employ when addressing tech debt, as long as you keep the cardinal rule front-of-mind, everyone will be better off.
Are you still running on legacy applications? Do you have tech debt weighing you down?
Adservio can accelerate software delivery, modernize your legacy apps, optimize performance, boost resiliency, and help your organization escape the weight of technical debt for good.
Interested in learning more about how Adservio can assist you? Contact us today!