10 tips for reducing and then preventing tech debt

Technical debt might be the most widely relatable problem in software engineering. We collected ideas for paying it down and then keeping it at bay, with special attention to how this world is changing in the era of GenAI.

Technical debt might be the most widely relatable problem in software engineering. Even if you don’t work in software engineering, you can think of a problem in your life that keeps cropping up. Maybe it’s an issue with your car, or a leak in your roof, even an argument that recurs in a relationship. You know that you should invest the time and energy in finding a permanent fix, but keep avoiding that hard work, finding ways to patch things up that allow you to ignore the issue for a while, but just make the problem bigger and more complex over time. That’s what tech debt is for the world of software development. It’s no surprise then, that tech debt remains the number one frustration experienced by developers in our annual Dev Survey. It’s a common pain point because, arguably, technical debt is inevitable—some developers joke that all code is technical debt. Some say this with a straight face. When a developer writes code, they build in assumptions about best practices, the state of the application, and the best tools for the job. But those assumptions can change—developers are always learning, and in the face of new information, developers may find that the code they thought was good may be holding them back. Eventually, the debt can overwhelm the entire project, where all your time gets spent working around the issue instead of investing the time to fix it.

Sometimes teams intentionally take on technical debt. Like financial debt, technical debt can help a project get to market faster. Again, that debt will cause issues in your development cycle as devs work around the old choices. Think of it as interest: it’s the work you do to maintain the debt without eliminating it. If you let it go too long, then that maintenance work is all the developers will be doing—tech bankruptcy if you will.

We wanted to examine the issue of tech debate and highlight how today’s technologies, including GenAI, might play a role. A number of experienced software developers and leaders of tech organizations have voiced concerns that the increase in code generation made possible by today’s AI might result in tech debt accumulating far more rapidly. And of course, it’s not just extra code generation that will influence this outcome. As many companies look to integrate LLMs and GenAI into their tech stack, they are adding new data pipelines, vector databases, and API gateways, all of which need to be well integrated with existing systems to avoid incurring major tech debt along the way.

Causes of tech debt

For software developers, technical debt can crop up in different ways.

  1. New version, who dis? One of the biggest sources of tech debt comes when the programming libraries, frameworks, and libraries that your software relies on upgrade to include new features and fixes. These new versions may require changes in how your software uses them, which can be easy to ignore if the old version works for you. But those new features and fixes can suddenly become mission-critical.
  2. Outgrowing old tech and techniques. What once was an asset can become debt once your user requirements change. You may start to see the strain as more customers sign on, making you a victim of your success. You can try workarounds (see below), but eventually that bill comes due.
  3. Taking a shortcut while creating something new. When developers choose quick solutions instead of well-thought-out ones, they create tech debt. You may get the project done faster, and even be recognized and rewarded in the moment for speed delivery, but down the road you will face bigger problems that require more time and resources to fix.
  4. Letting small problems pile up. Let’s say you didn’t take any shortcuts with your work. No matter how careful you are, small problems are likely to emerge over time, especially as your team and codebase grow. If you don’t set aside time to regularly address these nagging issues, they can metastasize into much bigger worries.
  5. Opting for a quick fix over a permanent solution. When you put a bandaid on a problem, solving the issue for a few days or week, but ensuring the overall problem just gets more complex, you’re creating tech debt. It’s like borrowing money with high interest—while you get what you need now, you’ll have to invest more work and resources later, potentially slowing down future progress.

We recently published a blog post from a senior developer that reveals the mindset and choices which can lead to tech debt:

There’s nothing quite like the rush of starting a new job and whipping out projects, particularly at an organization where technology is not generally prioritized. Management falls head over heels for your ability to stand up and deploy marketing requests, and as you crank out project after project, it seems like you’re going straight to the top.

Then one fateful day, an urgent request comes in: “We have a real opportunity to capture some immediate sales, but we don’t have an avenue for tagging and remarketing to these leads. What can you stand up before the week’s end, in time for the conference? Surely, you can get something set up?” They stare back at you on a Teams call while you archetype an API service to accommodate the traffic. With your coworkers depending on you and your hubris swollen from recent victories, you make a choice. To meet the deadline, you turn to another language or framework to stand this up, because you are a go-getter and a wizard. As you quickly deploy the dirty codebase, and as your accolades are sung from on high, you think, “I’ll lace this up with our core later. They needed it now—and I did it.” You have reached peak developer-nirvana; none dare question your skill.

Months later, marketing and management requests have continued non-stop and (of course) you’ve had no time to lace everything up. You think back to that fateful decision to implement a quick fix, not anticipating that the organization would utilize it on a daily basis, requiring constant updates for every unique sales avenue. In your haste, you built a system that is functionally not operable within the rest of the ecosystem—and you are now subject to that decision. As the requests take longer and longer to work, questions start to arise: “Is our developer losing his touch? Why is this taking so long when it used to take minutes?” Instead of articulating the issues or communicating the challenges, you keep applying patches and quick fixes. As the choices you’ve made in your haste pile up into a series of compounding issues, your turnaround time slows, but the requests for new projects don’t.

This is a very real story and one that I’ve been party to many times. It’s so enticing to push projects out the door, to woo and impress colleagues and supervisors, but the stark truth is that even the smallest projects should have proper review periods and attention. These daily choices of cutting corners and improperly aligning architecture, all without a real understanding of the organization’s business goals, can create a bad situation within small teams. This is particularly true in companies where the focus is not technology and where developer resources are spread across requests from sales, marketing, and engineering. It’s a tricky place to be as a seasoned professional, much less as a developer early in your career. Do you push the envelope or do you push back?

