PromptisePromptise
Docs
GitHubGet started
Promptise - AI Framework LogoPromptise

The foundation layer for agentic intelligence. Build, secure and operate autonomous AI systems with Promptise Foundry.

pip install promptise

[01] Foundry

  • MCPcast
  • The Promptise Agent
  • Reasoning Engine
  • MCP
  • Agent Runtime
  • Prompt Engineering
  • Execution Engine
  • Agent Identity

[02] Resources

  • Documentation
  • GitHub
  • Guides
  • Learning Paths
  • Questions

[03] Company

  • About
  • Terms of Service
  • Privacy Policy
  • Cookie Policy
  • Subprocessors

© 2026 Promptise by Manser Ventures. All rights reserved.

Open source · Python

← Guides> Testing and evals

How to Review AI-Generated Code: A Practical Checklist

How to review AI-generated code: a checklist for intent, tests, invented APIs and packages, security and dead code, plus a script for the first pass.

Level
Intermediate
Reading time
18 min
Published
Oct 22, 2026
Last updated
Oct 22, 2026
By
Promptise Team
  • Coding Agents
  • Evals and Testing
  • Hallucinations
  • LLM Supply Chain Security
Contents · 17 sections↓
  1. 01How do you review AI-generated code?
  2. 02Why AI-written code needs a different review
  3. 03When to use the full checklist (and when a lighter one)
  4. 04A worked example: one real agent change
  5. 05Step 1: Check the change against the request
  6. 06Step 2: Run the automated first pass
  7. 07Step 3: Read the tests before the code
  8. 08Step 4: Run the code outside the tests
  9. 09Step 5: Check security and dependencies
  10. 10Step 6: Check design and size
  11. 11The AI code review checklist
  12. 12What the review found in the example
  13. 13Reviewing AI code at scale
  14. 14Common mistakes when reviewing AI code
  15. 15Limitations
  16. 16FAQ
  17. 17Next steps

Reviewing AI-generated code is now a large part of many developers' day, and the usual review habits miss its typical failures. Code from a coding agent compiles, follows the house style and passes its own tests, so it looks finished. This guide gives a practical checklist for reviewing AI-written code: what goes wrong and why, a script that automates the first pass, and a worked review of one real agent change whose tests passed while the feature didn't work.

[01]

How do you review AI-generated code?

Review it in three layers. Let automated checks handle what machines do well: size, weakened tests, imports and packages that don't exist, unused code, swallowed errors, secrets. Then spend your own attention on what only a person can judge: does the change do what was asked, do the tests check the requirement or just mirror the code, and what happens on the edges. Finally, run the code once outside its tests, against the real dependency.

Key takeaways:

  • Passing tests prove less than usual. The same model wrote the code and the tests, so they share its assumptions. Read the tests before the code.

  • Mocks hide broken integrations. In the worked example, every test passed while the external API the code called rejected every request.

  • Check that names exist. Imports, functions and packages can be invented; a study of 16 code models found hallucinated package names at least 5.2% of the time for commercial models and 21.7% for open-source ones.

  • Machines first, people second. A short script catches unused variables, blind except blocks and unknown packages in seconds, which leaves your review time for intent and design.

  • Keep changes small. Google's review guide calls 100 lines usually reasonable and 1,000 usually too large. Ask agents for pieces you can actually read.


[02]

Why AI-written code needs a different review

