Co-authored by: Emily Kelly
Imagine you’re cleaning your kitchen because you’re expecting guests. You look at the clock and realize they’ll be here any minute. You don’t have time to really clean the floor, so you sweep the dirt under the rug. With the rug back in place, your kitchen is presentable — but it’s not actually clean. You have every intention to go back and properly clean the floor, but, after your guests leave, you realize you have a billion other things to do and that dirt ends up staying right where it was.
Besides being a relatable story for more of us than we’d might like to admit, this is a decent analogy for something that negatively affects most developers and codebases at some point or another. That thing is technical debt.
What is technical debt? Techopedia defines it as “a concept in programming that reflects the extra development work that arises when code that is easy to implement in the short run is used instead of applying the best overall solution.” It’s what happens when you allow (or are forced into) short-term solutions that deviate from your original plan. The term came from a programmer named Ward Cunningham, who likened software development to financial debt. He described it 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. Entire engineering organizations can be brought to a stand-still under the debt load of an unconsolidated implementation.”
If technical debt is so harmful, why does anyone ever let it happen? It’s often due to external factors. Perhaps the project schedule is at risk, or the developers didn’t estimate well and have burned through the allotted time for a feature, or maybe the client needs to cut budget. You know it’s coming when you hear phrases like:
“We’ll fix it after the release.”
“This is good enough for now.”
“This isn’t done, but I can’t spend any more time on it.”
Truth be told, technical debt isn’t inherently bad. It can be introduced as part of a valid and calculated trade-off. Sometimes, getting something that’s “mostly done” out the door is worth it from a competitive or market share perspective. What gets developers into trouble is not circling back right away to refactor the code that created the debt. Inertia takes over, new code is introduced around the suboptimal code, and all of a sudden what would’ve been a simple refactor balloons into something much snarlier.
Intentional vs. Unintentional Technical Debt
As a developer, you’re not usually an unwitting victim of technical debt. A lot of it comes as a consequence of choices. This “intentional” debt usually occurs when a development team runs into unexpected constraints or pressures. It happens when programmers take shortcuts and deviate from plan to accommodate those constraints, whether that’s not writing unit tests, abandoning architectural best practices, duplicating code that should be modularized, or not using defined patterns.
Technical debt can also be a nasty surprise, or “unintentional.” Perhaps you have a junior programmer whose lack of experience results in bad code. Or you contract with a developer who doesn’t stick to your coding standard. Or you start the project off with unclear specifications or unhealthy team communication, and you end up with a churn-heavy thrash fest where nothing gets implemented the way it was intended.
Why it’s a problem
Technical debt isn’t a problem for the project in which it’s introduced, it’s a problem for the projects that come after. It causes development to slow down, trivial tasks take longer to fix, and code becomes more and more brittle. Having to work around suboptimal code means your development team is more prone to missing deliverables. You run a good chance of coming across unanticipated regressions or dependencies, and if the release does go live before you catch those bugs, you risk a bad user experience. Even if your releases miraculously stay unaffected, you’ve likely got a team of increasingly frustrated and dissatisfied developers on your hands. Nobody loves working around stinky, suboptimal code.
How developers can help manage technical debt
- Talk with clients or stakeholders about why good code takes time, and the tradeoffs involved in taking shortcuts. Transparency can help fend off the circumstances that lead to technical debt. Even then, someone may make a business decision to intentionally cut corners to bring a product to market faster. In this case, make sure there’s a debt mitigation plan in place. Again, the debt itself isn’t the problem, it’s failing to revisit it promptly. Build in the mitigation plan from the start, and insist on following it after launch.
- Have clear development standards and stick to them. Deadlines and other pressures can make passing “good enough” code to QA very tempting, but raising your team’s standard of what quality code looks like can help keep code that incurs technical debt from creeping in.
- Get more eyes on the code. Establish a code review process and ensure more than just the developer who wrote the code looks at it. Do paired programming when you can, especially on critical or complex features. Developers will challenge other developers on code that doesn’t smell good. More eyes almost always leads to a better output.
- Test thoroughly. Build in time to write unit tests. Run them. They are your canary in the coal mine that can help alert you when bad code is affecting your implementation.
How clients and stakeholders can help manage technical debt
- Think about technical debt as being in the same nasty family as financial debt. It’s cumulative and crippling, even if everything looks fine from the outside. Decide you don’t want to allow it to affect projects you care about.
- Read through the section above and support your developers in executing on projects in a way that will minimize debt-incurring code. Better yet, demand that they execute this way.
- Before any project that enhances existing code, question your development team: Do we have anything ugly in our current code base we should address first before we take this on? If they say yes, talk frankly about priorities. Revisit that mitigation plan.
Technical debt is ugly, but it can be managed. The secret is to be acutely, painfully aware of it and address it quickly. This is no time to bury your head in the sand. It may mean prioritizing this behind-the-scenes debt mitigation work ahead of sexier client-facing work, which is frustrating from a business perspective. Almost always, it means refactoring existing code, which can be hard to accept from a development perspective. But it’s better than the alternative: doing nothing, which will almost assuredly cause you bigger and more complex headaches later on.