Review the actual change, not the file list CodeRabbit, an AI code review tool, was tested on a real 218-file pull request that removed 74,096 lines from the HiddenWars game codebase. The tool reorganized the PR into a structured walkthrough with cohorts and layers, catching issues in a complex Go interface that mixed two systems. The review demonstrates the tool's utility for large-scale code changes despite some limitations. Review the actual change, not the file list A hands-on look at CodeRabbit on a real, absurdly large feature-removal PR. Sponsored by CodeRabbit — but this is a real PR, on a real codebase, with real findings. I’m going to show you exactly what the tool caught, what it got wrong, and whether the “AI reviews your PR” pitch holds up when the PR is 218 files and minus‑74,096 lines. AI writes more code than ever. Reviewing it shouldn’t mean scrolling forty files in alphabetical order. CodeRabbit Review reorganizes any pull request from a flat file list into a structured, layer-by-layer walkthrough - the logical reading order of the change, not the order your platform happens to sort it. Every range gets its own plain-language summary, with sequence diagrams, state machines, and ERDs generated inline wherever a visual earns its place. Cohorts group related files and chunks so you review one idea at a time. Layers order them so foundational changes - data shapes, contracts - come before the code that depends on them. Code Peek lets you click any variable, function, class or type to see its definition and usages without leaving the tab, while Semantic Diff view cuts past formatting noise to show what actually changed. Open it straight from the Review Change Stack button in the PR Walkthrough. Navigate cohorts and layers from the keyboard, comment against exact line ranges and submit native reviews, comments and approvals post back to GitHub or GitLab right where your team expects them. In the early access, available for free to everyone. From the team that pioneered AI code reviews. 2M reviews every week. 6M repos. 15K customers.The review interface built for the way modern PRs are actually written. Review your next PR with CodeRabbit Review Today The setup: killing a feature that outgrew the game HiddenWars is a browser-based hacking strategy game — Go + Echo on the backend, Svelte 5 on the frontend, PostgreSQL, Redis. The Corruption War was a cooperative map/season meta-game that had accumulated ~74k lines of code and too many design docs. It was time for it to go. 218 files changed · +1,453 / −74,096 · 5 phases · one SQL migration This is the worst-case input for an automated reviewer. Most of the diff is deletions, the codebase has a genuinely confusing naming collision more on that in a minute , and the “interesting” changes are surgical edits buried inside shared files. If a tool can produce a useful review here, it can produce one anywhere. I installed CodeRabbit on the repo, pointed it at the PR, set the profile to Chill the balanced middle option — more on the three profiles later , and let it run. What follows is a look at what it caught on the Go specifically, what it got wrong, and whether the “AI reviews your PR” pitch holds up when the PR is 218 files. The hard part: a Go interface that mixed two systems The single gnarliest file was backend/internal/db/botnets.go . It defines a repository interface that he botnet subsystem hangs off: type BotnetRepository interface { ListPlayerNodes ctx context.Context, playerID string, limit, offset int, filter NodeListFilter models.PlayerNode, int, error // ... healthy, degraded, corrupted node management ... // Siphon-scoped locking: pins draining nodes to a specific session so // concurrent siphons keep separate draining pools. LockNodesForSiphon ctx context.Context, playerID, slug string, count int, sessionID string int, error UnlockNodesBySiphonSession ctx context.Context, playerID, sessionID string error // War-op-scoped locking: pins healthy/degraded nodes of slug to a hack op as // 'war committed' ownership via war op id . Returns the locked node IDs. LockNodesForWarOp ctx context.Context, playerID, slug string, qty int, opID string string, error // UnlockWarOpNodes reverts the player's surviving war committed nodes for opID ... UnlockWarOpNodes ctx context.Context, playerID, opID string error // LiveOpComposition returns the current surviving committed composition per // player for an op: counts only nodes still state='war committed'. LiveOpComposition ctx context.Context, opID string map string models.NodesComposition, error LiveOpDegraded ctx context.Context, opID string map string map string int, error LiveOpAbsorbers ctx context.Context, opID string map string map string models.PlayerNode, error // DegradeWarOpNodesTracked damages up to maxCount of the player's war committed // nodes of slug pinned to opID, using the hack damage model ... DegradeWarOpNodesTracked ctx context.Context, playerID, opID, slug string, maxCount int, damage, corruptThreshold float64 int, models.NodeDamageEntry, error // ... raid, bounty, siphon, mining, signature counts ... } The trap is the word corruption . This interface, and this whole file, contains two unrelated mechanics : - The Corruption War — the WarOp / LiveOp methods above, the war committed node state. This is what I’m deleting. - Node corruption — corrupted nodes, corruption spread , the corruption immunity buff. A separate botnet maintenance mechanic that stays. A reviewer — human or AI — that confuses those two will either delete the wrong code or drown you in false “you’re removing the wrong thing ” comments. I was specifically watching for how CodeRabbit handled it. The Go that needed surgery, not deletion The war methods came out cleanly — eight method signatures deleted from the interface, eight implementations deleted below. The subtle work was the other Go in the file: methods that stay, but whose raw SQL references war states inline. Take Se verNodes : func b Botnets SeverNodes ctx context.Context, playerID string, nodeIDs string int, error { if len nodeIDs == 0 { return 0, nil } conn := b.Acquire defer conn.Release rows, err := conn.Query ctx, DELETE FROM player nodes WHERE id = ANY $1 AND botnet id = SELECT id FROM botnets WHERE player id = $2 AND state NOT IN 'locked', 'committed', 'raid committed', 'war committed', 'region recon', 'boss committed', 'hashing', 'draining', 'recompiling' RETURNING id , nodeIDs, playerID // ... } war committed and region recon in that NOT IN list are war coupling — they have to come out. But ’locked’ , ’committed’, ’raid committed’ , ’corrupted’ further up all stay, because those are raid/bounty/node-corruption mechanics. This is raw SQL as a Go string literal : your compiler will not help you. There is no type checker that catches a stray ’war committed’ left in a query. This is exactly where an AI reviewer earns its keep — if it can read the SQL. CodeRabbit did. It never flagged the legitimate states, and it correctly identified every war-coupled string across the file. The naming collision it had to reason through: this corrupted is the keep-mechanic, that war committed is the delete-mechanic. It inferred the distinction from context. That’s the moment I stopped thinking of it as a linter. The same pattern, in a 60-line reaper query Where it got genuinely impressive was ReleaseOrphanedLockedNodes — a background reaper that’s one big CTE with a UNION per node state. Each arm reaps nodes stuck in a transient state locked, committed, hashing, draining when their owning op/session has vanished. The war removal meant: delete the war committed arm entirely, but leave the raid/bounty/siphon/decryption arms alone. The surviving query looks like this trimmed for length : func b Botnets ReleaseOrphanedLockedNodes ctx context.Context int64, error { conn := b.Acquire defer conn.Release tag, err := conn.Exec ctx, WITH orphaned AS -- committed nodes whose raid is completed SELECT pn.id FROM player nodes pn JOIN botnets bot ON bot.id = pn.botnet id WHERE pn.state IN 'committed', 'raid committed' AND pn.locked at IS NOT NULL AND NOT EXISTS SELECT 1 FROM raid registrations rr JOIN raids r ON r.id = rr.raid id WHERE rr.player id = bot.player id AND r.completed at IS NULL UNION -- locked nodes with no live bounty hunt ... UNION -- hashing nodes with no active decryption event ... UNION -- draining nodes whose owning session ended SELECT pn.id FROM player nodes pn JOIN botnets bot ON bot.id = pn.botnet id WHERE pn.state = 'draining' AND pn.locked at IS NOT NULL AND NOT EXISTS SELECT 1 FROM siphon sessions ss WHERE ss.id = pn.siphon session id AND ss.status = 'active' UPDATE player nodes SET state = CASE WHEN health < 70 THEN 'degraded' ELSE 'healthy' END, locked at = NULL WHERE id IN SELECT id FROM orphaned // ... } There used to be a fifth arm here — a war committed reaper that found nodes whose war op had resolved. Deleting it is trivial. The hard part is being confident the remaining four arms don’t have a hidden war dependency. CodeRabbit read the query and was satisfied that they didn’t. A human reviewer would have to do the same read; CodeRabbit did it in the time it took me to refill my coffee. The SQL-in-Go the AI actually flagged CodeRabbit did have an opinion about this file — a Major on the draining arm of that reaper. The bot noticed that when the reaper resets an orphaned draining node, it clears state and locked at but leaves siphon session id dangling: UPDATE player nodes SET state = CASE WHEN health < 70 THEN 'degraded' ELSE 'healthy' END, locked at = NULL WHERE id IN SELECT id FROM orphaned Its suggested fix: UPDATE player nodes SET state = CASE WHEN health < 70 THEN 'degraded' ELSE 'healthy' END, locked at = NULL, siphon session id = NULL WHERE id IN SELECT id FROM orphaned This is a real observation about state hygiene in a reaper query. I ultimately didn’t take it — and here’s the honest part of an honest review. The feature that sold me: “Prompt for AI Agents” Every CodeRabbit comment ships with a collapsible 🤖 Prompt for AI Agents — a machine-readable instruction formatted for a coding agent. The real one for the reaper finding: Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @backend/internal/db/botnets.go around lines 2783 - 2796, Update the reaper’s player nodes UPDATE for orphaned draining nodes to clear siphon session id along with resetting state and locked at. Ensure reused nodes no longer retain stale siphon ownership that could be acted on by delayed UnlockNodesBySiphonSession cleanup. This is the workflow shift. You don’t read the comment and paste it into your agent — you hand it the prompt and it knows the file, the line range, the intent, and the validation contract. I ran my whole review-fix loop this way: collect the prompts → feed them to my coding agent → have it assess each against the current Go and fix only the still-valid ones. The review stops being a thing you read and becomes a thing you execute . The three review profiles CodeRabbit offers three, set in .coderabbit.yaml : Quiet: Only the most important findings, inline | Mature codebases, noisy PRs, low reviewer bandwidth | Chill: what I used | Balanced — Majors and Minors, nitpicks separated | General purpose; the sane default | Assertive: More feedback, may feel like nitpicking | Greenfield, junior-heavy teams, strict bars | I ran Chill and it felt right for a removal PR. Assertive on 218 files would have fatigued me; Quiet would be my pick for a hotfix where I only want “will this break prod?” So, is it worth it? Here’s the honest accounting for Go shops specifically. Go gives you a strong type system, go vet , staticcheck , and good test tooling — so a lot of what AI review catches in dynamically-typed languages, Go already catches at compile time. What’s left for an AI reviewer in Go is exactly what bit m here: raw SQL embedded as string literals, interface/method consistency across a big refactor, and cross-file invariants the compiler can’ see. botnets.go is 3,500 lines of pgx queries against a state machine living in a Postgres CHECK constraint — no Go tool in my toolchain reasons about the SQL inside those backticks. CodeRabbit does. The mental model that worked for me: CodeRabbit is a very fast, very thorough junior reviewer who never gets tired but never says “I don’t know, what do you think?” It always has an opinion, and some will be wrong in the particular way a competent-but-context-light reviewer’s are. Your job shifts from finding issues to adjudicating them — strictly faster work. On a 74,000-line removal PR, it caught two real things I’d missed and cost me maybe ten minutes adjudicating the few misses. For a Go team too small for thorough human review — it’s worth a serious look. Review your next PR with CodeRabbit Review Today