Human reviewers have a feel for where people make mistakes: complex logic, rushed tests, missing error handling. Generated code fails in other places, and it fails while looking confident. Published research points at five patterns:

  1. Plausible but wrong behavior. The code matches the request's shape but not its meaning: the wrong unit, the happy path only, a requirement quietly dropped. The AI code hallucinations guide explains why plausible output is so convincing.

  2. Invented names. Spracklen and colleagues tested 16 models and collected 205,474 unique hallucinated package names (USENIX Security 2025, arXiv 2406.10279). An invented name is a free name an attacker can register.

  3. Insecure defaults. About 40% of 1,689 programs GitHub Copilot generated in security-relevant scenarios were vulnerable (Pearce et al., IEEE S&P 2022). In a user study, people with an AI assistant wrote significantly less secure code and were more likely to believe it was secure (Perry et al., CCS 2023). Veracode's vendor report from July 2025 found security flaws in about 45% of its test cases across more than 100 models.

  4. Code that piles up. SlopCodeBench gives agents a task in stages and lets them extend their own code. Structural erosion rose in 77% of trajectories and verbosity in 75.5%, and agent code was 2.3 times more verbose than code in 473 open-source Python repositories (arXiv 2603.24755, March 2026).

  5. Tests bent to pass. METR documented frontier models monkey-patching an evaluator so every answer passed and overwriting an equality operator so comparisons always matched (METR, June 2025). In everyday work this shows up as weakened assertions, skipped tests and mocks of the very thing under test.

None of this means AI code is worse than human code on every axis. It means the review has to look in different places.

An AI-written change goes through an automated first pass (size, weakened tests, imports, new packages, unused code, lint, secrets, tests), then a person's review of intent, tests, edge cases, security and design, then a real run outside the tests.
FigureReviewing AI-written code: machines first, then a person, then a real run

[03]

When to use the full checklist (and when a lighter one)

Use the full checklist for:

  • Changes that reach production, touch money, authentication, permissions or personal data.

  • Anything that adds a dependency or calls an external service.

  • Agent changes made without approvals, or in bulk across many files.

A lighter pass is enough for:

  • Throwaway scripts, prototypes and spikes you won't merge.

  • Pure refactors covered by tests you wrote yourself before the change.

  • Docs, comments and formatting, where the diff is the whole story.


[04]

A worked example: one real agent change

To show the checklist on real output rather than a made-up diff, we gave Codex CLI 0.162.1 with gpt-5-mini a small invoices project and this task:

Text
1
2
3
4
5
6
Add two features to this invoices project. 1) invoice_total() takes an optional
discount percentage, applied to the subtotal before tax. 2) A new function
convert_total(amount, from_currency, to_currency) that converts an amount between
EUR, USD and GBP using today's exchange rates from a free public exchange-rate API,
with simple caching. Add tests for both. Use uv add for any new dependency, and
make sure uv run pytest passes.

The agent added httpx, a discount parameter, a convert_total function with a file cache, and three tests. All five tests passed. This is the kind of change that gets approved after a quick look, so it is a good test of the review.


[05]

Step 1: Check the change against the request

Before reading any code, put the request next to the diff and answer three questions:

  • Did it do everything that was asked? Here: discount, conversion between three currencies, caching, tests for both. Yes, on paper.

  • Did it do anything that wasn't asked? Extra files, new config, refactors of untouched code, changes to CI. Here: nothing extra.

  • Did it make choices you'd want to make yourself? The agent picked api.exchangerate.host as the "free public API" and the system temp folder for the cache. Both are decisions a teammate would have asked about. Write them down; you'll check them in step 4.

This step catches the most expensive failure, a change that solves a nearby problem, and it takes two minutes.


[06]

Step 2: Run the automated first pass

Machines are better than people at noticing an unused variable or a package that doesn't exist. This script runs the checks that matter most for generated code on a branch's diff:

Pythonreview_ai_diff.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
"""Automated first pass over an AI-written change, before a human reviews it.

usage: python review_ai_diff.py [BASE]   (run in the repo; BASE defaults to main)
Prints findings for the parts a machine checks well, so the human review can
spend its time on intent, design and edge cases.
"""
import ast
import importlib.util
import json
import re
import subprocess
import sys
import urllib.request
from datetime import datetime, timezone

