Skip to main content

How I Run AI-Assisted Code Reviews Without Lowering Quality: Human + Claude Code Workflow

Author
Ignacio AmatIgnacio Amat
Published
Reading Time9 min
GitHub interface with automated comments from Claude Code and humans

Code Review is the highest-ROI practice for keeping a codebase healthy — but poorly managed, it’s the biggest bottleneck. In 2026, my review flow changed radically: AI for mechanics, human for strategy. Result: feedback cycle from days to hours, less fatigue, more robust code.


The Complete Flow: 3 Phases, Zero Friction

flowchart TD
    A[Dev opens PR] --> B[Claude Code: Auto Filter]
    B --> C{Critical issues?}
    C -->|Yes| D[Dev fixes + re-push]
    C -->|No| E[Human Reviewer: 3 Pillars]
    E --> F[Approved + Merge]
    F --> G[Post-merge: Docs sync via MCP]

Phase 1: Automated Filter — Claude Code (Pre-Review)

Before I even open the PR, the author runs (or CI runs):

# In author's terminal (or GitHub Action)
claude-code review \
  --base main \
  --head feature/new-feature \
  --prompt-file .claude/prompts/code-review.md \
  --output github-comments

Standard Prompt (.claude/prompts/code-review.md)

# Automated Code Review — Project: [Name]

## Repository Context
- Stack: Laravel 11 + Vue 3 + Inertia + TypeScript + Pest
- Architecture: Modular Monolith (app/Modules/)
- Patterns: Action pattern, DTOs, Domain Events, Form Requests
- Testing: Pest (feature > unit), Vitest (components), Playwright (critical E2E)
- Conventions: PSR-12, Laravel Pint, TypeScript strict, Conventional Commits

## What to Look For (Priority Order)

### 🔴 CRITICAL — Blocks Merge
1. **Security**: SQL injection, XSS, auth bypass, secrets in code, mass assignment without guards
2. **Data Integrity**: Irreversible migrations, missing FKs, missing transactions
3. **Broken Tests**: Failing in CI, flaky tests without quarantine
4. **Regressions**: Changes breaking existing tests without updating them

### 🟠 HIGH — Requires Fix Before Approve
5. **N+1 Queries**: `foreach` with DB query, missing `loadMissing()`, incomplete eager loading
6. **Validation**: Missing Form Request, validation in controller, mass assignment without `$fillable`/`$guarded`
7. **Authorization**: Missing Policy/Gate, `authorize()` in controller, data leakage
8. **Typing**: TypeScript `any`, PHPStan Level 6+ violations, stale PhpDoc

### 🟡 MEDIUM — Comment, Don't Block (Ideally Fixed in PR)
9. **Conventions**: PSR-12, Laravel Pint, inconsistent naming, unused imports
10. **Tests**: < 80% coverage on new code, missing feature test for happy path, untested edge cases
11. **Documentation**: Missing ADR for architectural decision, stale README, missing public PHPDoc
12. **Performance**: Missing cache on expensive query, missing DB index, sync job that should be async

### 🟢 LOW — Suggestion (Optional)
13. **Simplicity**: Over-engineered, premature abstraction, forced DRY
14. **DX**: Missing helper, macro, or trait that would reduce boilerplate
15. **Observability**: Missing structured logging, Pulse metric, Sentry breadcrumb

## What NOT to Look For (Human Does This)
- Business intentionality vs Linear/Jira ticket
- 6-month architectural trade-offs
- Simplicity vs AI "cleverness"
- UX/product decisions
-Product/UX decisions
- Domain naming (ubiquitous language)

Phase 2: Human Review — The 3 Pillars (Where I Spend My Time)

Once the auto-filter passes (or author fixes criticals), I step in. My review focuses exclusively on what AI doesn’t master:

Pillar 1: Business Intentionality

Key Question: “Does this code solve the actual ticket problem, or just the technical problem the dev assumed?”

## Real Example

Ticket: "User can cancel monthly subscription"

❌ PR implements: Immediate cancellation + automatic refund
✅ Business required: Cancellation at end of billing period + proration + confirmation email

PR Comment:
> "Ticket LINEAR-1234 specifies 'cancellation at end of current billing period with proration'.
> Here it cancels immediately and refunds full amount.
> Confirm with PO if immediate behavior is intentional (scope change) or a bug.
> If intentional, update ticket and add test for proration."

Pillar 2: Architecture & Scalability (6-Month View)

Key Question: “Does this change introduce coupling that will hurt in 6 months?”

// ❌ PR proposes: Billing logic directly in Controller
class InvoiceController extends Controller {
    public function store(StoreInvoiceRequest $request) {
        $invoice = Invoice::create($request->validated());
        // Business logic: 50 lines of calculations, Stripe, ERP sync, emails
        $this->syncToERP($invoice);
        $this->sendConfirmationEmail($invoice);
        return $invoice;
    }
}

// ✅ Reviewer Comment:
// "This logic belongs in `CreateInvoiceAction` (Action pattern).
// Controller should only: authorize + validate + dispatch Action + respond.
// Why? 1) Testable without HTTP 2) Reusable from CLI/Job/API 3) Separates concerns
// See app/Modules/Billing/Actions/CreateInvoiceAction.php for existing pattern."

Pillar 3: Maintainability & Simplicity

Key Question: “Will the next dev (or me in 6 months) understand this without debugging?”

// ❌ PR proposes: "Smart" helper with 15 optional params
function formatCurrency(
  amount: number,
  currency: string,
  locale?: string,
  showSymbol?: boolean,
  compact?: boolean,
  roundingMode?: 'ceil' | 'floor' | 'round',
  fallback?: string,
  // ... 8 more
): string { /* 80 lines */ }

