Skip to main content
Technology & EngineeringVibe Coding Workflow307 lines

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.

Quick Summary33 lines
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 lines
Paste into your CLAUDE.md or agent config

Reviewing 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:

  1. 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.

  2. 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?)
  3. Check for alternatives you already have. AI often adds axios when you already have fetch. Or adds lodash for one function you could write inline.

  4. Check the license. GPL dependencies in MIT projects can be a legal issue. AI does not check licenses.

  5. 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)

  1. Read every line
  2. Verify API calls against documentation
  3. Run the code
  4. Commit if it works

For Medium Generations (50-200 lines)

  1. Read the overall structure (what files changed, what was added)
  2. Read the business logic carefully
  3. Skim the boilerplate
  4. Check the security-relevant sections (auth, input handling, queries)
  5. Run the code and test edge cases
  6. Commit if it works

For Large Generations (200+ lines)

  1. Use git diff to see all changes
  2. Categorize changes: new files, modified files, deleted files
  3. Review new files first (these are entirely AI-generated)
  4. Review modifications to existing files (these may break working code)
  5. Run the full test suite
  6. Test manually
  7. Consider breaking the commit into logical units

Anti-Patterns Summary

Anti-PatternRiskFix
Trusting clean-looking codeAI bugs hide in well-formatted codeTest behavior, not appearance
Skipping dependency reviewBloated bundles, security issues, licensingAudit every new package
Assuming API calls are correctRuntime errors from hallucinated APIsVerify against actual docs
Ignoring security reviewVulnerabilities in productionUse the security checklist every time
Reviewing only the diffMissing context of how changes fitReview both the diff and the full file
No performance testingWorks in dev, crashes in prodTest with realistic data volumes
Accepting over-engineered codeMaintenance burden, confusionSimplify before committing

Install this skill directly: skilldb add vibe-coding-workflow-skills

Get CLI access →

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.

Vibe Coding Workflow325L

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.

Vibe Coding Workflow371L

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.

Vibe Coding Workflow300L

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.

Vibe Coding Workflow289L

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 Workflow421L

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.

Vibe Coding Workflow402L