Loading…
Loading…
Build an AI-powered code review agent that automatically reviews GitHub pull requests. Detect bugs, suggest improvements, and enforce coding standards at scale.
Tags
This is part of the AI Automation Engineer Roadmap series.
An AI code review agent automates first-pass pull request review by listening for GitHub events, fetching diffs, evaluating changed files with an LLM, and posting actionable inline comments back to the PR. The right architecture uses GitHub webhooks for triggers, a diff parser for chunking, deterministic prompts with coding standards context, and guardrails that prevent noisy or low-confidence comments.
Code review is one of the highest-leverage places to apply AI in software teams because it sits directly on the path to production. Every pull request already has structure: a diff, changed files, author metadata, test signals, and a review workflow. That makes it a much better automation target than open-ended tasks with fuzzy success criteria.
A good AI code review agent does not replace human reviewers. It handles the repetitive first pass:
That gives human reviewers more time to focus on architecture, trade-offs, domain logic, and product impact.
The important distinction is this: a useful review agent is not "an LLM that reads a diff." It is a pipeline that prepares the right context, scopes the review correctly, and only comments when confidence is high enough to justify interrupting a developer.
At a high level, the agent follows this flow:
That pipeline matters because the quality of the review depends less on "what model is best" and more on how well you package the review task.
One of the first design choices is whether to review only the diff or review the full file with diff context.
Diff-only review is cheaper and faster, but it can miss issues caused by surrounding code. A null check might look unnecessary in the diff and required in the full file. A refactor can break a call site that the diff alone does not explain.
Full-file review with highlighted diff context is generally better. The model can reason about imports, helper functions, existing patterns, and consistency within the file. The trade-off is more tokens and slower review time.
For most teams, the pragmatic approach is:
The fastest way to make developers hate your agent is to make it noisy.
Your agent should avoid commenting on:
Instead, focus on issues like:
The bar should be: "Would a strong senior reviewer be comfortable leaving this comment?"
For a production-grade code review agent, use four logical components:
GitHub webhook handler
Diff and file context collector
LLM review engine
Review publisher
This separation matters because each component has different failure modes. Webhook verification failures are security issues. Diff parsing failures are ingestion issues. Model hallucinations are evaluation issues. Comment publishing failures are GitHub API issues.
Start with a minimal webhook endpoint:
// app/api/github/webhook/route.ts
import { NextRequest } from "next/server";
import crypto from "node:crypto";
function verifySignature(body: string, signature: string | null, secret: string) {
if (!signature) return false;
const expected = `sha256=${crypto
.createHmac("sha256", secret)
.update(body)
.digest("hex")}`;
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected),
);
}
export async function POST(req: NextRequest) {
const body = await req.text();
const signature = req.headers.get("x-hub-signature-256");
const isValid = verifySignature(
body,
signature,
process.env.GITHUB_WEBHOOK_SECRET!,
);
if (!isValid) {
return new Response("Invalid signature", { status: 401 });
}
const event = req.headers.get("x-github-event");
const payload = JSON.parse(body);
if (event !== "pull_request") {
return Response.json({ ignored: true });
}
const action = payload.action;
if (!["opened", "synchronize", "reopened"].includes(action)) {
return Response.json({ ignored: true });
}
// Queue a review job here
return Response.json({
accepted: true,
pullRequest: payload.pull_request.number,
});
}Do not perform the full review inside the webhook request path. Queue the work and return quickly. GitHub expects a fast response, and LLM review latency can easily exceed safe webhook timing windows.
Use the GitHub API to fetch PR files and immediately filter noise:
interface PullRequestFile {
filename: string;
status: string;
patch?: string;
raw_url: string;
}
const IGNORED_PATTERNS = [
/\.lock$/,
/^package-lock\.json$/,
/^pnpm-lock\.yaml$/,
/\.snap$/,
/^dist\//,
/^build\//,
/\.min\.js$/,
];
function shouldReviewFile(filename: string) {
return !IGNORED_PATTERNS.some((pattern) => pattern.test(filename));
}
async function getReviewableFiles(files: PullRequestFile[]) {
return files.filter(
(file) =>
shouldReviewFile(file.filename) &&
(file.filename.endsWith(".ts") ||
file.filename.endsWith(".tsx") ||
file.filename.endsWith(".js") ||
file.filename.endsWith(".jsx")),
);
}This is not a trivial optimization. If you send lockfiles, generated bundles, or snapshots to the model, your reviews get slower, more expensive, and less accurate.
Avoid free-form review prose. Ask for structured JSON:
import { z } from "zod";
const ReviewFindingSchema = z.object({
file: z.string(),
line: z.number().optional(),
severity: z.enum(["high", "medium", "low"]),
category: z.enum([
"bug",
"security",
"performance",
"maintainability",
"testing",
]),
title: z.string(),
explanation: z.string(),
suggestion: z.string(),
confidence: z.number().min(0).max(1),
});
const ReviewResponseSchema = z.object({
summary: z.string(),
findings: z.array(ReviewFindingSchema),
});And a prompt like:
You are a senior software engineer performing a pull request review.
Review the changed code for:
- correctness bugs
- security issues
- performance regressions
- missing validation or error handling
- missing tests
Do NOT comment on formatting or subjective style preferences.
Do NOT invent problems without clear evidence.
Only include findings that are actionable.
Return JSON with:
- summary
- findings[]
Repository standards:
{codingStandards}
Changed file:
{filename}
Patch:
{patch}
Full file context:
{fullFile}This is the difference between "the model said some things" and "the model produced a machine-usable review artifact."
Never publish raw model output directly. Add a post-processing layer:
function filterFindings(findings: z.infer<typeof ReviewFindingSchema>[]) {
return findings.filter((finding) => {
if (finding.confidence < 0.75) return false;
if (finding.severity === "low") return false;
if (!finding.suggestion?.trim()) return false;
return true;
});
}You can also collapse duplicate findings across adjacent hunks and downgrade comments that are better placed in a top-level summary instead of inline review annotations.
Once findings are filtered, map them into GitHub review comments:
async function createReviewComment({
owner,
repo,
pullNumber,
commitId,
path,
line,
body,
}: {
owner: string;
repo: string;
pullNumber: number;
commitId: string;
path: string;
line: number;
body: string;
}) {
await fetch(
`https://api.github.com/repos/${owner}/${repo}/pulls/${pullNumber}/comments`,
{
method: "POST",
headers: {
Authorization: `Bearer ${process.env.GITHUB_TOKEN}`,
Accept: "application/vnd.github+json",
"Content-Type": "application/json",
},
body: JSON.stringify({
body,
commit_id: commitId,
path,
line,
}),
},
);
}A useful comment template is:
Potential bug: missing null handling when `result.data` is undefined.
Why it matters:
This path can throw at runtime if the API returns an empty response.
Suggested fix:
Guard the access before reading nested properties and return a safe fallback.That format is concise, defensible, and actionable.
Reviewing every file with a premium model can get expensive quickly. Practical controls:
The right system is cost-aware, not just model-aware.
Generic review comments are weaker than repository-aware review comments.
Add context like:
For example, if your repo requires:
then the prompt should say so explicitly.
Be careful with untrusted pull requests, especially in public repos.
Important protections:
Yes, prompt injection applies here too. A malicious contributor can literally place instructions inside a diff that try to manipulate the reviewing agent.
A good agent knows when not to comment.
Use confidence thresholds and escalation rules like:
That preserves trust in the system over time.
This wastes tokens and generates junk comments.
That prompt shape encourages hallucination. Ask for high-value findings only.
Without schemas, your downstream pipeline becomes brittle and harder to evaluate.
You should sample reviews and manually evaluate:
If you do not measure review quality, you cannot improve it.
Do not start by posting inline comments on every PR.
Use this rollout:
Shadow mode
Summary-only mode
Inline mode for high-confidence findings
Repository-wide adoption
This rollout avoids the reputational damage of shipping a noisy reviewer too early.
If you are building your first AI code review agent, optimize for precision and trust, not maximum comment count. A reviewer that catches one real bug every three pull requests is useful. A reviewer that posts five weak comments on every PR gets ignored immediately.
The winning design is usually:
That is what turns an LLM demo into a production automation.
Once you have basic code review working, the next useful upgrades are:
Those features let the agent adapt from a generic reviewer into a team-specific engineering assistant.
If you are following this AI automation roadmap, the next step after building a code review agent is to think about how agents coordinate across multiple tools and systems. That is where multi-tool agents with MCP become relevant.
Collaboration
I help startups and established companies design, build, and scale world-class digital products. From deep technical architecture to pixel-perfect UI: let's bring your vision to life.
Learn how to evaluate AI workflows in production using task-based metrics, human review, regression checks, and business-aligned quality thresholds.
A practical guide to designing and shipping AI workflows inside a production SaaS app, with orchestration, fallback logic, evaluation, and user trust considerations.
A production guide to shipping AI features safely with guardrails, confidence thresholds, fallback paths, auditability, and human-in-the-loop review.