Paying off technical debt

Now and then, developers are forced to make a compromise. They commit a quick fix to a project without tests. They ship a new module without proper documentation. They add a feature to production to satisfy a client request and deploy at 4:55pm on a Friday.

Each and every one of these compromises is debt. It’s shifting the burden of some development from now to the context of a future development team.

Just like taking a financial loan – financial debt – is shifting a monetary burden from now to the future, making this kind of compromise is taking on technical debt.

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. – Ward Cunningham

Every team and every project has technical debt. It comes with the territory when you start building software. Usually, the term “technical debt” is seen as a negative … but that’s not always true.

How Bad is it?

In business school, one of the more interesting things I learned was how debt could supposedly be a good thing. In personal finances, debt is negative; in corporate finances it’s a necessary tool used to help grow the business.

Likewise, technical debt is not always a bad thing. Instead, it’s a powerful tool in your team’s arsenal that helps you ship faster, iterate more quickly, and launch products to your customers’ delight.

if you’re not embarrassed when you ship your first version you waited too long. – Matt Mullenweg

With any project, cutting corners and making compromises is sometimes a requirement in order to get something shipped out the door. It’s never perfect and there’s often a laundry list of items left on the team’s to-do list for the next version.

The negative aspects of technical debt occur when this list of to-do items grows and affects the overal stability and security of the application itself.

Security Implications of Technical Debt

A large, complex codebase can hide security issues. It’s easy to pass data around through the lifecycle of an application and, the longer it lives in memory, forget where it came from. Determining whether or not a specific variable was properly escaped earlier in a program’s execution can become difficult.

More often than not, though, the security impact of technical debt is far more subtle.

Almost every project has external dependencies. Libraries we import to save time writing code. External system with which we integrate to flesh out a platform. The underlying operating systems and languages atop which we build. Every moving part added to an application is a compromise – it’s trusting that some other development team cares as much about our application’s security as our team does.

And trusting that our development team will keep these dependencies up to date.

Systems that require shortcuts or compromises to work are often very specialized to their purpose, incredibly inflexible or difficult to upgrade, and huge potential security risks within your organization.

An upgrade might mean 4-6 hours of downtime for your system will various other bottlenecks are worked out; it’s easy to rationalize deprioritizing this upgrade to keep a project moving. That compromise is technical debt and is also a major risk. Consider instead the potential downtime incurred if a security vulnerability is discovered – and exploited – because you didn’t upgrade promptly!

Solutions for Getting out of Debt

Unfortunately, technical debt is unavoidable. Your team will, one day, make a necessary compromise in order to get the code for a feature out the door. Remember, this is not a bad thing but is still something you want to keep track of.

Like financial debt, if technical debt is not paid off over time it will accrue interest. This interest often comes in the form of problematic legacy code and even longer lead times for refactoring or bug fixing.

Instead, keep track of these compromises when they happen and immediately make plans for revisiting your code in the future to reduce the impact of the debt you’ve just incurred. This could mean devoting a future sprint to addressing a feature request from a different angle or intentionally scheduling downtime to upgrade a critical system. Make room in your development schedule to implement long-term solutions to the near-term shortcuts you’re taking as you take them.

Additionally, take time to budget for refactoring and writing tests to increase overall code coverage of your application. Just like you’d budget to pay off a loan, commit a set percentage of development time each sprint to paying down your technical debt. Add new documentation, refine integration tests, refactor complex methods, upgrade library dependencies. Each of these changes will help your team cultivate a stronger, more maintainable code base.

Intentional Coding

Incurring technical debt should always be an intentional decision. Discuss priorities with the team and work together to find agreement on the best way to get your product out the door. Commit as well to following up on any deprioritized work so it doesn’t just disappear into the aether. Intentional technical debt can get your product in front of your customers before the competition. Intentionally paying off that debt can keep your product stable and secure long into the future.