A Mesma PR, Duas Reviews Diferentes

Um desenvolvedor da minha rede configurou o Claude Code como reviewer de CI. “É só deixar o Claude checar a PR”, ele me disse. “Ele pega coisas que eu deixaria passar.” Eu pedi para ele rodar a mesma PR pelo Claude duas vezes.

A primeira review disse que o tratamento de erros parecia abrangente. A segunda review marcou o tratamento como inadequado e sugeriu três mudanças. Ele ficou olhando para a tela por cinco minutos, tentando entender qual Claude estava certo.

Nenhum dos dois estava certo. Nenhum dos dois estava errado. O LLM estava fazendo palpites probabilísticos, não aplicando regras determinísticas. E esse é o problema fundamental de usar IA para verificação: verificação exige duas propriedades que LLMs não têm. Determinismo — a mesma entrada sempre produz a mesma saída. Completude — todos os modos de falha são capturados, sempre.

O Problema do Determinismo

Rode o mesmo código pelo Claude Code duas vezes e você pode receber duas reviews diferentes. Temperatura, janela de contexto, formulação do prompt, tudo introduz variância. Para brainstorming, variância é feature. Para verificação, é bug. Você não consegue construir software confiável em cima de gates de qualidade probabilísticos.

Checagens determinísticas não sofrem desse problema. tsc --noEmit sempre produz os mesmos erros. ESLint com a mesma config sempre sinaliza os mesmos problemas. Essas ferramentas aplicam regras, não probabilidades. Elas são chatas. É por isso que funcionam.

A Stack de Guards O(1)

É assim que a nossa stack de pre-commit se parece. Toda checagem é determinística. Toda checagem roda em milissegundos. Nenhuma liga para saber se foi um humano ou o Claude Code que escreveu o código:

# Pre-commit (< 1 second total)
TypeScript strict mode      # tsc --noEmit
ESLint boundary rules       # module isolation
Import restrictions         # dependency direction
dependency-cruiser          # graph analysis
Unit tests                  # contract verification

# CI (< 30 seconds)
Full test suite
Bundle size checks
Fresh clone launch test

A métrica principal é O(1) em relação ao tamanho da codebase. O type checking é incremental. O lint é por arquivo. O dependency-cruiser analisa o grafo de imports linearmente. Nenhum deles fica mais lento conforme seu app feito com vibe coding cresce. Reviews do Claude Code ficam mais lentas e menos precisas à medida que sua codebase ultrapassa a janela de contexto.

O que Checagens Determinísticas Realmente Pegam

Na semana passada, o dependency-cruiser pegou uma PR em que um componente de tela importava diretamente de uma implementação de service em vez de usar o barrel. A PR compilava. Os testes passavam. O Cursor tinha gerado o import automaticamente. Mas aquilo violava a nossa regra arquitetural: só o composition root importa implementações.

Uma review por LLM talvez pegasse isso. Ou talvez deixasse passar porque a PR era grande e o import estava enterrado no meio do arquivo. O dependency-cruiser nunca deixa passar porque ele não está lendo código, está analisando um grafo:

// .dependency-cruiser.cjs
// 8 lines. 100ms. Catches this every time, forever.
{
  name: 'no-deep-service-imports',
  comment: 'Only service barrels are public.',
  severity: 'error',
  from: { pathNot: '^src/services/' },
  to: {
    path: '^src/services/[^/]+/',
    pathNot: '^src/services/[^/]+/index\.ts$',
  }
}

Oito linhas. Menos de cem milissegundos. Toda PR. Para sempre. Uma review do Claude Code custa tokens, leva segundos e pode deixar passar. A conta não chega nem perto.

Onde a IA Realmente Entra

Eu não sou anti-IA. Uso Cursor e Claude Code todos os dias para rascunhar código, explorar abordagens, escrever testes e depurar. Mas eu não os coloco em funções de verificação. Verificação precisa ser determinística. Precisa ser chata. Precisa ser igual toda vez.

Guardrails deveriam ser invisíveis e inevitáveis. Eles deveriam pegar problemas sem depender de alguém lembrar de rodá-los. É isso que checagens determinísticas entregam: uma rede de segurança que nunca dorme, nunca se cansa e nunca perde contexto conforme sua codebase cresce.

O Autotomy Expo Starter Pack já traz TypeScript strict mode, regras de fronteira no ESLint, configuração de dependency-cruiser e hooks de pre-commit. Você ganha a velocidade do Cursor para geração. Ganha precisão de máquina para verificação. É assim que se faz vibe coding sem quebrar a produção.