reviewing-ai-code
Teaches how to review, audit, and evaluate AI-generated code effectively. Covers common AI code smells like over-engineering, dead code, wrong abstractions, and hallucinated APIs. Includes security review checklists, dependency auditing, performance review techniques, and strategies for catching the subtle bugs that AI confidently introduces. Use when reviewing code produced by any AI coding tool.
How to review and audit AI-generated code — catching the bugs, smells, and security issues that AI tools confidently introduce.
## Key Points
- **Confident incorrectness.** AI code looks clean and well-structured even when it is wrong. There are no signs of uncertainty in the code itself.
- **Plausible but fake APIs.** The AI may call functions that do not exist in the library version you are using, or use method signatures that were deprecated two years ago.
- **Pattern mimicry without understanding.** The code follows patterns from training data but may not apply them correctly to your specific case.
- **Hidden assumptions.** AI code often assumes things about your environment, data shape, or user behavior that are not stated or validated.
- Abstract base classes with one implementation
- Generic type parameters that are never used polymorphically
- Configuration objects for behavior that never varies
- Wrapper functions that add no logic
- "Extensible" patterns for features you never plan to add
- Functions defined but never called
- Imports that are not referenced
- Variables assigned but never read
## Quick Example
```
// AI might generate this — INSECURE
if (req.headers.authorization) {
// User is authenticated
next();
}
```
```
// AI might generate this — INSECURE
const user = await db.query(`SELECT * FROM users WHERE id = ${userId}`);
```skilldb get vibe-coding-workflow-skills/reviewing-ai-codeFull skill: 307 linesReviewing AI Code
How to review and audit AI-generated code — catching the bugs, smells, and security issues that AI tools confidently introduce.
Why AI Code Review Is Different
Reviewing AI-generated code is not the same as reviewing human code. Humans make typos, forget edge cases, and write lazy shortcuts. AI makes different mistakes:
- Confident incorrectness. AI code looks clean and well-structured even when it is wrong. There are no signs of uncertainty in the code itself.
- Plausible but fake APIs. The AI may call functions that do not exist in the library version you are using, or use method signatures that were deprecated two years ago.
- Pattern mimicry without understanding. The code follows patterns from training data but may not apply them correctly to your specific case.
- Hidden assumptions. AI code often assumes things about your environment, data shape, or user behavior that are not stated or validated.
Your review process must account for these failure modes.
Common AI Code Smells
Over-Engineering
AI loves abstraction. Ask for a simple function and you may get a class hierarchy with generics, a factory pattern, and a configuration object.
Signs:
- Abstract base classes with one implementation
- Generic type parameters that are never used polymorphically
- Configuration objects for behavior that never varies
- Wrapper functions that add no logic
- "Extensible" patterns for features you never plan to add
Fix: Ask yourself: "Would I add this abstraction if I were writing this by hand?" If no, simplify.
Dead Code
AI frequently generates helper functions, utility classes, or constants that nothing actually uses.
Signs:
- Functions defined but never called
- Imports that are not referenced
- Variables assigned but never read
- Entire files that nothing imports
- Switch cases or if-branches for states that cannot occur
Fix: Run your language's dead code detection. TypeScript has noUnusedLocals. ESLint has no-unused-vars. Rust has dead code warnings by default. Trust the tooling.
Wrong Abstractions
The AI will DRY up code that should not be DRY. It will merge two similar-looking functions into one generic function that handles neither case well.
Signs:
- Functions with boolean parameters that toggle behavior ("isAdmin" flags)
- Shared utilities that are only used in one place
- Base components that are harder to understand than the components they replaced
- Generic types that make simple code confusing
Fix: Duplication is better than the wrong abstraction. If two things look similar but serve different purposes, let them be separate.
Hallucinated APIs
The AI may use API methods, function signatures, or library features that do not exist in the version you are using.
Signs:
- Method calls that your IDE cannot resolve
- Import statements for modules that do not exist in the package
- Function parameters that the actual API does not accept
- Using v3 API patterns when you have v2 installed
Fix: Verify every external API call against the actual documentation for your installed version. Run npm ls [package] or equivalent to check versions.
Copy-Paste Variations
When asked to build similar features, AI often generates near-identical code with small variations rather than extracting shared logic.
Signs:
- Multiple files that are 90% identical
- The same validation logic repeated across routes
- Identical error handling in every API endpoint
- Copy-pasted components with one prop changed
Fix: After the feature works, prompt the AI to extract shared patterns. Or do it manually — this is where human judgment excels.
The Review Checklist
Use this checklist for every AI generation before committing:
Correctness
- Does the code do what I asked for? (Not just: does it look like it does?)
- Have I tested the happy path manually?
- Have I tested at least one error path?
- Are all API calls using real, existing endpoints and methods?
- Do the types match the actual data shapes?
- Are there any logic inversions? (AI sometimes reverses conditions)
Security
- No hardcoded secrets, API keys, or passwords
- User input is validated and sanitized before use
- SQL queries use parameterized queries (no string concatenation)
- Authentication checks are present on protected routes
- Authorization checks exist (not just "is logged in" but "can this user do this?")
- No sensitive data in client-side code or logs
- CORS is configured correctly (not
*in production) - File uploads are validated (type, size, content)
- Rate limiting exists on public endpoints
- Error messages do not leak internal details
Dependencies
- All new dependencies are intentional (AI sometimes adds packages you do not need)
- Dependency versions are compatible with your project
- No deprecated packages added
- License compatibility checked for new dependencies
- No duplicate dependencies (two libraries that do the same thing)
- Package is actively maintained (check last publish date, open issues)
Performance
- No N+1 queries (fetching related data in a loop)
- No unnecessary re-renders in frontend code
- Large lists are paginated, not loaded entirely
- Images and assets are optimized
- No synchronous operations that should be async
- Database queries have appropriate indexes
- No memory leaks (event listeners not cleaned up, intervals not cleared)
Security Review Deep Dive
AI-generated code has recurring security problems. These are the most common:
Authentication Bypass
AI often generates auth middleware that checks for the presence of a token but does not validate it properly.
// AI might generate this — INSECURE
if (req.headers.authorization) {
// User is authenticated
next();
}
This checks if a header exists, not if it is valid. Always verify the token's signature, expiration, and issuer.
SQL Injection
AI sometimes builds queries with string interpolation despite knowing better.
// AI might generate this — INSECURE
const user = await db.query(`SELECT * FROM users WHERE id = ${userId}`);
Always use parameterized queries. If the AI generates string interpolation in SQL, reject it immediately.
Exposed Internal Errors
AI error handlers often return the full error object to the client.
// AI might generate this — INSECURE
catch (error) {
res.status(500).json({ error: error.message, stack: error.stack });
}
Stack traces reveal file paths, library versions, and internal structure. Log the full error server-side; return a generic message to the client.
Overly Permissive CORS
AI defaults to permissive CORS because it works during development.
// AI might generate this — INSECURE in production
app.use(cors({ origin: '*' }));
Restrict CORS to your actual frontend domains in production.
Missing Rate Limiting
AI almost never adds rate limiting unless you ask. Every public endpoint — especially auth endpoints — needs it.
Dependency Audit Process
When AI adds a new dependency:
-
Check if it is necessary. Can you do this with what you already have? AI loves adding packages for things you can write in 10 lines.
-
Check the package. Go to npm (or equivalent) and check:
- Last publish date (anything older than 1 year is concerning)
- Weekly downloads (low downloads = low community vetting)
- Open issues and PRs (abandoned maintenance?)
- Bundle size (does a 2KB utility pull in a 500KB dependency?)
-
Check for alternatives you already have. AI often adds
axioswhen you already havefetch. Or addslodashfor one function you could write inline. -
Check the license. GPL dependencies in MIT projects can be a legal issue. AI does not check licenses.
-
Pin versions. AI may install with
^ranges. For production, consider pinning exact versions or using a lockfile.
Performance Review Patterns
The N+1 Query
The most common AI performance bug. The AI fetches a list, then fetches related data for each item individually.
// AI might generate this — SLOW
const posts = await db.query('SELECT * FROM posts');
for (const post of posts) {
post.author = await db.query('SELECT * FROM users WHERE id = ?', [post.author_id]);
}
Fix: Use a JOIN or batch query.
Unnecessary State in React
AI loves useState for things that should be derived.
// AI might generate this — REDUNDANT
const [items, setItems] = useState([]);
const [itemCount, setItemCount] = useState(0);
// Updates items and itemCount separately
Fix: const itemCount = items.length; — no state needed.
Loading Everything
AI generates code that fetches all records. This works with 10 items and crashes with 10,000.
// AI might generate this — DOES NOT SCALE
const allUsers = await db.query('SELECT * FROM users');
Fix: Always paginate. Add LIMIT and OFFSET (or cursor-based pagination) from the start.
Synchronous File Operations
AI sometimes uses fs.readFileSync or equivalent in request handlers.
Fix: Always use async file operations in server code. Sync I/O blocks the event loop.
Review Workflow
For Small Generations (< 50 lines)
- Read every line
- Verify API calls against documentation
- Run the code
- Commit if it works
For Medium Generations (50-200 lines)
- Read the overall structure (what files changed, what was added)
- Read the business logic carefully
- Skim the boilerplate
- Check the security-relevant sections (auth, input handling, queries)
- Run the code and test edge cases
- Commit if it works
For Large Generations (200+ lines)
- Use
git diffto see all changes - Categorize changes: new files, modified files, deleted files
- Review new files first (these are entirely AI-generated)
- Review modifications to existing files (these may break working code)
- Run the full test suite
- Test manually
- Consider breaking the commit into logical units
Anti-Patterns Summary
| Anti-Pattern | Risk | Fix |
|---|---|---|
| Trusting clean-looking code | AI bugs hide in well-formatted code | Test behavior, not appearance |
| Skipping dependency review | Bloated bundles, security issues, licensing | Audit every new package |
| Assuming API calls are correct | Runtime errors from hallucinated APIs | Verify against actual docs |
| Ignoring security review | Vulnerabilities in production | Use the security checklist every time |
| Reviewing only the diff | Missing context of how changes fit | Review both the diff and the full file |
| No performance testing | Works in dev, crashes in prod | Test with realistic data volumes |
| Accepting over-engineered code | Maintenance burden, confusion | Simplify before committing |
Install this skill directly: skilldb add vibe-coding-workflow-skills
Related Skills
ai-pair-programming
Teaches effective AI pair programming techniques for tools like Claude Code, Cursor, and Copilot. Covers when to lead versus follow the AI, providing persistent context through CLAUDE.md and .cursorrules files, breaking complex tasks into AI-manageable pieces, using git strategically with frequent commits as checkpoints, and recognizing when the AI is stuck in a loop. Use when working alongside AI coding tools in a collaborative development workflow.
debugging-ai-code
Teaches how to debug code generated by AI tools, covering the unique failure modes of AI-generated code including hallucinated APIs, version mismatches, circular logic, and phantom dependencies. Explains how to read error messages back to the AI effectively, provide minimal reproductions, diagnose when the AI is giving bad fixes, and use systematic debugging approaches on codebases you did not write by hand. Use when AI-generated code is not working and you need to find and fix the issue.
maintaining-ai-codebases
Covers the unique challenges of maintaining codebases built primarily through AI code generation. Addresses inconsistent patterns across AI-generated files, refactoring AI sprawl, establishing coding conventions after the code already exists, documentation strategies for AI-built projects, and managing the specific forms of technical debt that AI tools create. Use when a vibe-coded project needs ongoing maintenance or has grown unwieldy.
prompt-to-app
Guides the complete journey from an idea to a working application using AI code generation tools. Covers writing effective app specifications, choosing the right tool for the job (Claude Code, Cursor, Bolt, v0, Lovable, Replit Agent), the spec-first approach, iterating on generated code without losing coherence, and managing scope creep during AI-assisted development. Use when someone wants to build an app from scratch using vibe coding.
scaling-past-vibe
Guides the transition from a vibe-coded prototype to a production-grade application. Covers identifying when the project has outgrown pure vibe coding, refactoring AI-generated code for production reliability, adding tests retroactively to an untested codebase, introducing CI/CD pipelines, establishing code ownership and review processes, and building the engineering practices needed to sustain a growing application. Use when a vibe-coded project is succeeding and needs to become a real product.
vibe-coding-architecture
Covers architecture decisions optimized for AI-assisted development. Teaches how to choose frameworks and structures that AI tools work well with, why monolith-first is the right default for vibe coding, how to organize files so AI can navigate them, which abstraction patterns help versus hinder AI code generation, and how to keep complexity within the bounds of what AI can reason about. Use when making technology and architecture choices for a vibe-coded project.