How to Avoid Technical Debt in the Age of AI-Generated Code

    Matt Watson
    By Matt Watson · CEO of Full Scale, 4x Founder, Author of Product Driven
    Updated 10 min read
    avoid-technical-debt hero, Full Scale
    In this article

    I’ve been building software for more than twenty years. Almost none of what I wrote still runs.

    My first apps were in Visual Basic 6. After that came classic ASP, plus a stretch where I was weirdly good at making websites work in Internet Explorer 6. At VinSolutions, the company I co-founded, we built a slick finance calculator in Microsoft Silverlight that everyone loved. I wrote a mobile app for car dealer inventory on Windows CE back in 2004, before the iPhone existed. Every one of those is gone, deprecated, or sitting in a repo nobody wants to open.

    A few years ago I wrote about this in my newsletter, and the whole point was that my entire career is now technical debt. It went viral on Reddit and Hacker News, I think because it named something every engineer quietly knows but nobody likes to say. Flag debt is the close cousin of the same problem, which is part of why feature flags matter more once AI writes the code.

    Given enough time, all of it becomes technical debt.

    So when a founder or a CTO asks me how to avoid technical debt, I give them the honest answer first. You can’t, not completely. What you can do is avoid the kind of debt that actually kills your product, and that job got harder this year. AI now writes code faster than any team can keep it clean. At Full Scale, the company I run today, this is a conversation I have with engineering leaders almost every week. A lot of that debt comes from solving problems you don’t have yet, which is exactly why most software scalability work is premature.

    This is a guide to the technical debt that matters, and how to keep it from burying you. Left alone, it is also what pushes teams toward a backend rebuild they do not need. When a rebuild is genuinely warranted on a .NET app, you hire senior ASP.NET developers who modernize it without a full rewrite.

    Why you can’t fully avoid technical debt

    Code rots. That isn’t a knock on the people who wrote it. It’s just what happens over time.

    Sometimes the language falls out of favor and you can’t hire for it anymore. (Go try to find a Perl or ColdFusion developer this week.) Sometimes the platform dies under you, the way Apple killed Flash and Silverlight by dropping support. And a lot of the time the business simply changes its mind and replaces the thing you built. I’ve watched all three happen to my own code more than once. If you want the full breakdown of the forms it takes, I covered what technical debt actually is in a separate guide.

    People always want a clean number for how long software lasts. There isn’t one. The U.S. Government Accountability Office (GAO) studied the government’s most critical legacy systems and found they ranged from about 8 to 51 years old. Agencies now spend roughly 80% of a $100-billion-plus federal technology budget just operating and maintaining what already exists. A bank’s mainframe can run for forty years. A startup’s front-end framework can be legacy in three. The shelf life depends entirely on the system, but the meter is always running.

    Christian Hammer, who founded Vala AI to help companies tackle exactly this problem, put it best when he came on my podcast to talk about technical debt. “Modernization is a treadmill,” he told me. “It’s like painting the Golden Gate Bridge. Once you’re done, you’ve got to start again.” Christian has spent years building tools that help companies see their own debt, and even he treats it as something you manage forever, not something you finish.

    Here’s why your code keeps turning into debt no matter how well you write it:

    What happensA real example from my career
    The language loses support and you can’t hire for itVisual Basic 6, Perl, ColdFusion
    The platform gets killed offFlash and Silverlight died when Apple pulled support, and our VinSolutions calculator had to be rewritten
    A better standard replaces your custom workAt Stackify we built our own profiling libraries for six languages, then OpenTelemetry made them pointless
    The business pivots or gets acquiredApps I built got scrapped when companies were bought and switched stacks

    The goal was never zero technical debt. It’s deciding which debt you can live with.

    Technical debt is the cost of shortcuts in your code, the rework you'll owe later for speed today. Some is smart and deliberate; some is reckless. You can't avoid it all, so the goal is to manage it, not pretend. Manage the debt; don't pretend you have none.

    The technical debt worth avoiding, because not all of it is

    The best definitions come from people who have actually paid the interest. Christian’s version is that tech debt is “wherever technology slows me down instead of helping me.” Mine is even simpler: it’s the inability to keep up with change. Back in 2021 I said it less politely on the show and called it all the stuff we should have done and didn’t.

    What most “avoid technical debt” advice misses is that some debt is fine, and some of it is even smart. Shipping a scrappy first version, a minimum viable product (MVP), to learn what customers want is not a failure of discipline. It’s how you find out if you’re building the right thing at all. As I’ve said before, nothing is technical debt just because it isn’t perfect, because nothing ever is. Cutting scope to ship and learn is usually the right call, which is the whole idea behind a sound MVP development strategy.

    The debt that kills you is a different animal. It’s the code in a language you can’t staff, the upgrade you’ve put off until it became a year-long project, the system only one person understands right as they hand in their notice. That kind compounds quietly, and the bill is bigger than most leaders expect. I broke down the real cost of technical debt and when it’s worth paying down in its own post.

    The line between the two is worth drawing clearly:

    Building a development team?

    See how Full Scale can help you hire senior engineers in days, not months.

    Debt you can live withDebt that kills your product
    A scrappy v1 you shipped to learn what users wantA codebase in a language you can’t hire for
    Boring, slightly dated tech that still does the jobAn upgrade you delayed so long it’s now a massive project
    A known shortcut you wrote down and plan to revisitA system only one person understands, and they’re leaving
    Code that isn’t elegant but works fineSecurity holes you can’t patch without rebuilding half the stack

    If you only fight the debt in the right-hand column, you’ll spend your time where it actually matters.

    Debt worth avoiding vs debt that's fine: reckless debt has no tests or docs, copy-paste everywhere, shortcuts you'll forget, and slows every future change, so avoid it; deliberate debt is a known, logged shortcut taken to ship on time, tracked and revisited, paid down on purpose, and that's fine.

    How AI is changing the technical debt equation

    For most of my career, debt piled up at human speed. AI changed the speed.

    Christian said it plainly on the show: technical debt is now “accumulating exponentially faster.” The data backs him up. Stack Overflow’s 2024 Developer Survey found that 63% of professional developers already use AI in their workflow. GitClear, which analyzed 211 million changed lines of code from 2020 through 2024, found that refactoring fell from 25% of changes in 2021 to under 10% in 2024, while copy-pasted code rose from 8.3% to 12.3%. You get more code, written faster, with less reuse, and that is the textbook recipe for debt.

    I say this as someone who uses these tools daily and tells our engineers to use them too. The problem isn’t AI. It’s how people lean on it. As I keep telling teams: AI is a powerful tool that can boost productivity, but relying on it to generate unexplainable code creates tomorrow’s technical debt today. The hard part of software development isn’t writing code. It’s understanding the problem you’re trying to solve, and that’s why the software engineering principles that matter most are about thinking, not typing.

    The fix is a mindset, not a tool. Treat AI like a junior developer. Christian compared it to an intern, opinionated and confident and often wrong, and that’s exactly right. You wouldn’t merge a junior’s pull request without reading it, and you shouldn’t merge the AI’s either. The anti-pattern I can’t stand is the one where someone tells the AI to keep trying things until all the tests pass. That isn’t engineering. That’s guessing with extra steps. For the fuller picture of where AI genuinely helps and where it doesn’t, I wrote about the real impact of AI on software development.

    AI didn’t change the rules of technical debt. It just made you rack it up faster.

    AI ships more, refactors less: refactoring fell from 25% of code changes in 2021 to under 10% in 2024. AI ships more code, but debt is piling up.

    How to avoid technical debt: 7 practices that actually work

    You won’t get to zero, and you can’t prevent technical debt entirely, but you can stay out of the danger zone. Here are the seven things I’d tell any engineering leader to do.

    1. Decide which debt you’re taking on, on purpose. Strategic debt you chose and wrote down is manageable. Accidental debt nobody tracked is the one that bites. Make the tradeoff out loud, then move on without chasing perfect.
    2. Stay on the upgrade treadmill in small steps. Our worst project at Stackify was getting stuck on an old version of Elasticsearch. We kept kicking it down the curb until catching up became a months-long, almost year-long slog. The same trap shows up in a .NET 4 to .NET 7 jump. Small, regular upgrades are annoying. The skipped ones turn into emergencies.
    3. Build a team that can maintain it, not just ship it. The cheapest contractor who disappears mid-sprint leaves you holding all the debt, which is exactly the trap I call cheapshoring. I’d rather hire for curiosity than raw speed, because the engineer who asks why tends to write code the next person can actually live with. That instinct is core to the Product Driven approach I write about, and it’s what we screen for when we staff a custom software development team.
    4. Kill the keystone risk by spreading what’s in people’s heads. Bit rot sets in when the one person who understood a system walks out the door. Write things down, rotate who touches what, and make sure no critical piece lives in a single brain.
    5. Make technical debt a visible, budgeted line item. Put it on the roadmap and fund it like real work, because it is. Teams that hide their debt as a guilty secret never get the time to fix it. Teams that name it openly actually do.
    6. Review AI-generated code like a junior’s pull request. Read it, keep it Don’t Repeat Yourself (DRY), and refactor the duplication before it spreads. This is the direct counter to the cloning trend in the GitClear numbers above, and it costs a fraction of cleaning it up later.
    7. Close the gap between business and tech so you build the right thing once. Most rewrites happen because someone built to a misunderstood requirement and had to redo it a month later. Ask why before you write a line. A modern, well-run software development process exists largely to stop that waste at the source.

    The honest goal is to manage technical debt, not pretend you’ll avoid it

    Everything I’ve built will eventually be replaced, and so will everything you build. If you’re lucky, your code survives long enough to become technical debt to someone else. That’s not a sad ending. It means the thing worked long enough to matter.

    Maintainable code comes from senior engineers who stick around, which is the quiet reason debt gets out of hand in the first place. People leave, context walks out with them, and the next hire patches what they don’t understand. Our developer retention runs over 93%, so the person who knows how your system works is still there next year. That’s also why I push companies toward staff augmentation and long-term dedicated teams instead of throwaway project shops. Make sure you work with a dev partner that cares about your product, not just your project.

    AMC Theatres is the example I point to. Their developers in the Philippines aren’t treated as a vendor on the other side of a wall, they’re treated as real AMC engineers on the same standups and the same roadmap. As their CIO, Derrick Leggett, puts it: “It’s a fully integrated team. It’s just some of the people happen to be living in the Philippines.” A team that owns the codebase like that is the best defense against debt I know.

    Keep it simple and boring until you can’t, and keep the people who built it close. That’s most of the battle.

    How to avoid most technical debt: review every change, especially AI-generated code; keep tests and docs current, the cheapest debt insurance; budget time to refactor and don't let it fall to zero; and log the debt you take on, since deliberate beats accidental.

    Frequently asked questions

    How do you avoid technical debt without slowing down development?

    You don’t try to eliminate it, you manage which kind you take on. Ship fast where the debt is cheap and reversible, like a first version you’ll learn from, and slow down only where the debt is dangerous, like an architecture choice you’ll be stuck with for years. The slowdown isn’t the goal, drawing that line is.

    Can you eliminate technical debt completely?

    No, and chasing zero debt is its own kind of waste. All code eventually ages out as languages, platforms, and business needs change. The realistic target is keeping debt visible and paying down the parts that slow your team or block your roadmap, not pretending a perfectly clean codebase is achievable.

    How is AI changing technical debt?

    AI lets teams produce far more code, far faster, which means debt now accumulates faster too. GitClear’s analysis found refactoring dropping and duplicated code rising as AI use spread. The defense is to treat AI like a junior developer whose work you review, refactor, and keep DRY, rather than a coder you trust unread.

    What’s the difference between good and bad technical debt?

    Good debt is a conscious shortcut you chose, documented, and can revisit, usually to ship and learn sooner. Bad debt is the kind nobody tracked: code you can’t hire for, upgrades you can’t make, or systems only one person understands. Good debt buys you speed now. Bad debt quietly takes it back later, usually at the worst possible time.

    Key takeaways: you can't avoid all technical debt, the goal is to manage it; not all debt is bad, deliberate and tracked debt is fine; AI ships more code but refactors less, so debt is piling up; review changes, keep tests current, and budget time to refactor.

    Stop letting technical debt pick your team for you

    The biggest source of unmanageable debt is turnover, when the people who understood your code leave and the next ones guess. If you want senior engineers who stick around and write code your team can live with, book a discovery call and we’ll talk about what your roadmap actually needs.

    Get Product-Driven Insights

    Weekly insights on building better software teams, scaling products, and the future of offshore development.

    Subscribe on Substack

    Ready to add senior engineers to your team?

    Book a 15-minute call. Tell us your stack and where the gaps are, and we'll show you the engineers we'd put on your team.