Padrão autoFixable: 0 revisões humanas de lint em 3 meses
Vou começar pela contagem: nos últimos 3 meses, no meu time de 4 devs, o número de comentários de revisão humana relacionados a lint, formatação, import não usado, ordem de imports, semicolon esquecido foi exatamente zero.
Não zero na média. Zero na contagem absoluta. Puxei o export de comentários do GitHub via API filtrado por regex (no-unused-vars|prefer-const|@typescript-eslint/|prettier/|semi|import/order) e passei 4 vezes achando que a query estava errada. Não estava.
Antes disso, esse tipo de comentário era 34% do total de revisões humanas em PRs. Sumiu. Não porque a gente parou de revisar — a gente revisa mais, e melhor. Sumiu porque o harness corrige antes do humano ver.
Este post é sobre como cheguei nesse número, o que quebrei no caminho, e uma opinião que vai contra o discurso da bolha: revisão humana ainda serve. Só serve para uma coisa diferente do que você acha.
O padrão em 3 linhas
O harness roda em três etapas antes de qualquer humano encostar no PR:
- pre-commit local: Biome (ou ESLint —fix + Prettier) aplica todas as correções
safeFixno arquivo antes do commit sair da máquina do dev - GitHub Actions no
pull_request: mesma coisa roda de novo em CI, commita direto no branch do PR se ainda restou algo, e roda testes só depois - CodeRabbit no PR: sugere correções
unsafeFixcomo suggestion blocks que o autor aplica com 1 clique
Se o comentário conseguiu chegar a um humano, é porque a máquina já disse “isso aqui eu não sei arrumar sozinha”. E aí a decisão volta a ter valor.

