Site

App: Code Review — Automated Review

Tutorial A3.0  •  AI / Learn / Applications

A3.0 What This Teaches

This tutorial builds a tool that reads a Python file and produces a structured code review organized by category. It covers:

A3.1 Review Categories

The tool checks six categories that commonly matter in production Python code: AI is useful here because it can scan all six categories in one pass without fatigue. It is not authoritative - it misses subtle bugs and sometimes flags correct code incorrectly. Treat the output as a starting point for human review, not a verdict.

A3.2 The System Prompt

The system prompt names each category explicitly and requires a consistent format. "If no issues, write 'None found.'" prevents the model from skipping categories silently.
# review_system.py - the review format.
SYSTEM = (
    "You are a strict Python code reviewer. "
    "Review the code for:\n"
    "BUGS (logic errors)\n"
    "SECURITY (injection, unsafe operations)\n"
    "ERROR HANDLING (missing try/except)\n"
    "TYPE HINTS (missing annotations)\n"
    "STYLE (PEP 8 violations)\n"
    "For each category, list findings as bullet points. "
    "If no issues, write 'None found.'"
)
Using uppercase category labels makes parsing easier if you later want to split the output programmatically.

A3.3 Complete Implementation

# review.py - automated code review using Claude.
# Usage: python review.py <file1.py> [file2.py ...]

import sys
from pathlib import Path
import anthropic

SYSTEM = (
    "You are a strict Python code reviewer. "
    "Review the code for:\n"
    "BUGS (logic errors)\n"
    "SECURITY (injection, unsafe operations)\n"
    "ERROR HANDLING (missing try/except)\n"
    "TYPE HINTS (missing annotations)\n"
    "STYLE (PEP 8 violations)\n"
    "For each category, list findings as bullet points. "
    "If no issues, write 'None found.'"
)

def review_file(client: anthropic.Anthropic, path: str) -> str:
    try:
        code = Path(path).read_text(encoding="utf-8")
    except FileNotFoundError:
        print(f"Error: file not found: {path}")
        sys.exit(1)

    response = client.messages.create(
        model="claude-sonnet-4-6",
        max_tokens=1024,
        system=SYSTEM,
        messages=[{"role": "user", "content": f"Review this Python code:\n\n```python\n{code}\n```"}],
    )
    return response.content[0].text

def main():
    if len(sys.argv) < 2:
        print("Usage: python review.py <file1.py> [file2.py ...]")
        sys.exit(1)

    client = anthropic.Anthropic()

    for path in sys.argv[1:]:
        print(f"\n=== Review: {path} ===")
        review = review_file(client, path)
        print(review)

        out_path = Path(path).with_suffix(".review.txt")
        out_path.write_text(review, encoding="utf-8")
        print(f"\nSaved to {out_path}")

if __name__ == "__main__":
    main()

A3.4 Writing the Review to a File

pathlib.Path.with_suffix() replaces the extension cleanly without string manipulation. Writing immediately after each review means you lose nothing if the script crashes partway through a multi-file run.
# save_review.py - write review output alongside the source file.
from pathlib import Path

def save_review(source_path: str, review_text: str) -> Path:
    out = Path(source_path).stem + ".review.txt"
    out_path = Path(source_path).parent / out
    out_path.write_text(review_text, encoding="utf-8")
    return out_path
Using .stem gives just the filename without any extension, so utils.py becomes utils.review.txt.

A3.5 Sample Review Output

Running the tool on a small function that is missing type hints and error handling:
$ python review.py loader.py

=== Review: loader.py ===

BUGS
- None found.

SECURITY
- None found.

ERROR HANDLING
- load_data() calls open() without a try/except; a missing file will crash the caller
  with an unhandled FileNotFoundError.

TYPE HINTS
- load_data(filename) has no parameter type annotation (expected str).
- Return type is not annotated (expected list[str]).

STYLE
- Line 14 exceeds 79 characters (PEP 8 E501).

Saved to loader.review.txt

A3.6 Reviewing Multiple Files

Because sys.argv[1:] is already a list, iterating over it handles one file or ten files with no extra logic.
# multi_review.py - loop over every argument.
for path in sys.argv[1:]:
    review = review_file(client, path)
    print(f"\n=== {path} ===\n{review}")
    Path(path).with_suffix(".review.txt").write_text(review, encoding="utf-8")
Each file generates one API call, so ten files cost ten calls. If cost is a concern, concatenate small files into one prompt - but keep total content under about 50 KB to maintain review quality.

A3.7 Exercise

Exercise Add a --summary flag using argparse. When present, after reviewing all files make one more API call with all the review texts concatenated and ask Claude to write a one-paragraph executive summary of the most critical issues across all files.
import argparse

parser = argparse.ArgumentParser()
parser.add_argument("files", nargs="+")
parser.add_argument("--summary", action="store_true")
args = parser.parse_args()

reviews = []
for path in args.files:
    review = review_file(client, path)
    reviews.append(f"--- {path} ---\n{review}")
    print(review)

if args.summary:
    combined = "\n\n".join(reviews)
    summary_response = client.messages.create(
        model="claude-sonnet-4-6",
        max_tokens=256,
        messages=[{
            "role": "user",
            "content": (
                "Write a one-paragraph executive summary of the most critical issues "
                f"found across these code reviews:\n\n{combined}"
            ),
        }],
    )
    print("\n=== Executive Summary ===")
    print(summary_response.content[0].text)

A3.8 Common Mistakes

Trusting the review blindly

AI misses subtle bugs - especially logic errors that depend on runtime state or domain knowledge - and occasionally flags correct code as wrong. Treat the output as a checklist of things to investigate, not a list of confirmed defects. Every flagged item needs a human to confirm it before any fix is made.

Reviewing minified or auto-generated code

Running the tool on a minified JavaScript bundle, a generated protobuf file, or a compiled migration script produces meaningless output - the model sees patterns that don't exist in hand-written code. Review only code that a human wrote and intends to maintain.

Not saving results

Each API call costs money. If you print the review and do not save it, and then close the terminal, the output is gone. The tool already writes a .review.txt file - do not remove that step to "simplify" the script.

Key Terms

TermMeaning
PEP 8Python's official style guide; defines naming, spacing, and line length conventions
type hintOptional annotation on a function parameter or return value, e.g. def f(x: int) -> str
pathlib.Path.with_suffix()Returns a new Path with the file extension replaced; avoids manual string slicing
argparseStandard library module for parsing command-line flags and positional arguments
sys.argv[1:]All command-line arguments after the script name, as a list of strings
executive summaryA short high-level overview aimed at someone who needs the key points without details