Tech Debt in the Age of GenAI

Technical debt has always been a part of software development. But as systems become more complicated, sprawling across hybrid clouds and dozens of microservices, it can become harder to unwind a tangled problem. With the advent of GenAI, engineers are capable of producing more code in a short time, but that doesn’t necessarily mean they’re building a high-quality product.

As Itimar Friedman, co-founder and CTO at the startup Codium says, GenAI can have both a positive and negative impact on tech debt. “Two different customers told me they're using code generation. In the beginning it's a lot of excitement, but when they really try to analyze the effectiveness, they think it kind of sums to zero,” Itamar told us. This raises an important question: what is the real ROI on AI systems that can generate a lot of code quickly, with some portion of that code being subpar? “Our first goal is not helping you generate more code,” says Itamar, “but rather making it easier for you to think about your code and verify your code. Maybe you generate less code, but it’s higher-quality.”

In the GenAI era, companies undoubtedly have the ability to generate code more quickly. Lines of code, however, don’t necessarily equate to better business logic or product performance. “One thing everyone can agree on, is that having code with fewer bugs is a long term benefit,” says Itamar. Think of it this way: rather than building a bigger airplane, build one that costs less to maintain and repair over time. Rather than using GenAI to double the amount of code you produce, generate the same amount of code, but ensure that what you have is far less likely to fail, given failures can be enormously costly.

In other words, the impact of GenAI on your level of tech debt depends on how you use it. If you pump out a lot more code without taking the time to check its quality or implement good practices around testing and automation, you are probably not producing any real productivity gains in the long term. On the other hand, if you use GenAI to improve the quality of your code, reduce the number of bugs, and expand the detail of your documentation, then you are setting yourself up to deal with less tech debt in the future.

Reducing debt that exists

So, what are some simple solutions you can use, either as an independent contributor or manager of engineers, to address existing tech debt?

1. Prioritize and triage your debt, just like sorting your laundry. Not all tech debt is equally urgent. Some issues can wait, while others might cause serious problems if ignored. Like sorting laundry into piles of whites, colors, and delicates, start by identifying and prioritizing the most critical areas. You can spot critical debt by seeing which pieces of code get the most commits—or just listening to your engineers complain. (Manager)

2. Refactor as you code. Whenever you touch a piece of code for a feature or bug fix, consider if it could be refactored. Refactoring takes an understanding of the context of the code, so polishing up some code while you’re there helps cut down the number of interventions. Regular refactoring helps eliminate accumulated tech debt, making the code easier to work with and preventing bigger messes down the line. (IC)

3. Allocate time for tech debt repayment in sprints and regular workflows. Just as you’d budget a portion of your income to pay down financial debt, dedicate a portion of your development time specifically to reducing tech debt. This steady approach keeps the debt from growing out of control and helps ensure long-term stability. (manager)

4. Automate where possible—think of it as hiring a robot to do chores. Automation tools can help identify, track, and even fix some types of tech debt. It’s like having a robot vacuum clean your floors while you focus on other tasks. By automating routine maintenance, you free up time to focus on more complex issues.

5. Educate your team—like teaching everyone in the household to clean up after themselves. Ensure that everyone on your team understands the impact of tech debt and how to avoid creating more. Just as a clean home is easier to maintain when everyone pitches in, a well-informed team can prevent new tech debt from accumulating while helping to reduce what’s already there.

Preventing Debt From Occurring

All right, you’ve done your spring cleaning. But is there anything you can do to prevent things from getting quite so messy before next spring rolls around? Below are a few simple ideas for preventing tech debt from building up in the first place.

1. Write clear documentation—like leaving a map for others to follow. Comprehensive documentation is like a well-drawn map that guides developers through the codebase. When everyone knows how and why certain decisions were made, it’s easier to avoid missteps that could lead to new tech debt. Good documentation ensures that future developers won’t get lost and accidentally create more debt.

2. Adopt a “pay it forward” mentality—always leave the code better than you found it. Just as campers are encouraged to leave their campsite better than they found it, developers should strive to improve the code whenever they touch it. Even small improvements, like fixing a minor bug or refactoring a messy function, can keep the codebase in good shape and prevent tech debt from creeping back in.

3. Implement code reviews—like having a second set of eyes double-check your work. Regular code reviews help catch potential issues early, before they turn into tech debt. It’s like having a friend proofread your essay: they might spot mistakes or suggest improvements you wouldn’t have noticed on your own. This collaborative approach helps maintain high code quality and prevents debt from building up over time.

4. Test early and test often. A comprehensive test suite can spot failures pretty easily. In fact, writing good tests can clarify the design of code and force you to follow best practices. Bad code is often harder to test, so increasing your test coverage may spur better refactoring.

5 Align with business objectives If you can demonstrate that dealing with tech debt will contribute to revenue growth, shrink losses, boost profits, or improve hiring and retention, it will be far easier to get approval for the time and resources needed to work on this problem.

Stack Overflow has been around for 15 years, and we’ve shared many thoughts over the years on dealing with tech debt. These days we rely on Stack Overflow for Teams to ensure we’re building an accurate, up-to-date knowledge base that will allow future employees to understand how and why things were built and how best to keep them running smoothly.

Get the checklist

Download