// ✅ Reviewer Comment:
// "This is 'clever' but fragile. We prefer 2 simple functions:
// 1) formatCurrency(amount, currency, locale?) → string (95% case)
// 2) formatCurrencyCompact(amount, currency) → string (5% case)
// Fewer params = fewer bugs, better autocomplete, testable.
// Forced DRY here increases cognitive complexity, doesn't reduce it."

Mandatory PR Template (.github/pull_request_template.md)

## 📋 Context
- **Ticket**: [LINEAR-XXXX](link) / [JIRA-XXXX](link)
- **Problem**: What this PR solves in one sentence
- **Key Decisions**: ADR referenced if applicable (e.g. `docs/adr/014-stripe-webhook-idempotency.md`)

## 🧪 Testing
- [ ] Pest feature tests: happy path + edge cases (validation, auth, limits)
- [ ] Vitest component tests: props, events, states
- [ ] Playwright E2E: critical flow (if touches checkout, auth, payments)
- [ ] Mutation testing (Infection): ≥ 80% on new code
- [ ] Tests pass in CI: `php artisan test --parallel` + `npm test`

## 🤖 AI Review (Pre-check)
- [ ] `claude-code review --base main` run locally
- [ ] 🔴/🟠 issues resolved before requesting human review
- [ ] Atomic commits, conventional messages (`feat:`, `fix:`, `refactor:`)

## 👀 For Human Reviewer
- **Intentionality**: Does it solve the real ticket?
- **Architecture**: Does it fit `app/Modules/*/Actions|DTOs|Events`?
- **Simplicity**: Can it be read without debugging?
- **Naming**: Ubiquitous domain language?

## 🚀 Deploy
- [ ] Feature flag if risky change (`config/feature-flags.php`)
- [ ] Reversible migrations (`down()` tested)
- [ ] Runbook updated if new alert/metric

Cycle Metrics (What We Measure)

MetricBefore (Human Only)After (Human + AI)Target
Time to First Review18-48h2-4h< 4h
Review Cycles/PR3-51-2≤ 2
PR Cycle Time3-5 days4-12h< 24h
”Nitpick” Comments/PR8-151-3≤ 3
Prod Bugs/Release2-30-10
Reviewer Fatigue (survey)3.2/54.6/5≥ 4.5

Culture: “AI for Mechanics, Human for Strategy”

Team Rules (In CONTRIBUTING.md)

  1. Author runs AI filter BEFORE requesting review — “Don’t waste my time on semicolons”
  2. Human reviewer ONLY comments on Pillars 1-3 — If you see a style comment, it’s a process failure
  3. Small PRs (<300 lines, 1 purpose) — If it doesn’t fit, split it. AI reviews small PRs better.
  4. Review SLA: 4 business hours — If you can’t, reassign. “Approve/Request Changes” buttons in GitHub.
  5. Post-merge: MCP syncs docs — ADRs, OpenAPI, CHANGELOG updated automatically via MCP server.

Example of a “Well-Done” Human Comment

> **Architecture** (Pillar 2)
> 
> In `app/Modules/Billing/Actions/CreateSubscriptionAction.php:45`
> 
> `SyncToERPJob` is dispatched synchronously inside the DB transaction.
> If ERP fails → subscription rollback → user has no subscription but Stripe charged.
> 
> **Proposal**: Move `SyncToERPJob` outside transaction (after_commit) + compensation:
> ```php
> DB::transaction(function () {
>     $subscription = $this->createSubscription($data);
>     // Only local DB here
> });
> 
> // After commit - if fails, compensate via Stripe webhook + retry
> SyncToERPJob::dispatch($subscription)->afterCommit();
> ```
> 
> See `docs/adr/012-erp-sync-reliability.md` for full pattern.
> 
> Thoughts? If agreed, implement in this PR or follow-up ticket.

What I NEVER Delegate to AI (Red List)

AreaWhy
Architectural decisionsAI optimizes locally, misses system-wide + business trade-offs
Critical security (auth, payments, crypto)I review line-by-line; AI assists, doesn’t decide
Business intentionalityOnly human validates against ticket/PO/client
Final mergeI approve full diff in GitHub before merge
Domain namingUbiquitous language = team agreement, not AI suggestion

Tool Stack (2026)

ToolRole
Claude CodeAuto pre-review filter, refactoring, test scaffolding
GitHub ActionsCI gate: tests, PHPStan, Pint, Lighthouse CI, Mutation testing
Sentry + PulsePost-merge observability (error rate, perf, cron)
MCP ServersPost-merge doc sync (ADR, OpenAPI, CHANGELOG)
Linear/GitHub ProjectsTicket ↔ PR ↔ Deploy tracking

Want to Implement This in Your Team?

Available for technical consulting (2-4 weeks) or hiring as Senior who brings this workflow ready.

Modalities:

  • EOR (Deel, Remote, Oyster) — core indefinite roles
  • Freelance B2B (Autónomo, intra-community invoice 0% VAT) — 3-12 month projects
  • Direct Permanent — if you have Spanish entity

Stack: Laravel 11, PHP 8.3+, Vue 3 + TS, Inertia.js, Livewire 3, Astro, Docker, GitHub Actions, Laravel Pulse, Sentry, Claude Code / Cursor / MCP servers.

View My Profile, Full Stack & Terms →


Related articles

Review my developer profile

If this article matches the kind of product work your team is facing, review my stack or professional availability.

Tell me what you need

You can reach out about a role, contract, technical collaboration, question, or general inquiry. A few lines of context are usually enough for me to reply within 24 business hours.

0/500
Availability