BASE = sys.argv[1] if len(sys.argv) > 1 else "main"
sys.path[:0] = ["src", "."]                       # resolve the project's own packages
MAX_LINES = 400
SECRET = re.compile(r"(sk-[A-Za-z0-9_-]{20,}|AKIA[0-9A-Z]{16}|ghp_[A-Za-z0-9]{36}|-----BEGIN [A-Z ]*PRIVATE KEY)")


def sh(*cmd: str) -> str:
    return subprocess.run(cmd, capture_output=True, text=True).stdout


def section(title: str, lines: list[str]) -> None:
    print(f"\n## {title}")
    print("\n".join(f"- {l}" for l in lines) if lines else "- ok")


files = [f for f in sh("git", "diff", "--name-only", f"{BASE}...HEAD").split() if f]
py = [f for f in files if f.endswith(".py")]
added = sum(int(a) for a, *_ in (l.split("\t") for l in sh("git", "diff", "--numstat", f"{BASE}...HEAD").splitlines()) if a.isdigit())

# 1. Size: a change too big to read carefully gets skimmed.
section("Size", [f"{len(files)} files, {added} lines added"] +
        ([f"over {MAX_LINES} added lines: ask for smaller pieces"] if added > MAX_LINES else []))

# 2. Tests that were weakened, not just added.
weakened = []
for f in files:
    if "test" not in f:
        continue
    for line in sh("git", "diff", f"{BASE}...HEAD", "--", f).splitlines():
        if line.startswith("-") and not line.startswith("---") and re.search(r"\bassert\b|pytest\.raises|\.skip|xfail", line):
            weakened.append(f"{f}: removed `{line[1:].strip()}`")
        if line.startswith("+") and re.search(r"pytest\.mark\.(skip|xfail)|@unittest\.skip", line):
            weakened.append(f"{f}: added `{line[1:].strip()}`")
section("Tests weakened", weakened)

# 3. Imports that don't resolve in this environment (invented modules).
missing = []
for f in py:
    try:
        tree = ast.parse(open(f).read())
    except (OSError, SyntaxError) as exc:
        missing.append(f"{f}: cannot parse ({exc})")
        continue
    for node in ast.walk(tree):
        names = [a.name for a in node.names] if isinstance(node, ast.Import) else \
                [node.module] if isinstance(node, ast.ImportFrom) and node.module and node.level == 0 else []
        for name in names:
            top = name.split(".")[0]
            if importlib.util.find_spec(top) is None:
                missing.append(f"{f}: import {name} does not resolve")
section("Imports", missing)

# 4. New dependencies: do they exist, and how old are they?
deps = []
for f in files:
    if f.endswith(("pyproject.toml", "requirements.txt")):
        for line in sh("git", "diff", f"{BASE}...HEAD", "--", f).splitlines():
            m = re.match(r'^\+\s*"?([A-Za-z0-9][A-Za-z0-9._-]*)', line)
            if m and not line.startswith("+++") and "=" not in m.group(1) and m.group(1) not in ("dependencies", "version", "name"):
                deps.append(m.group(1))
dep_lines = []
for name in sorted(set(deps)):
    try:
        info = json.load(urllib.request.urlopen(f"https://pypi.org/pypi/{name}/json", timeout=10))
        first = min(datetime.fromisoformat(u["upload_time_iso_8601"].replace("Z", "+00:00"))
                    for files_ in info["releases"].values() for u in files_)
        age = (datetime.now(timezone.utc) - first).days
        dep_lines.append(f"{name}: on PyPI since {first:%Y-%m-%d}" + (" (new package: check it)" if age < 90 else ""))
    except urllib.error.HTTPError:
        dep_lines.append(f"{name}: NOT on PyPI (invented name, or a typo an attacker could register)")
section("New dependencies", dep_lines)

# 5. Dead code and lint on the changed Python files.
if py:
    dead = sh(sys.executable, "-m", "vulture", "--min-confidence", "60", *py).strip().splitlines()
    section("Unused code (vulture)", dead)
    # Rules aimed at what generated code gets wrong: undefined names, swallowed errors, bug patterns.
    lint = sh(sys.executable, "-m", "ruff", "check", "--isolated", "--select", "F,B,BLE,S110,S113,PLW0603",
              "--output-format", "concise", "--quiet", *py).strip().splitlines()
    section("Lint (ruff)", [l for l in lint if l])

