
The uncomfortable truth is that AI-generated code passes review because it looks finished. Then a teammate renames a field, changes an API response, or adds a parameter, and the code breaks in ways its tests never caught. The problem isn’t that the code is wrong today. It’s that nobody, including the model, holds its assumptions in their head.
Evan X. Merz, a former Meta engineer, recently published a post that’s been making the rounds on Hacker News (369 points and counting). His background matters here: he generated at least 90% of his code with AI in his final months at Meta, the minimum threshold for a decent performance rating. And he watched the quality bar collapse anyway.
“I saw the code being pushed by some of my peers who were heavily reliant on AI, and I saw that it was brittle code”, Merz writes. “There were many subtle issues that would lead to tech debt in the long term.”
This isn’t a Luddite’s lament. It’s a structural problem with how LLMs approach code generation, and it’s getting worse as more organizations mandate AI-first development.
The Four Ways AI Makes Codebases Fragile
1. AI Writes Tests That Test Nothing
The most obvious failure of modern LLMs is their tendency to write simplistic test suites that miss what actually matters. Merz observed AI agents generating lots of tests for get/set methods, trivial code that rarely needs testing, while skipping the integration paths that actually break.
When AI does write end-to-end tests, they tend to only cover cases already present in the codebase. One user type. One happy path. The resulting gaps in coverage are invisible to developers because line-level coverage metrics still look healthy.
This creates a dangerous false confidence. Your CI pipeline shows 87% coverage, your tests pass, and you merge with confidence. Six months later, the edge case that wasn’t tested becomes the production incident that wakes you up at 3 AM.
2. AI Over-Complicates Everything
AI also tends to write overly complex code. It uses two or three helper methods when one would suffice. It follows DRY to the point of absurdity, creating abstractions that make the code more difficult for humans to debug and understand.
The Hacker News thread crystallized this pattern: developers reported overly abstracted or generic code where AI defaults to complex design patterns unnecessarily. One cited example was AI generating a full factory pattern for simple, one-off object creation. This increases the codebase surface area without proportional benefit.
The result is a “patchwork of foreign paradigms” where no single developer understands the whole. This differs fundamentally from traditional technical debt. With old-school debt, the team understands the messy code but chooses speed over cleanliness. With AI debt, the understanding itself is missing from the start.
3. Developers Lose the Plot
Here’s the insidious part: the longer a company relies on AI agents to generate code, the fewer developers actually understand the codebase.
This isn’t about intelligence, it’s about exposure. When you never write the code, you never develop the mental model of how the pieces fit together. Developers can’t spot when a pull request introduces a new abstraction that doesn’t work with pre-existing structures because they don’t know the pre-existing structures.
The analysis from Finn Kamau quantifies this: debugging and extending AI-written sections took 30-50% longer than human-written code of similar complexity. New team members can’t reconstruct the AI’s scattered reasoning, making them hesitant to modify apparently functional code.
Refactoring becomes riskier too. Tools struggle to identify logical dependencies in AI-generated code, breaking implicit couplings that aren’t visible in the syntax.
4. Review Velocity Crushes Quality
AI moves too fast for human review to keep pace. Each pull request arrives faster than the last and gets reviewed in even less time. Developers skim the code. They miss the subtle issues. The small quality gaps, testing gaps, poorly named variables, inconsistent error handling, accrue until the codebase becomes a morass of spaghetti.
Merz witnessed one of the best programmers on his team try to merge code that used JavaScript events for mouseover CSS effects rather than the :hover selector. “That’s a totally unscalable way of writing mouseovers”, he notes. When senior engineers miss these obvious signals, the review process has already failed.
The Four Signs Your AI Codebase Is Already Fragile
1. Incidents Keep Rising
When you switched your department over to agentic coding, did the number of incidents slowly start rising? This pattern appears deceptively gradual at first. PRs come and get merged. Everything seems fine. Then one p0 hits at the end of the month. Two SEVs the following month. Then more. After a year or two of agentic coding, it feels like there’s an incident every weekend.
2. Recovery Takes Longer
Not only are there more incidents, they take longer to mitigate. Your developers have a shallower understanding of the codebase. When the coding agent can’t find the issue, humans have to dig through highly complex, poorly tested code. They’re essentially entering unfamiliar territory, so every diagnosis takes longer. Companies lose more money to incidents that take longer to resolve.
3. Errors Only Users See
When humans don’t spend time in the code, they fail to understand all the use cases. They don’t ensure the AI is instrumenting analytics properly or catching all errors. Invisible errors start appearing, errors only real-world users encounter, not the artificial users in automated tests.
4. User Experience Deteriorates
This leads to the ultimate problem: AI codebases are software made by AI for AI. They aren’t tailored to how human users interact with the product. They aren’t designed for how human developers need to maintain it. The experience degrades on both sides, creating a vicious cycle of enshittification.
The Data Behind the Disaster
The numbers backing these anecdotes are sobering. GitClear’s analysis of 211 million changed lines of code from 2020 to 2024 found that in 2024:
- Blocks of five or more duplicated lines rose eightfold in frequency
- Copy-pasted lines outnumbered moved lines (moving code indicates reuse and tidying, copying indicates duplication)
Google’s 2024 DORA report adds another layer: a 25% rise in AI use correlated with faster code reviews and better documentation but also a 7.2% drop in delivery stability.
The comparison table from the PromptZone analysis captures the structural difference:
| Aspect | AI-Assisted Codebase | Traditional Codebase |
|---|---|---|
| Onboarding Speed | Slower (lacks consistent model) | Faster (shared understanding) |
| Refactoring Safety | Lower (hidden couplings) | Higher (explicit dependencies) |
| Architectural Consistency | Low (patchwork patterns) | Variable (team-dependent) |
| Initial Velocity | Very High | Standard |
The fragility stems from aggregated micro-decisions made by an AI without context, creating a codebase that’s harder to reason about than one written by a single junior developer.
The Counterargument: Won’t AI Just Fix It Later?
There’s a seductive logic floating around engineering leadership circles: “If next year’s frontier model can refactor a module, backfill tests, or migrate an API in an afternoon, the interest rate on debt taken today is effectively falling.”
This belief changes behavior. Teams ship the prototype, skip the hardening, defer the operational design. The model will sort it out.
The problem, as Stacklok’s analysis points out, is that this logic only holds for code-level debt. Duplicated logic, missing tests, awkward interfaces, these are exactly what coding agents handle well.
Structural debt is different. It lives in how sessions recover, how identity flows, how state survives a pod restart, and how you prove what an agent did. That debt lives in architecture, not in files an agent can rewrite. And the interest rate on structural debt is not falling.
Models are getting better at writing code, and equally better at finding security holes in it. Hardening doesn’t get cheaper when both sides of the ledger move together.
This is the scalability and fragility of cloud-first AI architectures in miniature: every layer of AI adds capability and complexity, and the complexity compounds faster than the capability.
So What Do You Actually Do?
The Hacker News consensus converged on a structured workflow to capture AI’s velocity while containing its fragility.
Step 1: Write the Contract Yourself
Before any code is generated, write the function signature and docstring manually. This defines the contract and intent. The AI implements the body against your specification, not against its own guess of what you wanted.
Step 2: Catch the Mechanical Failures Automatically
Most fragile AI code falls into three buckets:
- Divergent duplicates: the model re-implements logic that exists elsewhere
- Invented APIs: it calls methods or config keys that look plausible but don’t exist in your stack, hidden behind a
try/exceptthat swallows the failure - Absent contracts: it skips type hints, validation, and error paths because the happy path was all the prompt asked for
The first two are mechanical. You don’t need a human to catch them.
A copy-paste detector like jscpd finds re-implemented helpers before merge:
# .github/workflows/duplication.yml
name: duplication
on: [pull_request]
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npx jscpd src --min-tokens 40 --reporters consoleFull --fail-on-duplication
The --min-tokens 40 threshold matters. Set it too low and you flag every data class. At 40 tokens you catch real copied logic, exactly what the model produces when your prompt says “add a function that formats invoices.”
Strict type checking kills the silent-except failure mode:
# What the model writes
def get_theme(user):
try:
return user.profile.settings.theme
except Exception:
return "default"
# What mypy forces
def get_theme(user: User) -> str:
settings = user.profile.settings if user.profile else None
return settings.theme if settings else "default"
The refactored version is explicit about every fallback. When the settings shape changes, the type checker tells you exactly where to look.
Step 3: Write Prompts That Name Your Contracts
The single highest-leverage change to your prompts is pasting relevant types and existing helpers into the context:
Use the `format_invoice` helper in src/utils/billing.py.
Here is the Invoice type and the helper's signature: [...]
Do not add new dependencies.
Return early on missing fields, no broad except blocks.
A model with real signatures in context has no reason to invent an API or duplicate a helper. A model without them will guess, and the guess will look confident.
Step 4: Own the Architecture Review
Automation can’t judge whether code belongs in your architecture. A function can be duplication-free, fully typed, and still wrong: in the wrong layer, holding a connection it should release, or quietly coupling your billing module to your auth module.
Read AI code the way you’d read a junior engineer’s pull request. Ask what assumptions it makes and what happens when each one changes. If you can’t answer that in a minute, the code is fragile regardless of who wrote it.
This is where how LLM-generated code undermines architectural integrity through design drift becomes the real issue. The limitations of vector-based retrieval in understanding codebase-wide dependencies mean your AI tools can’t see the forest, only the trees in their context window.
The Startup Trap
The fragility risk is highest for early-stage startups and small teams building foundational products. Code written in the first 6-12 months sets the architectural trajectory. AI-generated foundational code can cripple scaling efforts.
This matters because the governance risks of external AI code generation in enterprise systems compound the problem. Every prompt sent to an external API is a potential data exposure, and every generated function is a potential architectural landmine.
The risk is lower for large teams maintaining mature, well-tested systems where AI generates isolated, well-specified utilities. It’s also lower for learning and prototyping, where long-term maintenance isn’t a priority.
The core issue is applying prototyping tools to production architecture.
The Human Touch Still Matters
For a product to satisfy human customers, it has to be informed by a human perspective. Nobody likes ordering from machines at a restaurant. Shopping for clothes from a website always results in worse fits than shopping in person. The same is true of SaaS products.
Any time you leave your SaaS product in the hands of a machine, you lose the human touch that’s valuable to your users and your developers. This may work for the cheapest products that only compete on price. But high-quality software products will continue to have a human touch.
Treat AI as an intern who writes first drafts, not an architect who designs systems. The human must own the specification, the integration, and the final review.
The most durable systems may not be the ones designed to last forever. They may be the ones designed to let important parts of the architecture become obsolete without taking the rest of the business down with them. As CTO Magazine’s expert roundtable concluded: “The architectural metric I care about isn’t how quickly we can ship the next feature. It’s how many existing components we have to touch to ship it.”
That metric applies to the code your AI generates today. If you can’t answer how many existing components each new AI-generated function touches, you’re already building the house of cards.