Por que o Biome mudou a matemática
Quando comecei a testar, em fevereiro, ainda estava rodando ESLint + Prettier separados via Husky. Funcionava. Mas cada commit levava uns 4-6 segundos só para o hook rodar em ~200 arquivos TypeScript, e cerca de 15% dos erros do ESLint eram --fix incompatíveis (regras que dizem “arruma X” mas não sabem como escrever X sem quebrar Y).
Em março troquei para Biome v2. Três coisas importantes mudaram:
Autofix reliability. Biome tem hoje ~500 regras e distingue explicitamente safeFix (mudança semanticamente equivalente) de unsafeFix (pode mudar comportamento). No pre-commit rodo só as safe. As unsafe entram via CodeRabbit suggestion, o autor decide.
Velocidade. O pre-commit hook baixou de 4-6s para uns 300-400ms no mesmo repositório. Isso importa: quando o hook demora mais que 2 segundos, o dev começa a rodar git commit --no-verify “só uma vez, é urgente”. Aí o padrão morre.
Cobertura. Biome cobre ~80% das configs comuns de ESLint, com gaps conhecidos em jsx-a11y, alguns edge cases de import/order e regras que dependem de type information do TypeScript. Para lint puro de estilo e correção mecânica, os 80% que ele cobre são exatamente o que estava gerando comentário humano.
Se você está em Python ou Rust, a mesma lógica vale com Ruff e Clippy. O ponto não é a ferramenta específica. É a garantia de que o autofix não vai produzir código quebrado. Sem essa garantia, o time perde confiança e volta a comentar manualmente por medo.
O biome.json que eu uso hoje
Direto do repositório principal, sem invenção:
{
"$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"
},
"correctness": {
"noUnusedImports": "error",
"noUnusedVariables": "warn"
}
}
},
"organizeImports": { "enabled": true }
}
Roda tudo com um comando:
biome check --write .
--write aplica só safe fixes. Para incluir as unsafe, --write --unsafe, mas isso eu NUNCA rodo em CI. Só localmente quando quero fazer um sweep manual.
O hook pre-commit (lefthook)
Trocamos Husky por lefthook porque paraleliza. Em repositórios com 300+ arquivos alterados num rebase, faz diferença.
# lefthook.yml
pre-commit:
parallel: true
commands:
biome:
glob: "*.{js,ts,jsx,tsx,json}"
run: npx biome check --write --no-errors-on-unmatched {staged_files}
stage_fixed: true
biome-format:
glob: "*.{js,ts,jsx,tsx,json,md}"
run: npx biome format --write {staged_files}
stage_fixed: true
O detalhe importante é stage_fixed: true. Sem isso, o hook corrige o arquivo mas não faz git add do resultado, aí o commit sai com a versão errada e o dev pensa que o hook está bugado. Perdi 2 horas descobrindo isso.
O GitHub Actions que fecha o loop
O hook local não é suficiente. Sempre tem o dev que instala Husky/lefthook mas por algum motivo pulou uma configuração. Ou o hook falhou silenciosamente. Ou o --no-verify aconteceu. O CI é a rede de segurança:
# .github/workflows/autofix.yml
name: autofix
on:
pull_request:
types: [opened, synchronize]
jobs:
autofix:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.head_ref }}
token: ${{ secrets.GITHUB_TOKEN }}
- uses: oven-sh/setup-bun@v2
- run: bun install --frozen-lockfile
- name: Biome check --write
run: bunx biome check --write .
- 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: autofix via biome" &&
git push
)
E o mais importante: os testes rodam num job separado que depende deste. Se autofix commitou algo, os testes rodam contra o código já corrigido. Se não commitou, roda contra o commit original. Nunca acontece de o teste passar num código pré-fix e falhar depois no main.
O 34% que sumiu, distribuído
Antes do padrão entrar, os 34% de comentários humanos de lint se distribuíam mais ou menos assim:
| Categoria | % dos comentários humanos | Onde vai agora |
|---|---|---|
| Formatação (espaço, quebra) | 12% | Biome formatter safeFix |
| Imports não usados | 8% | Biome noUnusedImports safeFix |
| Ordem de imports | 5% | Biome organizeImports |
prefer-const, no-var | 4% | Biome safeFix |
| Semicolon / trailing comma | 3% | Biome formatter |
any explícito | 2% | Biome noExplicitAny (error, quebra CI) |
Nada disso justifica tempo de humano. Nada disso justifica esperar o autor voltar da reunião. Cada um destes casos hoje ou é corrigido antes do commit sair, ou é bloqueado no CI antes do PR ser considerado revisável.
O efeito colateral: revisão humana ficou mais difícil
Aqui é onde o discurso “automatize tudo” quebra na prática. Quando você elimina os 34% de comentários fáceis, os 66% que sobram ficam concentrados em decisões de design, contratos de API, nomes de coisas, tratamento de erro em bordas do sistema.
Todo comentário humano agora é caro. Todo comentário exige que eu leia o código de verdade, não só o diff. Todo comentário custa uma pergunta genuína ao autor, e ele custa uma resposta que não cabe em uma linha.
Meu tempo médio revisando um PR subiu de 8 minutos para 22 minutos. E isso é ótimo. Antes eu estava gastando 8 minutos para deixar 4 comentários de estilo. Agora gasto 22 minutos para deixar 1 comentário de design que muda como duas classes conversam entre si.
A revisão humana não morreu. Ela ficou com o trabalho que sempre foi dela e nunca deveria ter sido misturado com o resto.
Contrarian: pare de dizer “revisão humana é obsoleta”
Vejo isso o tempo todo em thread de X e no TabNews: “IA vai substituir code review”. Não vai. IA (via CodeRabbit ou similar) substitui o subconjunto mecânico que a gente sempre soube que era mecânico. O que sobra é o pedaço que exige entender o problema de negócio, o histórico do sistema, e o que o próximo dev que ler esse arquivo em 6 meses vai pensar.
Nada disso está resolvido nem com Claude Opus 4.7 nem com GPT-5. Testei os dois em PRs reais do meu time durante 2 semanas. Ambos são bons em detectar bugs óbvios e péssimos em explicar por que a escolha de arquitetura X está incoerente com uma decisão que ficou registrada num ADR de 2024 que ninguém indexou. Isso é humano. E é onde meus 22 minutos por PR vão hoje.
Se você quer o pipeline completo (o Capítulo 12 tem toda a matriz autoFixable / non-autoFixable e as regras para timing de merge), escrevi um book sobre isso — está no Kindle BR. Mas o padrão em si você pode aplicar amanhã com um biome.json, um lefthook.yml e o Actions acima. É basicamente o que fiz, e o resto veio pelo hábito.
Se quiser um passo anterior (revisar código com IA sem passar por IA no primeiro filtro), escrevi antes sobre revisão de código com Tree-sitter antes do LLM — o padrão autoFixable encaixa na saída dele. E se você quer o compartimento adjacente (o mesmo padrão, mas medindo tempo em vez de contagem de comentários), já contei essa história em autoFixable: 30 min em 47 segundos.
3 meses, 0 revisões humanas de lint, 22 minutos por PR na parte que importa. Se seu time ainda comenta manualmente remove esse import, você está pagando salário sênior para trabalho de máquina.
ken imoto · WebRTC & Voice AI engineer · kenimoto.dev · TabNews
Este artigo foi útil?