Substituí a Revisão de Código Baseada em Grep por um Grafo de Conhecimento + MCP. Aqui Estão 3 Bugs que a Busca Vetorial Perdeu.
Por cerca de um ano, minha configuração de revisão de código com IA parecia assim: a IA recebe um PR, a IA procura código relacionado, a IA lê muitos arquivos, a IA diz "parece bom".
Funcionou na maior parte. Até que os bugs que não apareceram no grep começaram a ser enviados.
O problema não era o modelo. Era a recuperação. A busca vetorial e o grep por palavras-chave são ótimos para encontrar arquivos que mencionam auth.py. Eles são terríveis para encontrar arquivos que dependem de auth.py através de três saltos de importação, um barramento de eventos e um decorador. É aí que os bugs vivem.
Eu reestruturei a camada de recuperação com um gráfico de conhecimento de código conectado através do MCP. Três bugs surgiram na primeira semana que a busca vetorial havia perdido silenciosamente. Aqui está o que mudou e os próprios bugs.
Por que grep + busca vetorial perderam esses
A busca vetorial recupera por similaridade semântica. "Encontrar código sobre autenticação" encontra auth.py, login.py, password_validator.py. Útil.
Gráficos de conhecimento recuperam por relação estrutural. "O que depende de auth.py?" retorna o gráfico de chamadas -- incluindo event_handlers/login_event.py, que nunca menciona auth em seus nomes de variáveis, mas escuta um evento de login cujo payload muda quando auth.py muda.
Ambos são válidos. Eles respondem a perguntas diferentes. Os bugs que vão para produção tendem a viver na segunda pergunta.
A configuração: KG de código como um servidor MCP
O Protocolo de Contexto do Modelo (MCP), lançado pela Anthropic no final de 2024, permite que você exponha ferramentas a um modelo de uma maneira padrão. Em 2026, é suportado pelo Claude Code, Cursor, Windsurf, Zed, VS Code, e (a partir do GA em maio de 2025) o Registro MCP oficial hospeda centenas de servidores.
Eu usei code-review-graph, uma ferramenta de código aberto que constrói um gráfico de propriedades da sua base de código e o expõe como um servidor MCP. A configuração é um ritual de três linhas:
pip install code-review-graph
code-review-graph build ./my-project
code-review-graph install # auto-detecta Claude Code / Cursor / Windsurf
O gráfico contém nós para arquivos, classes, funções e testes, com arestas para importações, chamadas, herança, decorações, escuta e testado por. Uma vez conectado, a IA pode chamar ferramentas MCP como:
| Ferramenta | O que responde |
|---|---|
blast_radius(file) |
Todo arquivo que depende deste (N saltos) |
flow_trace(func) |
Onde o fluxo de saída de uma função vai |
semantic_search(query) |
Híbrido: proximidade vetorial + gráfica |
community_detect() |
Módulos fortemente acoplados |
risk_score(diff) |
Risco numérico de uma mudança |
dead_code() |
Inacessível a partir de qualquer ponto de entrada |
A implementação do MCP pela Anthropic em 2025 também trouxe OAuth, prompts e assinaturas de recursos, para que o gráfico possa enviar atualizações quando os arquivos mudam em vez de serem reconsultados a cada turno. Esse detalhe importa em escala -- KGs de código não são baratos de percorrer, e instantâneas desatualizadas são como as equipes enviam bugs.
A matemática dos tokens (a razão barata para se preocupar)
Antes do gráfico, meu revisor de IA estava recebendo contexto assim:
PR diff: auth.py + 1 arquivo
Contexto do revisor: grep por "auth" -> 50 arquivos relacionados
Tokens: ~150.000
Depois do gráfico, ele recebe isso:
PR diff: auth.py + 1 arquivo
Contexto do revisor: blast_radius("auth.py", hops=2) -> 7 arquivos
Tokens: ~18.000
Na primeira vez que executei, a IA respondeu à pergunta de revisão em dois segundos com um contexto de 7 arquivos. Eu havia passado 30 minutos no dia anterior greppando a mesma resposta manualmente. Esse momento de "o que eu estava fazendo com minha carreira" é, eu acho, o verdadeiro produto da engenharia de harness.
Mas um contexto mais barato não é a parte interessante. A parte interessante é quais bugs o gráfico revelou que grep + vetor haviam perdido.
Os 3 bugs
Bug 1: a mudança silenciosa de contrato (manipulador de eventos)
O diff era pequeno. auth.py adicionou um campo device_id ao seu payload de evento de login.
A busca vetorial recuperou login.py, auth_test.py, password_validator.py -- os vizinhos óbvios. O revisor aprovou.
O gráfico recuperou um arquivo extra: event_handlers/audit_log.py. Ele escuta login_event e serializa o payload para um esquema fixo no S3. Adicionar um novo campo quebrou o validador de esquema em cada login. A produção pegou fogo 90 minutos após a mesclagem.
Por que o grep perdeu isso: audit_log.py não importa auth.py. Ele escuta um barramento de eventos. Não há correspondência de string em "auth" no arquivo.
O que o gráfico viu: auth.py --emite--> login_event --consumido-por--> audit_log.py. Três saltos, zero correspondências de string, mas um caminho estrutural limpo.
Uma passagem de revisão de código que não segue assinaturas de eventos é uma passagem de revisão de código que não revisa sistemas orientados a eventos.
Bug 2: a surpresa do decorador (mudança transitiva)
Um colega refatorou @with_retry para adicionar um parâmetro de backoff. O valor padrão era o mesmo, então os chamadores existentes estavam "não afetados". O revisor aprovou com base na força dos testes unitários.
A busca vetorial recuperou arquivos que importavam explicitamente o decorador. Cerca de uma dúzia.
O gráfico recuperou 31 arquivos. Os 19 que o gráfico adicionou foram arquivos que aplicaram @with_retry a funções que, três chamadas profundas, acabaram chamando uma função cujo comportamento de retry havia mudado sutilmente sob carga.
Um desses chamadores era um manipulador de webhook de pagamentos. Sob retry, agora esperava 800ms a mais antes de levantar. Esses 800ms o colocaram além do tempo limite do webhook. Começamos a perder cerca de 0,4% das entregas de webhook silenciosamente.
Por que o grep perdeu isso: o efeito de um decorador se propaga para cada local de chamada de cada função decorada. Isso é estrutural, não lexical.
O que o gráfico viu: with_retry --decora--> {19 funções} --chamadas-por--> {31 arquivos}. O chamador de uma função decorada herda o comportamento do decorador, mesmo que nunca mencione o nome do decorador.
Bug 3: o teste órfão (falsa confiança)
Uma migração mudou a forma como um ID era hashado. O PR incluía um teste que afirmava o novo hash. O CI estava verde.
O gráfico mostrou algo que o executor de testes não viu: aquele teste estava em um arquivo que não era mais executado no CI porque havia sido movido para fora do diretório tests/ três semanas antes e ninguém havia atualizado o glob de caminho na configuração do CI. O teste passou porque o arquivo de teste nunca foi executado. O PR foi aceito com um hash quebrado que corrompeu as primeiras 8.000 linhas da migração.
O gráfico tinha dead_code() para funções inacessíveis e uma consulta inversa para arquivos de teste inacessíveis. Eu nunca havia perguntado isso. Após este PR, adicionei "executar dead_code() em arquivos de teste" à verificação pós-voo.
Por que o grep perdeu isso: o grep não sabe o que o CI executa. A existência de um arquivo de teste e a execução de um arquivo de teste são diferentes fac
A implementação do MCP pode revolucionar a forma como as empresas brasileiras realizam revisões de código, permitindo uma identificação mais precisa de dependências e bugs. Isso pode resultar em menos falhas em produção e maior eficiência no desenvolvimento de software.
