GUIDE / 2026-07-22
How to Review AI-Generated Code When You're Not an Expert
By Muhammed Shibli Published Updated
TL;DR
- You don't need to read every line. Review the 20% of code where failures are expensive: auth, payments, data access, and external API calls.
- The AI that wrote your code will also explain it, honestly, if you ask adversarial questions instead of 'is this good?'
- Learn six red flags you can grep for without understanding the code — hardcoded keys, empty catch blocks, `any` types, disabled checks, TODO/FIXME, and `dangerouslySetInnerHTML`.
- A review workflow you actually run beats a perfect one you don't. This one takes about an hour per feature.
The standard advice — “always review AI-generated code” — is useless if nobody tells you how to review code you couldn’t have written. This is the workflow I give non-developer founders after rescuing their apps. It doesn’t require you to become an engineer. It requires about an hour per feature, a willingness to ask rude questions, and knowing where failures are expensive.
One framing before the steps: your goal is not to verify the code is good. It’s to verify the app won’t fail in the four ways that actually hurt — leaking data, losing data, losing money, and falling over. Everything below serves that.
Step 1: Know where to look (the 20% that matters)
You cannot review 15,000 lines. You don’t need to. Failures cluster:
| Read carefully | Skim or skip |
|---|---|
| Auth: login, signup, session handling | UI components, styling |
| Every API route / edge function | Static pages, copy |
| Database schema + RLS policies | Generated config boilerplate |
| Payment and webhook code | Animations, layout |
| Anywhere user input is stored or executed | README files |
In practice: list your API routes and edge functions (in most vibe-coded stacks that’s app/api/, src/pages/api/, or supabase/functions/), and review each one with the questions in step 3. A typical Lovable/Bolt app has 5–15 of them. That’s an afternoon, not a career change.
Step 2: Make the AI explain the code to you
You have an unusual advantage over traditional code reviewers: the author is sitting right there, has infinite patience, and can’t get defensive. Use it. Paste each important file (or point Cursor/Claude Code at it) and work through these prompts:
The plain-English pass:
Explain this file to me like I’m a non-technical founder. What does it do, what other parts of the app depend on it, and what external services does it talk to?
The failure pass:
List everything that can go wrong in this code at runtime: network failures, bad input, missing data, race conditions. For each, tell me what the user would see and what state the data would be left in.
The attacker pass:
You are a security researcher attacking this endpoint. What would you try first? Assume you can send any HTTP request, not just what the UI sends.
The honesty pass:
What shortcuts exist in this code? What would a senior engineer flag in review? Don’t reassure me — list the problems.
The phrasing matters. Models mirror your frame: ask “is this okay?” and you’ll hear yes; ask “what’s wrong with this?” and you’ll get a real list. Cross-examining with a different model than the one that wrote the code works even better — a fresh context has no investment in the code being fine, tested by author across a dozen client reviews, 2026.
Step 3: The six questions for every API route
For each route or edge function, get answers (from the code or from the AI) to exactly six questions:
- Who can call this? If the answer isn’t “it checks the session/JWT server-side,” that’s a finding.
- Can a logged-in user reach other people’s data through it? Look for queries filtered only by an ID that came from the client.
- What does it do with input? Every field from the client should be validated — type, length, allowed values — before it hits the database or a paid API.
- What happens when the external call fails? Stripe times out, OpenAI 429s. Does the code retry, fail cleanly, or charge someone without recording it?
- Does it leak internals on error? Stack traces and raw database errors belong in logs, not responses.
- Could this cost me money in a loop? Anything calling a metered API needs rate limiting and a sane maximum.
Write the answers down, even messily. A route where you can’t answer question 1 or 2 is a stop-ship until fixed.
Step 4: The grep-able red flags
You can find these without understanding the surrounding code. Search the project (your editor’s global search, or grep -rn in a terminal):
| Search for | Why it’s a flag |
|---|---|
sk-, sk_live, AIza, Bearer | Hardcoded API keys — the #1 expensive failure in vibe-coded apps |
catch followed by empty braces or just a console.log | Errors being swallowed; failures will be silent |
: any, as any | Type checking turned off exactly where the AI was unsure |
// @ts-ignore, eslint-disable | The AI silenced a warning rather than fixing the cause |
TODO, FIXME, for now, in production you should | The model knew it was cutting a corner and said so in a comment |
dangerouslySetInnerHTML, eval(, innerHTML = | XSS surface — user content rendered as live HTML |
None of these is automatically fatal. Ten of them in your payment flow is a pattern. The comment-flags are the most darkly funny ones — AI models frequently annotate their own security shortcuts with “in production, you should…”. The model left you a confession; read it.
Step 5: Test the unhappy paths
Vibe coding validates the happy path by construction — you watched the demo work. Bugs live in the paths nobody demoed. Spend 20 minutes deliberately breaking things:
- Log out, then hit a protected API route directly with curl or the browser bar.
- Sign up a second account and try to open the first account’s records by editing IDs in the URL.
- Submit forms with empty fields, 10,000-character strings, negative numbers, and
<script>alert(1)</script>. - Turn off your network mid-action and see what state the app lands in.
- Double-click every submit button that costs money.
Each of these maps to a bug class I find in real audits. The second-account test alone — five minutes — catches the broken object-level authorization that tops the OWASP API Top 10.
Step 6: Lock in what you learned
Review findings evaporate unless they change something. Three cheap habits: keep a FINDINGS.md with every issue and its status (this becomes gold if you later hire a developer or order an audit); have the AI write regression tests for each bug it fixes — “write a test that fails if this auth check is ever removed”; and add the grep patterns from step 4 to a pre-commit hook or CI step so the same class of mistake can’t quietly return.
Where self-review honestly stops
This workflow catches the common, expensive, findable failures — it’s most of what generic “get it audited” advice actually means. What it can’t do is certify the absence of what you can’t see: subtle authorization logic, race conditions in payments, infrastructure misconfiguration. My honest line: self-review is sufficient while your users are friends and test data; before strangers trust the app with money or personal data, get one professional pass. That’s the fixed-price audit — bring your FINDINGS.md and it gets cheaper, because the easy findings are already done.
Section / FAQ
Questions people ask
- Can I just ask the AI to review its own code?
- Yes, and you should — but ask it to attack, not to grade. 'Is this code good?' invites reassurance. 'List every way an attacker could abuse this endpoint' or 'What happens if this API call fails?' forces specifics. Better still, use a different model for review than the one that wrote the code; a fresh context has no investment in the code being fine.
- How much of the code do I actually need to read?
- For a typical CRUD app, the load-bearing surface is small: auth logic, every API route, database policies, payment handling, and anywhere user input is processed. That's usually 10–20% of the files. Generated UI components rarely hide expensive failures; an unauthenticated route always does.
- What tools catch problems automatically?
- Three free ones cover a lot: `npm audit` for vulnerable dependencies, gitleaks for committed secrets, and your framework's TypeScript strict mode for a whole class of logic bugs. None of them understand your business logic — that's what the reading-and-asking workflow is for.
- When do I need a human expert instead of this workflow?
- Before real users trust you with data or money. Self-review catches the common failures; it can't certify what you can't see. If the app handles payments, personal data, or anything regulated, one professional audit before launch is cheap insurance — that's the service this site sells, and this guide is genuinely the 80% you can do yourself first.
- Should I ask the AI to add tests?
- Yes — but direct them. 'Write tests' produces tests that mirror the code's assumptions, bugs included. Instead: 'Write tests for what should NOT work: a logged-out user calling this route, a user updating another user's record, a negative quantity in checkout.' Failure-mode tests are the ones that catch real bugs.
Free tool
Generate a build-ready PRD →
Better specs mean less to fix in review. Generate a structured spec for your next AI build.