{"slug": "review-the-actual-change-not-the-file-list", "title": "Review the actual change, not the file list", "summary": "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.", "body_md": "# Review the actual change, not the file list\n\n### A hands-on look at CodeRabbit on a real, absurdly large feature-removal PR.\n\nSponsored 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.\n\n**AI writes more code than ever. Reviewing it shouldn’t mean scrolling forty files in alphabetical order.**\n\nCodeRabbit 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.\n\n**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.\n\nOpen 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.\n\n*In the early access, available for free to everyone.*\n\nFrom 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.\n\n**Review your next PR with CodeRabbit Review Today**\n\n## The setup: killing a feature that outgrew the game\n\nHiddenWars is a browser-based hacking strategy game — Go + Echo on the backend, Svelte 5 on the frontend, PostgreSQL, Redis.\n\nThe **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.\n\n218 files changed · +1,453 / −74,096 · 5 phases · one SQL migration\n\nThis 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.\n\nI 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.\n\nWhat 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.\n\n## The hard part: a Go interface that mixed two systems\n\nThe single gnarliest file was `backend/internal/db/botnets.go`. It defines a repository interface that `he botnet subsystem hangs off:`\n\n```\ntype BotnetRepository interface {\n\tListPlayerNodes(ctx context.Context, playerID string, limit, offset int, filter NodeListFilter) ([]models.PlayerNode, int, error)\n\t// ... healthy, degraded, corrupted node management ...\n\n\t// Siphon-scoped locking: pins draining nodes to a specific session so\n\t// concurrent siphons keep separate draining pools.\n\tLockNodesForSiphon(ctx context.Context, playerID, slug string, count int, sessionID string) (int, error)\n\tUnlockNodesBySiphonSession(ctx context.Context, playerID, sessionID string) error\n\n\t// War-op-scoped locking: pins healthy/degraded nodes of slug to a hack op as\n\t// 'war_committed' (ownership via war_op_id). Returns the locked node IDs.\n\tLockNodesForWarOp(ctx context.Context, playerID, slug string, qty int, opID string) ([]string, error)\n\n\t// UnlockWarOpNodes reverts the player's surviving war_committed nodes for opID ...\n\tUnlockWarOpNodes(ctx context.Context, playerID, opID string) error\n\n\t// LiveOpComposition returns the current surviving committed composition per\n\t// player for an op: counts only nodes still state='war_committed'.\n\tLiveOpComposition(ctx context.Context, opID string) (map[string]models.NodesComposition, error)\n\tLiveOpDegraded(ctx context.Context, opID string) (map[string]map[string]int, error)\n\tLiveOpAbsorbers(ctx context.Context, opID string) (map[string]map[string][]models.PlayerNode, error)\n\n\t// DegradeWarOpNodesTracked damages up to maxCount of the player's war_committed\n\t// nodes of slug pinned to opID, using the hack damage model ...\n\tDegradeWarOpNodesTracked(ctx context.Context, playerID, opID, slug string, maxCount int, damage, corruptThreshold float64) (int, []models.NodeDamageEntry, error)\n\n\t// ... raid, bounty, siphon, mining, signature counts ...\n}\n```\n\nThe trap is the word **corruption**. This interface, and this whole file, contains *two unrelated mechanics*:\n\n- **The Corruption War** — the `WarOp` / `LiveOp` methods above, the `war_committed` node state. This is what I’m deleting.\n\n- **Node corruption** — `corrupted` nodes, corruption *spread*, the `corruption_immunity` buff. A separate botnet maintenance mechanic that stays.\n\nA 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.\n\n## The Go that needed surgery, not deletion\n\nThe 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`:`\n\n```\nfunc (b *Botnets) SeverNodes(ctx context.Context, playerID string, nodeIDs []string) (int, error) {\n\tif len(nodeIDs) == 0 {\n\t\treturn 0, nil\n\t}\n\tconn := b.Acquire()\n\tdefer conn.Release()\n\n\trows, err := conn.Query(ctx, `\n\t\tDELETE FROM player_nodes\n\t\tWHERE id = ANY($1)\n\t\t  AND botnet_id = (SELECT id FROM botnets WHERE player_id = $2)\n\t\t  AND state NOT IN ('locked', 'committed', 'raid_committed', 'war_committed', 'region_recon',\n\t\t                    'boss_committed', 'hashing', 'draining', 'recompiling')\n\t\tRETURNING id\n\t`, nodeIDs, playerID)\n\t// ...\n}\n```\n\n`war_committed`\n\nand `region_recon`\n\nin that `NOT IN`\n\nlist are war coupling — they have to come out. But `’locked’`\n\n, `’committed’, ’raid_committed’`\n\n, `’corrupted’`\n\n(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’`\n\nleft in a query. This is exactly where an AI reviewer earns its keep — if it can read the SQL.\n\nCodeRabbit 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`\n\nis the keep-mechanic, that `war_committed`\n\nis the delete-mechanic.* It inferred the distinction from context. That’s the moment I stopped thinking of it as a linter.\n\n## The same pattern, in a 60-line reaper query\n\nWhere it got genuinely impressive was `ReleaseOrphanedLockedNodes`\n\n— a background reaper that’s one big CTE with a `UNION`\n\nper 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`\n\narm entirely, but leave the raid/bounty/siphon/decryption arms alone. The surviving query looks like this (trimmed for length):\n\n```\nfunc (b *Botnets) ReleaseOrphanedLockedNodes(ctx context.Context) (int64, error) {\n\tconn := b.Acquire()\n\tdefer conn.Release()\n\ttag, err := conn.Exec(ctx, `\n\t\tWITH orphaned AS (\n\t\t\t-- committed nodes whose raid is completed\n\t\t\tSELECT pn.id FROM player_nodes pn\n\t\t\tJOIN botnets bot ON bot.id = pn.botnet_id\n\t\t\tWHERE pn.state IN ('committed', 'raid_committed')\n\t\t\t  AND pn.locked_at IS NOT NULL\n\t\t\t  AND NOT EXISTS (\n\t\t\t    SELECT 1 FROM raid_registrations rr\n\t\t\t    JOIN raids r ON r.id = rr.raid_id\n\t\t\t    WHERE rr.player_id = bot.player_id AND r.completed_at IS NULL\n\t\t\t  )\n\t\t\tUNION\n\t\t\t-- locked nodes with no live bounty hunt ...\n\t\t\tUNION\n\t\t\t-- hashing nodes with no active decryption event ...\n\t\t\tUNION\n\t\t\t-- draining nodes whose owning session ended\n\t\t\tSELECT pn.id FROM player_nodes pn\n\t\t\tJOIN botnets bot ON bot.id = pn.botnet_id\n\t\t\tWHERE pn.state = 'draining'\n\t\t\t  AND pn.locked_at IS NOT NULL\n\t\t\t  AND NOT EXISTS (\n\t\t\t    SELECT 1 FROM siphon_sessions ss\n\t\t\t    WHERE ss.id = pn.siphon_session_id AND ss.status = 'active'\n\t\t\t  )\n\t\t)\n\t\tUPDATE player_nodes\n\t\tSET state = CASE WHEN health < 70 THEN 'degraded' ELSE 'healthy' END,\n\t\t    locked_at = NULL\n\t\tWHERE id IN (SELECT id FROM orphaned)\n\t`)\n\t// ...\n}\n```\n\nThere *used to be* a fifth arm here — a `war_committed`\n\nreaper 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.\n\n## The SQL-in-Go the AI actually flagged\n\nCodeRabbit did have an opinion about this file — a `Major`\n\non the draining arm of that reaper. The bot noticed that when the reaper resets an orphaned draining node, it clears `state`\n\nand `locked_at`\n\nbut leaves `siphon_session_id`\n\ndangling:\n\n```\nUPDATE player_nodes\nSET state = CASE WHEN health < 70 THEN 'degraded' ELSE 'healthy' END,\n    locked_at = NULL\nWHERE id IN (SELECT id FROM orphaned)\n```\n\nIts suggested fix:\n\n```\n UPDATE player_nodes\n SET state = CASE WHEN health < 70 THEN 'degraded' ELSE 'healthy' END,\n    locked_at = NULL,\n    siphon_session_id = NULL\nWHERE id IN (SELECT id FROM orphaned)\n```\n\nThis 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.\n\n## The feature that sold me: “Prompt for AI Agents”\n\nEvery 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:\n\n```\nVerify each finding against current code. Fix only still-valid issues, skip the\n\nrest with a brief reason, keep changes minimal, and validate.\n\nIn `@backend/internal/db/botnets.go` around lines 2783 - 2796, Update the reaper’s\n\nplayer_nodes UPDATE for orphaned draining nodes to clear siphon_session_id along\n\nwith resetting state and locked_at. Ensure reused nodes no longer retain stale\n\nsiphon ownership that could be acted on by delayed UnlockNodesBySiphonSession\n\ncleanup.\n```\n\nThis 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*.\n\n## The three review profiles\n\nCodeRabbit offers three, set in `.coderabbit.yaml`\n\n:\n\n**Quiet:** Only the most important findings, inline | Mature codebases, noisy PRs, low reviewer bandwidth |\n\n**Chill:** *(what I used)* | Balanced — Majors and Minors, nitpicks separated | General purpose; the sane default |\n\n**Assertive:** More feedback, may feel like nitpicking | Greenfield, junior-heavy teams, strict bars |\n\nI 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?”\n\n## So, is it worth it?\n\nHere’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: `\n\n**raw SQL embedded as string literals, interface/method consistency across a big refactor, and cross-file invariants the compiler can’**` see. `\n\n`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.\n\nThe 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.\n\nOn 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.\n\n**Review your next PR with CodeRabbit Review Today**", "url": "https://wpnews.pro/news/review-the-actual-change-not-the-file-list", "canonical_source": "https://packagemain.tech/p/review-the-actual-change-not-the", "published_at": "2026-07-17 12:23:59+00:00", "updated_at": "2026-07-17 12:36:39.846904+00:00", "lang": "en", "topics": ["ai-tools", "developer-tools", "artificial-intelligence"], "entities": ["CodeRabbit", "HiddenWars", "GitHub", "GitLab", "Go", "Echo", "Svelte", "PostgreSQL"], "alternates": {"html": "https://wpnews.pro/news/review-the-actual-change-not-the-file-list", "markdown": "https://wpnews.pro/news/review-the-actual-change-not-the-file-list.md", "text": "https://wpnews.pro/news/review-the-actual-change-not-the-file-list.txt", "jsonld": "https://wpnews.pro/news/review-the-actual-change-not-the-file-list.jsonld"}}