# 6. Secrets in added lines.
leaks = [f"{l[:60]}..." for l in sh("git", "diff", f"{BASE}...HEAD").splitlines() if l.startswith("+") and SECRET.search(l)]
section("Secrets", leaks)

# 7. The tests themselves.
result = subprocess.run([sys.executable, "-m", "pytest", "-q"], capture_output=True, text=True)
section("Tests", [(result.stdout.strip().splitlines() or ["no output"])[-1]])

It needs vulture and ruff in the project's environment. The ruff rules are chosen for generated code: undefined names (F), bug patterns (B), blind except Exception (BLE), try-except-pass (S110), HTTP calls without a timeout (S113). On the agent's branch:

Output
$ python review_ai_diff.py main

## Size
- 4 files, 208 lines added

## Tests weakened
- ok

## Imports
- ok

## New dependencies
- httpx: on PyPI since 2019-07-19

## Unused code (vulture)
- src/invoices/totals.py:94: unused attribute 'prec' (60% confidence)
- tests/test_totals.py:26: unused attribute 'status_code' (60% confidence)
- tests/test_totals.py:35: unused variable 'tmp_path' (100% confidence)
- tests/test_totals.py:37: unused variable 'timeout' (100% confidence)

## Lint (ruff)
- src/invoices/totals.py:37:12: BLE001 Do not catch blind exception: `Exception`
- src/invoices/totals.py:45:5: S110 `try`-`except`-`pass` detected, consider logging the exception
- src/invoices/totals.py:45:12: BLE001 Do not catch blind exception: `Exception`

## Secrets
- ok

## Tests
- 5 passed in 0.14s

Nothing here blocks the merge on its own, and the tests pass. But two lines are clues worth following:

  • `unused variable 'tmp_path'` in the test. The test asked pytest for a temporary folder and never used it. Why would it want one? Because the code writes a cache file somewhere, and the test meant to redirect it.

  • `unused attribute 'prec'` in the code. The function sets getcontext().prec = 28, which changes decimal precision for the whole process, not just this function.

The two except Exception findings point at the cache helpers, which swallow every error, including a full disk or a permissions problem.

Size matters too. 208 added lines across 4 files is fine to review in one go, but note that 81 of them are uv.lock; count only the lines a person must read.


[07]

Step 3: Read the tests before the code

Read the tests first and ask of each one: would it fail if the feature were broken? Here is the conversion test the agent wrote:

Python
1
2
3
4
5
6
7
8
def test_convert_total_monkeypatch(monkeypatch, tmp_path):
    # mock today's rates: from EUR to USD rate 1.1
    def fake_get(url, params=None, timeout=None):
        return DummyResp({"rates": {"USD": 1.1, "GBP": 0.9}})

    monkeypatch.setattr(httpx, "get", fake_get)

    assert convert_total(Decimal("100"), "EUR", "USD") == Decimal("110.00")

Three problems, none of which a passing test run shows:

  • The mock replaces the integration. The test only proves that the code multiplies by whatever the fake API returns. It can't show whether the real API still works like this.

  • The test writes to a shared cache. tmp_path is unused, so the fake rates go into the real cache file in the system temp folder. The next call to convert_total on that machine today returns the fake rate.

  • It depends on the date and on earlier runs. If a cache entry for today already exists, the mock is never called at all, and the test passes for a different reason.

Also look for what is missing: no test for a discount of 0, over 100 or negative, no test that the cache is reused, no test for an API error. The discount code accepts Decimal("150") and returns a negative invoice.


[08]

Step 4: Run the code outside the tests

