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.