DEV Community

Cover image for CodeRabbit autoFix + Biome pre-commit: The 2-Layer Split That Stops Nitpick Round-Trips
Ken Imoto
Ken Imoto

Posted on Originally published at kenimoto.dev

CodeRabbit autoFix + Biome pre-commit: The 2-Layer Split That Stops Nitpick Round-Trips

Telling someone their shoelaces are untied is slower than tying them yourself.

Two-layer auto-fix: Biome at pre-commit, CodeRabbit autoFix at PR open, 90%+ of PRs reach human review with zero style comments

That is roughly the whole thesis of this post. Every code review comment that says "unused import," "wrong quote style," "missing semicolon," or "add trailing comma" is a nitpick round-trip. Someone writes it, someone reads it, someone edits the file, someone re-runs CI, someone re-reads the diff. On a normal PR, that entire loop is 4 to 12 hours of wall-clock elapsed, spread across timezones. And every single one of those comments describes a fix that a machine could have applied deterministically.

The two-layer setup below deletes that entire category from human review queues. Layer 1 is Biome on a pre-commit hook. Layer 2 is CodeRabbit autoFix on the PR. The split matters, and putting them in the wrong order is worse than not having either.

The clean split: what belongs on which layer

Machine-fixable falls into two buckets. Getting them into the right bucket is the whole point.

Layer Runs at Fixes Time-to-fix
Biome (pre-commit) Before the commit hash exists Formatting, import order, quote style, semi rules <1 s local
CodeRabbit autoFix Once PR opens, review-time Unused imports, obvious null-checks, small refactors ~10 s in PR

Biome is the ESLint + Prettier merge that ships as one Rust binary and runs the whole check-and-write cycle in under a second on a typical file. The pre-commit hook catches formatting drift before the commit hash exists, which means the reviewer literally never sees a diff where the only change is " vs '.

biome.json is the whole configuration:

{
  "$schema": "https://biomejs.dev/schemas/1.9.0/schema.json",
  "formatter": {
    "enabled": true,
    "indentStyle": "space",
    "indentWidth": 2
  },
  "linter": {
    "enabled": true,
    "rules": {
      "recommended": true,
      "suspicious": {
        "noExplicitAny": "error"
      }
    }
  },
  "organizeImports": {
    "enabled": true
  }
}
Enter fullscreen mode Exit fullscreen mode

Wired into .husky/pre-commit or lefthook.yml as biome check --apply ., this is the layer that never lets a formatting-only diff into a PR. Not "shouldn't." Cannot.

What's left after Biome is where autoFix earns its keep

Biome handles deterministic transforms. It does not, and should not, handle changes that require reading semantic context, like "this import is unused because the only usage got refactored out three commits ago." That is a whole-file analysis, not a line-level transform.

CodeRabbit's autoFix picks up exactly this class of change on the PR. When it comments a nitpick: (their prefix for low-severity findings), the comment ships with a committable suggestion block:

nitpick: Unused import.

--- a/src/app/page.tsx
+++ b/src/app/page.tsx
@@ -1,5 +1,4 @@
 import React from 'react'
-import { useState } from 'react'   // unused
 import { UserList } from '@/components/UserList'

[Apply suggestion]   <- one-click commit into the branch
Enter fullscreen mode Exit fullscreen mode

The reviewee sees a fix, not a task. Click. Commit. Move on.

If you want to skip the click entirely, CodeRabbit's autoFix runs as a batch job that opens either a commit-to-branch or a stacked PR containing every accepted suggestion at once. Trigger it from a PR comment. The docs cover both flows: CodeRabbit Autofix documentation.

The autoFixable vs non-autoFixable line

The most useful mental model I have found is a rule-of-thumb table. If your team is arguing about whether something should be a lint rule or a human review point, this is the question to ask.