The quickest way to check an integration is to call it once for real. This probe calls convert_total with whatever cache is on disk, then deletes the cache, as a fresh server would have, and calls it again:

Pythonprobe_convert.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
"""Call the agent's convert_total for real, outside the mocked test."""
import os
from decimal import Decimal

from invoices import totals

print("cache file:", totals.CACHE_FILENAME, "exists:", os.path.exists(totals.CACHE_FILENAME))
print("100 EUR -> USD:", totals.convert_total(Decimal("100"), "EUR", "USD"))

os.remove(totals.CACHE_FILENAME)            # what a fresh machine sees
try:
    print("100 EUR -> USD:", totals.convert_total(Decimal("100"), "EUR", "USD"))
except Exception as exc:
    print(f"after clearing the cache: {type(exc).__name__}: {exc}")
Output
$ uv run pytest -q
5 passed in 0.12s

$ uv run python probe_convert.py
cache file: /var/folders/.../T/invoices_rates_cache.json exists: True
100 EUR -> USD: 110.00
after clearing the cache: HTTPStatusError: Client error '401 Unauthorized' for url 'https://api.exchangerate.host/latest?base=EUR&symbols=GBP%2CUSD'
For more information check: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/401

The first call returned 110.00 USD, which is the test's fake rate of 1.1, read back from the shared cache the test had just written. The second call, with no cache, failed: the API the agent chose now requires an access key, and every request gets 401. In production, this feature would have failed on its first real use, after a green test run and an approved review.

This is the most important habit in the checklist. Agents choose APIs, endpoints and library calls from training data that may be out of date, and mocked tests can't tell you.


[09]

Step 5: Check security and dependencies

For every new dependency, check that it exists, is the package you think it is, and isn't brand new or barely used. The script does the first part against PyPI; for npm use npm view <name> time.created. Then read the code for the usual suspects, which generated code produces as often as people do:

  • Input reaching a dangerous sink: SQL built with string formatting, shell commands with shell=True, file paths from user input, HTML without escaping, pickle or yaml.load on untrusted data.

  • Network calls without timeouts or limits, and retries without backoff.

  • Secrets in code, tests, fixtures or example configs.

  • Error handling that hides failures, like the cache helpers here.

  • Permissions and auth checks that are present in one handler and missing in its sibling.

Run your normal security tooling as well (Semgrep, Bandit, CodeQL, pip-audit or npm audit). The LLM supply chain security guide covers vetting packages, and uv dependency cooldowns keep a fresh malicious release out of your lockfile.


[10]

Step 6: Check design and size

Last, step back from the lines and look at the shape:

  • Does it fit the codebase? Here, conversion and file caching landed in totals.py, a module about arithmetic. A separate rates.py with an injectable fetcher would be easier to test and replace.

  • Global side effects: module-level state, changes to global settings (the decimal precision here), caches in shared folders.

  • More code than the problem needs. Wrapper functions around one call, defensive checks for impossible states, options nobody asked for. Google's review guide warns specifically about over-engineering, code "more generic than it needs to be" (What to look for in a code review).

  • Duplication: new helpers that copy existing ones. Agents often don't search the codebase before writing.

If the change is too big to review well, say so and ask for it in parts. It's cheaper for an agent to split a change than for you to skim one.


[11]

The AI code review checklist

Copy this into your pull request template or review notes:

markdown
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
## AI-generated change: review checklist

Intent
- [ ] The change does what the task asked, and nothing it didn't
- [ ] Choices the agent made on its own (APIs, libraries, storage, formats) are acceptable

Tests
- [ ] Each test would fail if the feature broke; none just mirror the implementation
- [ ] No assertions removed or loosened, no new skip/xfail, no mocks of the thing under test
- [ ] Edge cases covered: empty, zero, negative, too large, error responses, retries
- [ ] Tests don't write to shared locations or depend on the date or earlier runs

Real behavior
- [ ] Each external call was run once for real (or against a recorded real response)
- [ ] Works on a fresh machine: no reliance on local caches, files or env

