AI Integration · Software Engineering
7 AI Coding Mistakes Nobody Talks About, and How to Fix Them
AI coding tools introduce subtle, systemic problems most teams miss. Here are 7 AI-specific mistakes and the guardrails that prevent them.
Anurag Verma
13 min read
Sponsored
Every article about AI coding tools focuses on the upside: faster development, increased productivity, more tests, better documentation. And the upside is real.
But there are AI coding mistakes nobody talks about, because the industry mostly talks up the wins. Teams that lean heavily on AI coding tools run into patterns of damage that are unique to AI-generated code: problems that would not exist if the code had been written manually.
These are not bugs in the traditional sense. The code works. The tests pass. The features ship. But the codebase is silently rotting in ways that will cost real money to fix later. Here are 7 of those mistakes, with examples and the patterns that prevent them.
The most dangerous AI coding mistakes are the ones where the code works perfectly, until it does not.
Mistake 1: Ghost Technical Debt
What it is: Ghost technical debt is code that works correctly, passes every test, ships to production, and that nobody on the team can actually explain. It looks like clean, working software right up until something breaks in a way that requires understanding logic no one on the current team ever built a mental model of.
This is the most insidious problem with AI-generated code. When a developer writes code manually, they build a mental model of how it works as they write it. When an AI generates code and the developer reviews it quickly and merges it, the mental model does not exist. The code is a black box that happens to produce the right output.
A typical example: a Next.js application where the data fetching layer is entirely AI-generated. It works flawlessly, until a third-party API changes its response format. The error shows up as incorrect data on a dashboard, but the root cause is buried in a chain of transformations that nobody understands. If the developer who prompted the AI to write it has left the company, the current team can spend days tracing through code that would have taken 30 minutes to debug if someone had written it manually and understood the logic.
The cost: days of senior developer time for a fix that might be a few lines of code.
How to prevent it:
Our "Ghost Debt" Prevention Rules:
├── 1. Every AI-generated function gets a JSDoc comment written
│ by the HUMAN, not the AI. If you cannot write the comment,
│ you do not understand the code.
│
├── 2. Complex logic gets a "why" comment, not just a "what"
│ comment. AI writes "what" comments perfectly. Only humans
│ can explain "why": the business context, the trade-offs,
│ the alternatives that were considered.
│
├── 3. No "magic" utility functions. If a utility does something
│ non-obvious (complex regex, bit manipulation, custom
│ encoding), the developer must be able to explain it in
│ a code review. If they cannot, it gets rewritten.
│
└── 4. Monthly "explain the code" sessions. Random sections of
the codebase are selected and developers explain them to
each other. If nobody can explain a section, it gets
flagged for documentation or rewriting.
Mistake 2: The Copy-Paste Architecture
What it is: AI generates new implementations of functionality that already exists in the codebase, leading to duplicated logic scattered across multiple files.
This happens because AI tools have limited context. When you ask Claude Code or Cursor to “add a function to validate email addresses,” it generates a new email validation function, even if there is already one in src/utils/validation.ts. The new one might be slightly different (different regex, different error messages), creating inconsistency.
Over time, this compounds. An audit of a codebase built this way can turn up something like:
- 4 different email validation functions (3 AI-generated, 1 original)
- 3 different date formatting utilities
- 2 different error handling patterns
- 5 different ways to make HTTP requests to the same internal API
The cost: Developer confusion, inconsistent behavior for users, and every bug fix needs to be applied in multiple places.
How to prevent it:
# In our CLAUDE.md / project context files:
## Existing Utilities (ALWAYS use these, never create duplicates)
### Validation
- Email: `src/utils/validation.ts` → `validateEmail()`
- Phone: `src/utils/validation.ts` → `validatePhone()`
- URL: `src/utils/validation.ts` → `validateUrl()`
### Formatting
- Dates: `src/utils/format.ts` → `formatDate()`, `formatRelative()`
- Currency: `src/utils/format.ts` → `formatCurrency()`
- File size: `src/utils/format.ts` → `formatFileSize()`
### HTTP
- Internal API: Always use `src/lib/api-client.ts`
- External APIs: Always use `src/lib/http.ts` with retry logic
### Error Handling
- API errors: Use `AppError` class from `src/lib/errors.ts`
- User-facing errors: Use `ERROR_MESSAGES` from `src/constants/`
DO NOT create new utility functions without checking this list first.
This project context approach prevents most of the duplication, and code review catches the rest. If you’re on Cursor specifically, our guide to codifying team standards into Cursor’s rules system covers the same idea in more depth.
Mistake 3: The Security Blind Spot
What it is: AI-generated code systematically deprioritizes security in favor of “making it work.”
This is not a subtle problem. It is a systematic one. AI models optimize for correctness (does the function produce the right output?) and completeness (does it handle the specified requirements?). Security is a secondary concern, if it is considered at all.
Typical examples:
// AI-generated: Input validation that looks correct but is incomplete
const createUserSchema = z.object({
name: z.string().min(1).max(100),
email: z.string().email(),
role: z.string(), // ← Should be z.enum(['user', 'admin', 'editor'])
// AI accepted any string, including 'admin'
// This was a privilege escalation vulnerability
});
// AI-generated: File upload without proper validation
app.post('/upload', async (req, res) => {
const file = req.files?.document;
// AI checks file size ✓
if (file.size > 10 * 1024 * 1024) {
return res.status(400).json({ error: 'File too large' });
}
// AI checks extension ✓
if (!['pdf', 'doc', 'docx'].includes(file.name.split('.').pop())) {
return res.status(400).json({ error: 'Invalid file type' });
}
// AI does NOT check MIME type ✗
// AI does NOT check for embedded scripts in PDFs ✗
// AI does NOT scan for malware ✗
// AI saves to a predictable path ✗ (directory traversal risk)
await file.mv(`/uploads/${file.name}`); // ← Path injection possible
});
How to prevent it: Every security-sensitive path (authentication, authorization, file handling, data access, input validation, and payment processing) should get a manual security review against a written checklist. AI generates the first draft. A human reviews it against a security threat model specific to that feature.
Mistake 4: The Review Bottleneck
What it is: AI generates code faster than humans can review it, creating a quality bottleneck that teams solve by lowering review standards.
Before AI, a developer might produce 1-2 PRs per day. The team could review each PR thoroughly. With AI, a developer might produce 4-6 PRs per day. The review queue backs up. And when the review queue backs up, one of two things happens:
- Reviews get rushed (someone skims the diff and approves)
- Reviews get skipped (“the tests pass, it is fine”)
Both lead to the same outcome: production code that nobody has carefully examined.
How it shows up: a useful signal is the number of substantive comments per review. If that number drops after AI adoption while PR volume rises, the code is probably not getting better. The reviews are getting worse, which is part of why we think dedicated AI code review tools are worth running as a first pass, not as a replacement for the human review that still has to happen.
How to prevent it:
-
PRs have a maximum size. No PR should change more than 300 lines. If an AI generates a 500-line change, it gets split into smaller, reviewable PRs.
-
Review time is sacred. Schedule 1 hour per day specifically for code review. It is not optional. It does not get bumped for “urgent” tasks (which are rarely as urgent as they seem).
-
AI-generated code gets flagged. Each PR includes a note indicating which files were AI-generated. This tells reviewers to apply extra scrutiny to those files.
-
The “explain it” rule. During review, the author must be able to explain any AI-generated code. If they cannot, the PR goes back for revision, even if the code is correct.
Mistake 5: The Frankenstein Architecture
What it is: Over time, AI-generated code from different prompting sessions creates an inconsistent, layered architecture where different parts of the system follow different patterns.
This happens because each AI interaction is independent. When you ask Claude Code to build the user module on Monday, it might use Repository pattern. When you ask it to build the product module on Wednesday, it might use Active Record pattern. Both work correctly in isolation. Together, they create a codebase where developers never know which pattern to follow.
A typical example: three different state management approaches in the same React application:
- Local component state (from the original manual code)
- Zustand stores (from an AI session in January)
- React Context (from a different AI session in February)
All three work. None of them share state. The data flow is incomprehensible.
How to prevent it:
Maintain an Architecture Decision Record (ADR) file in every project:
# Architecture Decisions
## State Management
Decision: Use Zustand for all shared state.
Date: 2025-11-15
Rationale: Zustand offers simplicity, TypeScript support, and
devtools integration. React Context is too verbose for our needs.
Constraint: No new Context providers without team discussion.
## Data Access
Decision: Repository pattern via src/lib/data/ directory.
Date: 2025-11-15
Rationale: Centralizes database queries, makes testing easier.
Constraint: Components and API routes NEVER query the database
directly.
## Error Handling
Decision: Custom AppError class hierarchy.
Date: 2025-11-20
Rationale: Consistent error responses, proper HTTP status codes.
Constraint: All thrown errors must be instances of AppError.
This ADR file is included in the CLAUDE.md context so that every AI session knows the existing architectural decisions. It keeps new code from drifting into a fourth pattern. Picking the right model for the task in the first place also helps here; our comparison of AI coding models covers which ones are strongest at holding architectural context over long sessions.
Mistake 6: The False Productivity Trap
What it is: AI-generated code ships fast but creates downstream costs that are not measured, leading to a false sense of productivity.
This is the most dangerous mistake because it feels like progress. The team is shipping features at 2x speed! Velocity metrics are up 40%! The sprint board is clearing faster than ever!
But here is what the velocity metrics do not capture:
- The 3-hour debugging session when an AI-generated API returns incorrect data on edge cases
- The 2-day refactoring sprint when you discover three different implementations of the same business rule
- The security incident when an AI-generated endpoint is exploited because nobody audited the input validation
- The customer support tickets from users who encounter inconsistent behavior across different parts of the application
The real math: If a feature takes 4 hours to generate with AI but creates 6 hours of downstream debugging and maintenance, you have not saved time. You have borrowed it.
How to prevent it: Track “total cost of ownership” for features alongside “time to ship.” This includes post-ship bug fixes, support tickets, and refactoring time attributed to that feature. When a pattern of high downstream costs shows up in AI-generated features, increase the review rigor for that part of the codebase.
Mistake 7: The Skill Erosion Problem
What it is: Developers who rely heavily on AI tools gradually lose the ability to write, debug, and reason about code independently.
The pattern is easy to picture. A developer who used to trace through complex async code to find race conditions starts asking Claude Code to find them instead. When Claude Code cannot find the issue (because it requires understanding the interaction between two services the AI cannot see simultaneously), the developer struggles to do it manually, a skill they had six months earlier.
A developer who cannot debug without AI assistance is a liability, not an asset, because AI tools fail (context limits, hallucinations, system boundaries they cannot see) and when they fail, someone needs to step in with real understanding.
How to prevent it:
-
30% rule for juniors. Junior developers do at least 30% of their work without AI assistance, specifically the work that builds skills (complex debugging, writing algorithms, designing data structures).
-
Monthly “unplugged” sessions. Once a month, the team works on a feature or bug fix with all AI tools disabled. This is uncomfortable. That is the point. The discomfort is where skill development happens.
-
Teaching by doing. When a developer asks AI to explain code, follow up by asking them to explain it back to a teammate. Active recall builds understanding; passive reading does not.
The Meta-Problem: Most Teams Do Not Know They Have These Problems
The most dangerous aspect of AI coding mistakes is that they are invisible in the short term. The code works. The tests pass. The features ship. The velocity metrics look great.
The problems only become visible when:
- You need to debug a production issue in AI-generated code nobody understands
- You need to onboard a new developer and they cannot make sense of the architecture
- You need to add a feature and discover that the existing code has three different patterns for the same thing
- A security audit reveals systematic vulnerabilities in your input validation
By the time you notice, the technical debt has compounded for months.
How to Start Fixing This
If you are using AI coding tools (and you should be, the productivity gains are real), here is our prioritized list of actions:
Week 1: Write a CLAUDE.md / project context file that documents your architectural decisions, existing utilities, and coding conventions. This single action prevents a large share of the problems described above. If your team also relies on Cursor or another agentic IDE day to day, our agentic IDE comparison is worth reading alongside this, since some of these failure modes show up differently depending on which tool your team standardizes on.
Week 2: Implement the PR size limit (300 lines maximum) and schedule dedicated code review time.
Week 3: Add a security review checklist for all code that touches authentication, authorization, file handling, or payments. Review these paths manually regardless of whether AI or a human wrote the code.
Week 4: Start tracking total cost of ownership for features, not just time to ship. This gives you the data to identify where AI is creating hidden costs.
Ongoing: Watch for skill erosion. Talk to your developers about it openly. Create space for manual work that builds skills.
The goal is not to stop using AI coding tools. The goal is to use them effectively: getting the productivity benefits without accumulating the invisible debt that will cost you later.
Dealing with AI-Generated Technical Debt?
At CODERCOPS, we review AI-generated codebases for exactly these problems. If you suspect your codebase has accumulated hidden technical debt from AI tools, or if you want to establish guardrails before problems develop, we can help.
Frequently asked questions
- What is 'ghost technical debt' in AI-generated code?
- Code that works, passes every test, and that nobody on the team can actually explain. It happens because when an AI generates code and a developer merges it after a quick review, no one builds the mental model a human writer would have built while typing it themselves. The fix is to require the human, not the AI, to write the JSDoc and 'why' comments, since if you can't write the comment, you don't understand the code.
- Why does AI-generated code create so much duplicate logic?
- Because AI tools have limited context and don't reliably know an equivalent function already exists elsewhere in the codebase. It's common to find several email validation functions and multiple ways to call the same internal API in one codebase. Documenting existing utilities in a CLAUDE.md-style project context file, with explicit 'always use this, never create duplicates' instructions, prevents most of it.
- Does AI-generated code have more security vulnerabilities than human-written code?
- Not inherently more, but differently distributed. AI models optimize for correctness and completeness, with security as a secondary concern if considered at all. Typical examples are a Zod schema that accepts any string for a user role instead of restricting it to an enum (an actual privilege escalation bug) and file upload handlers that check size and extension but never MIME type. Every security-sensitive path (auth, file handling, payments) should get a manual review regardless of who or what wrote it.
- How do you review AI-generated code fast enough without lowering standards?
- Cap PR size at 300 lines so a 500-line AI-generated change gets split up, protect a dedicated hour per day for review that doesn't get bumped for 'urgent' work, flag which files are AI-generated so reviewers apply extra scrutiny, and require the PR author to be able to explain any AI-generated code in it. If they can't explain it, it goes back for revision even if the code is technically correct.
- How do you stop developers from losing debugging skills to AI tools?
- A 30% rule for junior developers, where at least 30% of their work (complex debugging, algorithms, data structures) happens without AI assistance, plus a monthly 'unplugged' session where the whole team works a feature or bug fix with AI tools disabled. It's uncomfortable by design. When a developer asks AI to explain code, have them explain it back to a teammate, since active recall builds understanding that passive reading doesn't.
Sponsored
More from this category
More from AI Integration
Sponsored
Discussion
Join the conversation.
Comments are powered by GitHub Discussions. Sign in with your GitHub account to leave a comment.
Sponsored