autoFixable non-autoFixable
Prettier / Biome formatting Design (multiple correct answers)
ESLint --fix / Ruff --fix Fix for N+1 query (business logic)
eslint-plugin-import order Naming (needs context)
TypeScript organizeImports (unused) Bug in logic (needs spec knowledge)
noExplicitAny where obvious API shape decisions

The split is: if there is exactly one correct answer that does not depend on business context, it belongs on a fix layer. Everything else is a real review conversation, and a human should have it.

The value of enforcing this line is not that machines are cheap. It is that human reviewer attention is expensive and finite. Every nitpick you route to a machine is a bug or a design flaw a reviewer noticed instead.

GitHub Actions, if you want the belt-and-suspenders version

Some teams do not want pre-commit hooks because contributors forget or bypass them. If you want the same guarantees enforced at PR time, you can run the fix pass in CI and commit the result back into the branch:

name: Auto Fix
on:
  pull_request:
    types: [opened, synchronize]

jobs:
  autofix:
    runs-on: ubuntu-latest
    permissions:
      contents: write   # required to push
    steps:
      - uses: actions/checkout@v4
        with:
          ref: ${{ github.head_ref }}
          token: ${{ secrets.GITHUB_TOKEN }}

      - uses: actions/setup-node@v4
        with:
          node-version: '22'
          cache: 'npm'

      - run: npm ci

      - name: Format + lint --fix
        run: npx biome check --apply .

      - name: Commit if changed
        run: |
          git config user.name  "github-actions[bot]"
          git config user.email "github-actions[bot]@users.noreply.github.com"
          git diff --quiet || (
            git add -A &&
            git commit -m "chore: auto-fix format and lint"
          )
Enter fullscreen mode Exit fullscreen mode

Two gotchas.

The contents: write permission is not the default for GITHUB_TOKEN. If you skip that line, the workflow will run cleanly and silently fail to push. Look for "unable to push" in the logs on the first run. I know because I spent a solid afternoon assuming Biome was just skipping files.

The ref: ${{ github.head_ref }} matters. Without it, pull_request checks out the merge commit, not the branch head, and your push goes nowhere useful.

The measurement I would actually track

If you install this two-layer setup and want to prove it worked, one metric matters:

Ratio of PRs that hit human review with zero formatting or style comments.

Before, in my experience this ratio hovers around 30-50%. Every other PR ships with at least one comment about a linting fix. After Biome pre-commit plus CodeRabbit autoFix, the ratio moves to 90%+. The remaining 10% is contributors who bypassed the hook or files the linter did not cover.

The number does not need to be perfect. It needs to move enough that reviewers stop reading nitpicks reflexively. Once the signal-to-noise ratio flips, reviewers start noticing actual bugs faster because they are no longer filtering through style comments to find them.

Why the order matters

If you had to pick only one, pick Biome pre-commit. It runs before the commit hash exists, so it cannot fail asynchronously and the fix is deterministic. CodeRabbit autoFix can only fix what is already committed, and it costs a review-cycle even when applied by a click. I learned this the expensive way — shipping autoFix alone and watching the PR queue still fill up with format-only diffs because the hook wasn't there to catch them upstream.

But the real win is stacking them. Biome catches 80% of the noise deterministically. CodeRabbit picks up the semantic long tail that lint rules cannot see. Together, the reviewer sees exactly one class of comment: the ones that actually require their judgment.

The most under-priced skill in code review is knowing which comments should have been fixed automatically. Once you build the machinery, the reviewers who used to burn cycles on nitpicks are the same ones who now catch design flaws faster. The floor moved. The ceiling did too.

If you want the full walkthrough (the chapter also covers the non-autoFixable classification, a GitHub Actions auto-fix workflow, and where Biome fits inside the broader review pipeline), it is in the Zenn book below.

Book

If you are building your own AI-plus-human review pipeline and want the full framework, the design decisions in this post come from the harness-code-review book:

Harness Code Review: a two-layer machine + human review pipeline

Notes

Top comments (0)