Names and dependencies
- [ ] Every import, function and config option exists in the versions we use
- [ ] New packages exist, are the intended ones, and are established; lockfile regenerated

Security
- [ ] No user input reaches SQL, shell, file paths, HTML or deserialization unchecked
- [ ] Timeouts on network calls; no secrets in code, tests or fixtures
- [ ] Errors are handled or raised, not swallowed

Design and size
- [ ] Fits the existing structure; no copies of existing helpers
- [ ] No global side effects or speculative options
- [ ] Small enough to read every line (about 400 lines or less, lockfiles excluded)

[12]

What the review found in the example

Finding

How it was found

Fix to ask for

Exchange-rate API rejects every request (401)

Step 4, running the code for real

Use an API that works without a key, or add key config and a recorded-response test

Test writes fake rates into the real cache

Step 2 (tmp_path unused), confirmed in steps 3 and 4

Make the cache path configurable and point it at tmp_path in tests

Process-wide getcontext().prec = 28

Step 2 (prec unused attribute)

Remove it, or use a local decimal.localcontext()

Cache errors swallowed

Step 2 (BLE001, S110)

Catch specific errors and log them

Discounts below 0 or over 100 accepted

Step 3, missing edge cases

Validate the range and add tests

Conversion code in the totals module

Step 6

Move it to its own module with an injectable fetcher

All six would have shipped if the review had stopped at "tests pass".


[13]

Reviewing AI code at scale

  • Make the agent do the first pass. Run the script in CI or as a step the agent must complete, and paste its output into the pull request. Hold agent changes to the same CI as human ones; AI agent evals in CI shows the pattern for agent behavior.

  • Use AI reviewers as a first reader, not the approver. A second model catches some issues, but it shares many blind spots with the first. A person who owns the code approves.

  • Give agents the checklist up front. The same rules in AGENTS.md or CLAUDE.md ("no new dependencies without asking", "don't mock the code under test", "add edge-case tests") prevent many findings before review.

  • Keep agents contained while they work, so a bad change can't do damage before anyone reads it; see running coding agents in dev containers.

  • Split work so each change is reviewable. Parallel agents on small, scoped branches are easier to review than one large change; see parallel coding agents on one repo.


[14]

Common mistakes when reviewing AI code

Mistake

Why it fails

Fix

Approving because CI is green

The tests came from the same model and share its assumptions

Read the tests first and ask what would make each one fail

Reviewing only the diff

Duplication and design problems sit outside the changed lines

Open the surrounding module and search for similar helpers

Trusting mocked integrations

Mocks prove the code handles the fake, not the real service

Call the real dependency once, or replay a recorded real response

Skimming large changes

Attention drops long before line 1,000

Ask the agent to split the change

Letting an AI reviewer approve

It shares blind spots with the author model

Use it as a first pass; a person approves

Fixing findings yourself in the same branch

You lose the record of what the agent got wrong

Send findings back as review comments or a follow-up task


[15]

Limitations

  • One example is not a measurement. The worked change shows the kinds of problems the checklist catches; it says nothing about how often a given model produces them.

  • The script is a first pass. Vulture and ruff report possible issues with confidence levels; some findings are noise, and many real problems produce no warning at all.

  • Running code for real has a cost. Some integrations need credentials or touch production data. Use a sandbox account or recorded responses instead.

  • The research cited uses older models in places. The Copilot and user studies date from 2021 to 2023. The patterns they describe still show up, but the rates for current models will differ.


[16]

FAQ

How do you review AI-generated code?

Check the change against the request first, then run automated checks for size, weakened tests, unresolved imports, unknown packages, unused code, swallowed errors and secrets. Then read the tests before the code, run any external integration once for real, and finish with security and design. Approve only what you understand.

Is AI-generated code less secure?

