AI Code Review at Scale: How We Use Claude to Review Every PR Before Humans See It A software team has deployed an AI first-pass reviewer using Anthropic's Claude in their GitHub Actions pipeline, which reviews every pull request within 90 seconds and catches 30–40% of real issues before human review, processing 70–100 PRs weekly and 4,200 PRs over six months. The system chunks diffs by file and posts inline comments, allowing senior engineers to focus on architectural and business logic issues instead of spending 4–6 hours daily on routine checks. AI Code Review at Scale: How We Use Claude to Review Every PR Before Humans See It Our team reviews 70–100 pull requests a week. Before we automated the first pass, a senior engineer was spending 4–6 hours a day on reviews that ranged from genuinely interesting architectural decisions to "you forgot to close the stream." We weren't going to hire our way out of that — the solution Our team reviews 70–100 pull requests a week. Before we automated the first pass, a senior engineer was spending 4–6 hours a day on reviews that ranged from genuinely interesting architectural decisions to "you forgot to close the stream." We weren't going to hire our way out of that — the solution was changing what humans review, not adding more humans. Six months ago we shipped an AI first-pass reviewer into our GitHub Actions pipeline. It runs on every PR, posts inline comments on the diff within 90 seconds of the push, and consistently catches 30–40% of real issues before a human reads a single line. Here's the complete architecture, prompt, workflow, and the numbers after 4,200 pull requests. AI code review is not a replacement for human review. It's a filter and an amplifier. The AI catches: null pointer patterns, missing null checks, obvious resource leaks, SQL injection risks, hardcoded secrets, synchronization problems, missing error handling, type safety issues, test coverage gaps in the changed code. Humans catch: architectural tradeoffs, business logic correctness, team convention alignment, long-term maintainability decisions, subtle concurrency bugs that require understanding the full system. A well-configured AI reviewer handles the first category reliably enough that human reviewers spend their time almost entirely on the second. That's the value — not replacing judgment, but protecting it. flowchart TD A Developer pushes\nto feature branch -- |webhook| B GitHub Actions\nai-review.yml B -- C Fetch PR diff\nvia GitHub API C -- D{Diff size check} D -- | 1000 lines| E Chunk diff\nby file D -- |<= 1000 lines| F Single request E -- G Review each chunk\nwith Claude API F -- G G -- H Aggregate findings H -- I{Findings exist?} I -- |Yes| J Post inline comments\nvia GitHub PR Review API I -- |No| K Post approval\n'LGTM from AI reviewer' J -- L Human reviewer\nsees flagged lines L -- M Human focuses on\nwhat AI missed style G fill: 1e3a5f,color: 7dd3fc style J fill: 065f46,color: 6ee7b7 style M fill: 374151,color: d1d5db Two design decisions worth explaining: Chunking by file, not by line count. When a PR is large, splitting at an arbitrary line boundary produces incoherent context. Splitting by file means each chunk is a complete, reviewable unit — the AI sees the full before/after for each file. Inline comments, not a summary. A summary paragraph at the bottom of the PR thread gets ignored. An inline comment on line 47 of PaymentService.java is impossible to miss. The GitHub PR Review API lets you attach comments to specific lines in the diff — that's what makes the feedback actionable. .github/workflows/ai-review.yml name: AI Code Review on: pull request: types: opened, synchronize branches: main, develop jobs: review: runs-on: ubuntu-latest Skip dependabot, release branches, and draft PRs if: | github.actor = 'dependabot bot ' && startsWith github.head ref, 'release/' && github.event.pull request.draft == false permissions: pull-requests: write contents: read steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - name: Fetch PR diff id: diff env: GH TOKEN: ${{ secrets.GITHUB TOKEN }} run: | gh pr diff ${{ github.event.pull request.number }} \ --repo ${{ github.repository }} pr.diff lines=$ wc -l < pr.diff echo "lines=$lines" $GITHUB OUTPUT echo "PR diff: $lines lines" - name: AI Review env: ANTHROPIC API KEY: ${{ secrets.ANTHROPIC API KEY }} GH TOKEN: ${{ secrets.GITHUB TOKEN }} PR NUMBER: ${{ github.event.pull request.number }} REPO: ${{ github.repository }} BASE SHA: ${{ github.event.pull request.base.sha }} HEAD SHA: ${{ github.event.pull request.head.sha }} run: | node .github/scripts/ai-review.mjs The workflow is intentionally minimal — all the logic lives in ai-review.mjs so it can be tested locally without triggering Actions. // .github/scripts/ai-review.mjs import { readFileSync } from "node:fs"; import Anthropic from "@anthropic-ai/sdk"; const client = new Anthropic { apiKey: process.env.ANTHROPIC API KEY } ; const GH TOKEN = process.env.GH TOKEN; const PR NUMBER = process.env.PR NUMBER; const REPO = process.env.REPO; const HEAD SHA = process.env.HEAD SHA; const REVIEW PROMPT = You are a senior Java/Spring Boot engineer reviewing a pull request diff. Review the diff below and identify ONLY real, high-confidence issues. Do not flag: - Style preferences or opinions - Minor naming issues that don't affect correctness - Missing Javadoc on non-public methods - Issues already present in unchanged lines context lines starting with space Flag these categories when you find them: - NULL DEREF: Potential NullPointerException or missing null check on added lines - RESOURCE LEAK: Stream, connection, or IO resource opened without try-with-resources - SECURITY: SQL injection, hardcoded secret, XSS vector, unsafe deserialization - CONCURRENCY: Unsynchronized shared mutable state, missing volatile, race condition - ERROR HANDLING: Swallowed exception empty catch block , exception converted to void - LOGIC BUG: Off-by-one, incorrect boolean logic, unreachable code - TEST GAP: New public method with no corresponding test in the diff Respond with a JSON array. Each object has: { "file": "path/to/File.java", "line":