How AI Is Accelerating Technical Debt (And What to Do About It)
How AI Is Accelerating Technical Debt (And What to Do About It)
I've been using AI coding assistants daily since early 2024. Copilot, Cursor, Claude, the works. My personal output has roughly doubled. So has the rate at which I introduce technical debt.
That's not a contradiction. It's the core tension of the AI-assisted development era. AI makes it faster to write code, and it makes it faster to write the wrong code. The same tool that helps me scaffold a service in 20 minutes also generates 300 lines of plausible-looking code that I don't fully understand.
This isn't an anti-AI article. I'm not going back to writing everything by hand. But I've learned that AI changes the technical debt equation in ways most teams aren't accounting for.
How AI Creates New Categories of Debt
1. Comprehension Debt
This is the big one. When you write code yourself, you understand it. When AI writes code for you, you understand it partially. That gap between what's in the codebase and what the team actually understands is comprehension debt.
I reviewed a PR last month where a developer had used Copilot to generate a complex caching layer. It worked. The tests passed. But when I asked why the cache invalidation used a specific TTL strategy, they couldn't explain it. The AI had chosen it, and it seemed reasonable, so they shipped it.
Two weeks later, that caching layer caused a subtle data consistency bug that took 3 days to diagnose. The fix was a one-line change, but finding it required understanding a design decision nobody on the team had consciously made.
2. Consistency Debt
AI assistants generate code based on patterns in their training data, not patterns in your codebase. Ask the same AI to solve similar problems in different parts of your app and you'll get different approaches. Different error handling patterns. Different naming conventions. Different architectural decisions.
Over time, this creates a codebase that looks like it was written by 50 different people with 50 different opinions about how to structure code. Because it was.
3. Duplication Debt
AI is extremely good at generating plausible code from scratch. It's much less good at finding and reusing existing code in your project. I've caught multiple instances where AI generated a new utility function that duplicated one already in our codebase, with slightly different naming and edge case handling.
// File 1: generated by AI in February
function formatCurrency(amount: number, currency: string): string {
return new Intl.NumberFormat('en-US', {
style: 'currency',
currency: currency,
}).format(amount);
}
// File 2: generated by AI in March (different developer, same project)
function toCurrencyString(value: number, currencyCode: string): string {
const formatter = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: currencyCode,
});
return formatter.format(value);
}
// File 3: the one that was already in utils/ since January
export function displayCurrency(cents: number, currency = 'USD'): string {
return new Intl.NumberFormat('en-US', {
style: 'currency',
currency,
}).format(cents / 100);
}Three functions doing almost the same thing. Only the original handles cents-to-dollars conversion. The AI-generated ones introduce a bug where prices display 100x too high if the input is in cents.
4. Test Quality Debt
AI generates tests that pass. That's not the same as tests that catch bugs.
I ran an experiment on one of our services: I asked AI to generate a test suite, then I intentionally introduced 10 bugs. The AI-generated tests caught 3 of them. Hand-written tests by a developer who understood the business requirements caught 8.
AI tests tend to test that the code does what it does, not that it does what it should.
The Speed Trap
Here's the uncomfortable math. Let's say AI doubles your code output:
Without AI:
- 100 lines/day, 95% understood, ~5% debt rate
- Debt generated: ~5 lines/day
With AI (naive usage):
- 200 lines/day, 70% understood, ~15% debt rate
- Debt generated: ~30 lines/day
You're producing features twice as fast, but generating debt 6x faster. Within a few months, the debt burden outweighs the velocity gain.
I was wrong about this. For the first 6 months of using AI coding tools, I thought I was being more productive. My commit frequency went up. My PR size went up. But when I looked at the data, our team's end-to-end delivery time hadn't improved. We were writing code faster and debugging it longer.
The AI Debt Management Framework
Here's what I do now to capture the speed benefits of AI without drowning in debt.
Rule 1: The Comprehension Check
Before merging any AI-generated code, every engineer on my team must be able to explain every function in the diff. Not "it formats currency." But "it formats currency using Intl.NumberFormat with locale en-US, and here's why that locale is correct for our use case."
If you can't explain it, you can't maintain it. Rewrite it or learn it before merging.
Rule 2: The Consistency Gate
We maintain a CONVENTIONS.md file that documents our patterns. Before accepting AI suggestions, check them against conventions. Better yet, include your conventions in the AI's context window.
## Conventions for AI Context
- Error handling: Always use Result<T, Error> pattern, never throw
- Naming: camelCase for functions, PascalCase for types, UPPER_SNAKE for constants
- Data fetching: Use our internal fetchApi() wrapper, never raw fetch()
- Currency: Always stored in cents, use utils/currency.ts for display
- Dates: Always stored as UTC, use utils/date.ts for formattingRule 3: The Duplication Scan
Before accepting AI-generated utility functions, search your codebase for existing implementations. This takes 30 seconds and prevents a category of debt that's surprisingly expensive to clean up later.
# Before accepting a new function, check for existing implementations
grep -r "formatCurrency\|toCurrency\|displayCurrency" src/Rule 4: The Test Review
AI-generated tests get extra scrutiny. I ask two questions:
- Does this test break if I change the behavior? (Testing behavior, not implementation)
- Would this test pass if the feature had a common bug? (Testing the right things)
If the answer to either is "no," rewrite the test.
Rule 5: The Weekly AI Debt Audit
Every Friday, review the week's AI-assisted PRs with fresh eyes. Flag any code that:
- Introduces a pattern inconsistent with the rest of the codebase
- Duplicates existing functionality
- Has unclear or missing comments on non-obvious logic
- Was accepted without the author fully understanding it
Track these flags. If the count is trending up, your team needs to slow down.
The Decision Tree for AI-Generated Code
Is this code for a critical path (auth, payments, data integrity)?
├── YES: Write it yourself. Use AI for syntax help only.
└── NO: Proceed with AI generation
│
Can you explain every line to a teammate?
├── NO: Stop. Learn it or rewrite it.
└── YES: Proceed
│
Does it follow your team's conventions?
├── NO: Modify it to match conventions.
└── YES: Proceed
│
Does similar code already exist in your codebase?
├── YES: Use the existing code. Delete the generated code.
└── NO: Ship it.
What I Tell My Team
AI coding tools are a multiplier, not a replacement for engineering judgment. A 2x multiplier on good engineering practices gives you great results. A 2x multiplier on careless practices gives you a codebase that's twice as hard to maintain in half the time.
The teams that will win in the AI era aren't the ones that generate the most code. They're the ones that maintain the highest ratio of understood code to total code. That's the metric that matters now.
The Contrarian Take
Most AI-in-engineering discourse focuses on productivity gains. I think the bigger story is quality risk. AI doesn't just change how fast you code. It changes the relationship between the developer and the code. For the first time in software history, it's normal for developers to ship code they didn't write and don't fully understand.
That's not inherently bad. But pretending it doesn't change the technical debt equation is dangerous. Account for it. Measure it. Manage it. Or drown in code nobody on your team can explain.
$ ls ./related
Explore by topic