A métrica errada

A maioria das conversas sobre a qualidade de código gerado por AI se concentra na correção no momento da geração. A saída compila? Ela passa nos testes? Ela corresponde à spec?

Isso é só o mínimo esperado. Não diz nada sobre o custo real.

A métrica real é a substituibilidade: com que baixo custo você consegue apagar este module e reimplementá-lo atrás do mesmo contract quando os requisitos mudam?

Se a resposta for “trivialmente”, a velocidade da AI se acumula. Se a resposta for “precisamos primeiro rastrear seis dependências implícitas”, você já perdeu a vantagem de velocidade que achou que estava comprando.

Por que o código gerado por AI tende ao acoplamento

LLMs otimizam para o pedido imediato. Eles não otimizam para mudanças futuras.

Quando você pede um fluxo de login, recebe um fluxo de login que funciona. Não recebe um fluxo de login atrás de uma interface de autenticação que possa ser trocada de Firebase para Supabase para um serviço JWT customizado sem tocar em nenhuma tela que o consome.

Isso não é uma falha do modelo. É uma falha de contexto. O modelo não foi instruído a otimizar para substituibilidade, então não o fez.

O resultado: por padrão, código gerado por AI tende ao acoplamento forte. Não porque o modelo seja ruim, mas porque isolamento nunca é o caminho de menor resistência para um preditor de próximo token.

Substituibilidade é uma decisão de arquitetura

Substituibilidade não acontece por acidente. É uma escolha estrutural deliberada:

  • Toda dependência externa fica atrás de uma interface que a aplicação controla.
  • Todo module gerado expõe um contract, não uma implementação.
  • Todo recurso opcional é injetado, nunca importado diretamente.
  • Toda decisão de composição vive em uma única composition root, não espalhada por cinquenta arquivos.

Isso não é novidade. É inversão de dependência básica. O que é novo é que código gerado por AI torna a violação desses princípios fácil e invisível até o momento em que você precisa mudar alguma coisa.

O efeito composto

Quando todo module é substituível:

  • Gerações ruins custam minutos, não dias.
  • Mudanças de fornecedor são trocas de interface, não rewrites.
  • A velocidade da AI permanece linear ao longo das iterações, não logarítmica.
  • O time pode dizer “regenere isto atrás do mesmo contract” e estar falando sério.

Quando os modules estão emaranhados:

  • Toda mudança exige entender o grafo completo de dependências.
  • Todo refactor assistido por AI corre o risco de quebrar sistemas não relacionados.
  • O time gradualmente deixa de confiar na saída da AI porque o raio de impacto é imprevisível.
  • A velocidade desaba de volta para níveis pré-AI, mas agora com mais código para manter.

O teste prático

Antes de aceitar qualquer module gerado por AI em uma codebase, aplique um teste:

Consigo apagar este arquivo e reimplementá-lo do zero, usando apenas a interface que ele expõe, sem modificar nenhum consumidor?

Se sim, pode lançar. Se não, corrija a fronteira antes de lançar.

Isso é barato de aplicar. Uma composition root somada a uma abordagem interface-first entrega essa propriedade estruturalmente. Você não precisa de uma governança elaborada. Precisa de uma regra arquitetural aplicada com consistência.

A conexão com a arquitetura AI-native

Escrevi sobre esse problema mais amplo em Stanford CS146S está certo sobre AI coding. A disciplina que falta é arquitetura. O princípio da substituibilidade é o mecanismo específico que torna codebases AI-native sobrevivíveis depois da primeira versão.

Stanford está ensinando desenvolvedores a usar ferramentas de AI com eficácia. Isso importa. Mas fluência com ferramentas sem substituibilidade é uma armadilha: você lança mais rápido até a codebase ficar cara de mudar, e então passa a lançar mais devagar do que equipes que nunca usaram AI.

A disciplina não é “fazer prompting melhor”. A disciplina é “criar a arquitetura de modo que erros de prompting sejam baratos de desfazer”.

Perguntas frequentes

O que “arquitetura substituível” significa para código gerado por AI?

Arquitetura substituível significa que todo module gerado por AI fica atrás de uma interface da qual o resto do sistema depende — e não da implementação em si. Quando os requisitos mudam ou a geração sai errada, você apaga o module e o reimplementa sem tocar nos consumidores.

Como você faz enforcement da substituibilidade em uma codebase gerada por AI?

Três mecanismos: interfaces controladas pela aplicação (e não pela dependência), uma única composition root onde todas as implementações são conectadas e uma checagem de CI que falha se algum module importar diretamente os detalhes internos de outro module.

Substituibilidade desacelera o desenvolvimento inicial?

Não. Definir uma interface antes de gerar uma implementação acrescenta segundos. O custo de não fazer isso aparece semanas depois, quando uma troca de fornecedor ou um refactor vira um rewrite completo.

Isso é o mesmo que injeção de dependência?

Injeção de dependência é um mecanismo para alcançar substituibilidade, mas não é o quadro inteiro. Substituibilidade também exige contract tests, validação de fronteiras e uma composition root — não apenas parâmetros de construtor.