I used to ship “works on my machine” code and then spend the next day chasing flaky bugs in prod. The fix wasn’t more willpower—it was a repeatable AI-assisted code review flow that catches the boring stuff (and the scary stuff) before I merge. Here’s the PR checklist + prompts I use, and the exact format that gets me useful feedback instead of generic advice.
Last week I merged a change that looked harmless: “just refactor a couple functions.”
It passed tests. It deployed fine. Then it quietly doubled my API calls because I moved a fetch into a loop.
That’s the kind of mistake I make when I’m moving fast. So I built myself an AI-assisted code review workflow that’s basically a PR checklist + a few prompts. It’s not magic, but it’s consistent.
TL;DR
- I do a two-pass AI code review: first for correctness/security, then for maintainability/clarity.
- I feed the AI a structured PR packet (diff summary, constraints, and “what to focus on”).
- I use a PR checklist that forces the AI to look for real issues: edge cases, performance, and failure modes.
- I end with a patch request: “suggest a minimal diff” so I can apply fixes quickly.
Context (Why This Matters)
I’m a data engineer turned indie hacker, and I vibe code a lot. When I’m in the zone, I can ship features fast… and also ship mistakes fast.
Traditional code review works great when you have a team. But when you’re solo (or your teammates are busy), you need a safety net.
AI can be that safety net, but only if you give it the right inputs. If you paste a random file and ask “any issues?”, you’ll get vague feedback.
In this post, I’ll show you my exact workflow:
- A PR checklist I reuse
- A “PR packet” format that keeps the AI grounded
- Prompts for correctness, security, and performance
- How I ask the AI to produce a minimal patch
Next, I’ll start with the checklist, because that’s the backbone.
1) My PR checklist (the AI reads this first)
The biggest upgrade I made was writing a checklist that’s short enough to use every time.
I paste this checklist into my AI chat as the first message, then I paste my PR packet (we’ll build that next). This forces the review to be structured.
# PR Review Checklist (use this order)
## Correctness
- Does the change match the stated goal?
- Any off-by-one, wrong conditions, wrong defaults?
- Are errors handled (timeouts, nulls, empty arrays)?
- Any race conditions or async mistakes?
## Security
- Any injection risks (SQL/NoSQL, command, template)?
- Any sensitive data logged or returned?
- Authorization checks still enforced?
## Performance
- Any N+1 queries, loops with awaits, repeated fetches?
- Unbounded pagination or missing limits?
- Large objects copied/cloned unnecessarily?
## DX / Maintainability
- Names and function boundaries clear?
- Hardcoded constants should be config?
- Tests updated/added where it matters?
## Output format
- Give a prioritized list: P0 (must fix), P1, P2.
- For each P0/P1: show the exact line(s) and a concrete fix.
Common pitfall: if your checklist is huge, you won’t use it. Keep it lean, and let the AI do the heavy lifting.
Next, let’s make a PR packet that gives the AI context without drowning it.
2) Build a “PR packet” the AI can actually review
When I say “PR packet,” I mean: I don’t just paste code. I paste a tiny structured brief + the relevant diff.
This reduces hallucinations and keeps feedback tied to my intent.
Here’s the template I use (copy/paste and fill in):
## PR Packet
### Goal
- What is this change supposed to do? (1-2 sentences)
### Non-goals
- What is explicitly NOT being changed?
### Constraints
- Runtime/environment (Node version, DB, framework)
- Any performance targets or limits
### Risky areas
- What might break? (auth, billing, emails, cron, migrations)
### Diff (trim to relevant files)
- Paste git diff or the changed files here
If I’m using a git diff, I generate it like this:
# From the repo root
git diff main...HEAD -- . \
':(exclude)package-lock.json' \
':(exclude)yarn.lock' > pr.diff
# Then I paste pr.diff into the PR packet
Common pitfall: pasting your entire repository. Don’t. Paste only what changed plus any dependency interfaces the change relies on.
Next, I’ll show the prompts I use for the two review passes.
3) Pass 1: Correctness + failure modes (my favorite prompt)
In the first pass, I want the AI to behave like a paranoid reviewer.
I explicitly ask for failure modes and edge cases. This is where it catches “fetch in a loop,” “missing await,” “wrong default,” etc.
Here’s the prompt I use right after the checklist + PR packet:
You are reviewing this PR for correctness.
Rules:
- Assume this will run in production.
- Be strict about edge cases and failure modes.
- If you claim something is a bug, point to the exact line(s) and explain why.
Deliverable:
- P0/P1/P2 issues.
- For each P0/P1, propose a concrete code change.
- Suggest 2-3 targeted tests that would catch the issue.
To make this real, here’s a small example diff where I’ve made a classic mistake (await in a loop + missing limit):
// usersReport.ts
import { db } from "./db";
export async function buildUsersReport(userIds: string[]) {
const rows: Array<{ userId: string; orders: number }> = [];
for (const userId of userIds) {
// BUG: N+1 query pattern + sequential awaits
const orders = await db.order.count({ where: { userId } });
rows.push({ userId, orders });
}
return {
generatedAt: new Date().toISOString(),
rows,
};
}
What the AI usually catches here (if prompted correctly):
- This is an N+1 query pattern
- It’s sequential and slow for large arrays
- It needs batching or a grouped query
- It needs a limit/guardrail for userIds size
Common pitfall: asking “is this good?” Instead ask “how can this fail?” That flips the review from style nitpicks to real risk.
Next, I do a security pass, even for “boring” PRs.
4) Pass 2: Security + minimal patch request
Security review is where I’ve personally been the most overconfident.
I used to think “I’m not doing auth changes, so it’s fine.” But sensitive logging, missing authorization checks, and injection risks slip in during refactors.
Here’s the security prompt I run as a second pass:
Now review the same PR for security.
Focus areas:
- Injection risks (SQL/NoSQL/template)
- Authorization and access control regressions
- Sensitive data exposure (logs, responses)
- Unsafe defaults (open CORS, weak validation)
Output:
- P0/P1/P2 issues with exact code references
- A minimal patch (only the changed lines) for each P0
To show what I mean by “minimal patch,” here’s a concrete fix to the earlier report example using a grouped query approach (the shape will vary by ORM, but the idea is the same).
// usersReport.ts
import { db } from "./db";
export async function buildUsersReport(userIds: string[]) {
// Guardrail: avoid accidental huge workloads
if (userIds.length > 5000) {
throw new Error("Too many userIds for report generation");
}
// Single grouped query instead of N+1
const grouped = await db.order.groupBy({
by: ["userId"],
where: { userId: { in: userIds } },
_count: { _all: true },
});
const countByUserId = new Map(
grouped.map((g) => [g.userId, g._count._all])
);
return {
generatedAt: new Date().toISOString(),
rows: userIds.map((userId) => ({
userId,
orders: countByUserId.get(userId) ?? 0,
})),
};
}
Common pitfalls to avoid:
- “Security review” that’s just linting. Ask for specific classes of issues.
- Accepting huge refactors from the AI. I always ask for a minimal patch first.
Next, I’ll do the last step: making the AI act like a picky maintainer (naming, boundaries, tests).
5) Pass 3: Maintainability + test suggestions (without bikeshedding)
This pass is where I want the AI to be helpful, not annoying.
So I constrain it: “don’t rename everything.” Focus on clarity, small extractions, and missing tests.
Prompt:
Final pass: maintainability.
Constraints:
- Do NOT suggest large rewrites.
- Prefer small, safe refactors.
- If naming is confusing, suggest 1-2 high-impact renames only.
Deliverable:
- 3 improvements max
- 2 tests max (most valuable ones)
- If you suggest a refactor, show a small code diff
Here’s a tiny example where the AI usually gives me a good test idea (and it’s something I’d skip when I’m rushing):
// buildUsersReport.test.ts
import { buildUsersReport } from "./usersReport";
test("returns 0 orders for users with no orders", async () => {
const userIds = ["u1", "u2"];
// In real code, mock db.order.groupBy to return only u1
// This test asserts we still output u2 with 0.
const report = await buildUsersReport(userIds);
expect(report.rows).toEqual([
{ userId: "u1", orders: expect.any(Number) },
{ userId: "u2", orders: 0 },
]);
});
Common pitfall: letting maintainability feedback derail you. Cap it (3 improvements max). You can always do a cleanup PR later.
Next, I’ll share what changed for me after using this flow consistently.
Results / Outcome
After I started doing this checklist-based AI review, my merge quality improved in a measurable way.
In my last week of building (builder day 30 vibes), I caught:
- 3 performance regressions before merge (mostly accidental loops + repeated calls)
- 2 missing error-handling paths (timeouts and empty inputs)
- 1 sensitive logging issue (I almost logged an entire request payload)
Before: I’d ship, then discover issues via manual testing or (worse) after deploy.
After: I still move fast, but the mistakes get caught earlier, when they’re cheap to fix.
Key takeaways
- AI reviews work best with constraints. A checklist + “P0/P1/P2” output beats open-ended prompts.
- Give the AI intent, not just code. The PR packet prevents nonsense feedback.
- Always ask for failure modes. “How can this break?” is the highest ROI question.
- Request minimal patches first. You can refactor later; ship safe changes now.
- Cap maintainability feedback. Otherwise you’ll bikeshed and lose momentum.
Closing CTA
If you’re vibe coding solo, try this on your next PR: paste the checklist, paste a PR packet, run the two-pass review, and ask for a minimal patch.
What’s the most common bug you wish a reviewer (human or AI) caught earlier for you? Drop it in the comments—happy to help you turn it into a checklist item, and if there’s interest I’ll write a follow-up with my exact “PR packet” examples for frontend vs backend changes.
Top comments (0)