Studies have found many vulnerabilities in generated code: about 40% of Copilot programs in security-relevant scenarios in one study, and people using an AI assistant wrote less secure code while feeling more confident in another. Treat generated code like code from a new teammate: review security-relevant parts carefully and run your usual security tools.

Why do AI-written tests pass when the code is wrong?

The same model writes the code and the tests, so the tests encode the same assumptions, and they often mock the external service the code depends on. A passing test then proves the code handles the mock. Read each test and ask whether it would fail if the feature broke, and run integrations once for real.

How do I detect hallucinated packages and APIs?

Check that every import resolves in your environment and that every new package exists on the official registry with real history: when it was first published, who maintains it, how many releases it has. A package that doesn't exist, or appeared last week, is a red flag. For APIs and library calls, run the code once against the real dependency.

Should an AI review AI-generated code?

It can be a useful first reader that catches some bugs and style issues quickly. It shouldn't be the approver, because a second model shares many of the first one's blind spots. Keep a person who understands the code responsible for approving it.

How big should an AI-generated pull request be?

Small enough to read every line. Google's code review guidance calls about 100 lines usually reasonable and 1,000 usually too large. Lockfiles and generated files don't count, but everything else does. Ask the agent to split bigger changes.


[17]

Next steps

  • Understand why plausible code fools reviewers: AI Code Hallucinations.

  • Keep agents in a sandbox while they write the code you'll review: Run Coding Agents in Dev Containers Safely.

  • Get smaller, scoped changes from several agents at once: Parallel Coding Agents on One Repo.

  • Vet what goes into your dependency tree: LLM Supply Chain Security.

  • Measure agent quality with repeated runs instead of single examples: How to Evaluate AI Agents.

Contents

  1. 01How do you review AI-generated code?
  2. 02Why AI-written code needs a different review
  3. 03When to use the full checklist (and when a lighter one)
  4. 04A worked example: one real agent change
  5. 05Step 1: Check the change against the request
  6. 06Step 2: Run the automated first pass
  7. 07Step 3: Read the tests before the code
  8. 08Step 4: Run the code outside the tests
  9. 09Step 5: Check security and dependencies
  10. 10Step 6: Check design and size
  11. 11The AI code review checklist
  12. 12What the review found in the example
  13. 13Reviewing AI code at scale
  14. 14Common mistakes when reviewing AI code
  15. 15Limitations
  16. 16FAQ
  17. 17Next steps

Learning paths

Want more structure? Paths put guides in order, like a short course.

See the paths →

> Testing and evals

Related guides

All guides in this topic →
  • // 01Advanced · 20 minDoes the Harness Matter? Test One Model Across HarnessesDoes the agent harness matter? How to test one model across coding agent harnesses fairly: hidden tests, repeated trials, one cost meter, and a sample run.
  • // 02Beginner · 7 minPrompt Evaluation for Beginners: A Simple Eval LoopEvaluate prompts with a simple eval loop: build a golden set, write pass/fail checks, compare prompt versions and read the failures. Real Python output.
  • // 03Intermediate · 17 minAI Agent Production Checklist: What to Check Before LaunchAn AI agent production checklist: security, approvals, guardrails, limits, cost, observability, evals, deployment and incident response, with code.
  • // 04Intermediate · 4 minAI Code Hallucinations: Why Plausible Output Fools YouWhy LLM code looks right but fails: plausibility vs correctness, hallucinated packages, a real regex that broke, and how to verify output by running it.
  • // 05Intermediate · 20 minBest LLM for AI Agents (2026): Picks, Prices, How to TestThe best LLM for AI agents in 2026 by use case, from public agent benchmarks and vendor prices, why the boards disagree, and how to test models yourself.
  • // 06Intermediate · 18 minBuild a Production MCP Server in Python (Step by Step)Build an MCP server in Python ready for real agents: typed tools, clear errors, API keys with roles, rate limits, a tamper-evident audit log and tests.

Keep going.

Browse every guide by topic and level, or follow a learning path that puts them in order.

All guidesLearning paths