O Que E Conflito Gerador - O Que é Conflito Gerador - BRAINCP
O Que é Conflito Gerador - BRAINCP

Entendendo conflitos em sistemas de geração de código

Trabalhando com geradores de código, seja para scaffolding de projetos, ORM, ou ferramentas como Django, Laravel, ou até mesmo geradores de parser, você eventualmente se depara com o que chamamos de conflito gerador. É aquele momento em que duas ou mais regras de geração tentam produzir o mesmo arquivo, variável ou estrutura ao mesmo tempo, e o sistema não sabe qual prevalece. No meu caso, aprendi isso na prática há alguns anos quando estava configurando um pipeline de geração de código para uma API REST. Tínhamos um gerador que criava modelos a partir de um schema OpenAPI e outro que gerava migrations de banco a partir de definições de entidades. Ambos produziam arquivos com o mesmo nome, mas conteúdos diferentes. O resultado era um caos silencioso: o build passava, mas a aplicação rodava com modelos desatualizados.

O que e conflito gerador

Um conflito gerador ocorre quando múltiplas fontes, templates ou regras de um sistema de code generation tentam escrever no mesmo espaço de saída sem um mecanismo de resolução de prioridade. Isso é especialmente comum em projetos que usam múltiplas ferramentas de geração simultaneamente, como quando você combina um gerador de GraphQL com um gerador de typescript, e ambos definem a mesma interface. O problema não é apenas estético. Conflitos geradores podem causar comportamento indefinido, perda de dados, ou pior ainda, geração silenciosa de código incorreto que só aparece em produção. Já vi times inteiros perderem dias rastreando bugs que na verdade eram conflitos de geração mal resolvidos.

Como funciona na prática

Quando você roda um gerador, ele geralmente segue este fluxo básico: lê uma fonte de verdade (schema, modelo, especificação), aplica templates ou regras de transformação, e escreve arquivos de saída. O conflito acontece quando dois geradores diferentes têm a mesma fonte de verdade ou regras sobrepostas que apontam para o mesmo destino. Vou te dar um exemplo concreto que me aconteceu recentemente. Estávamos usando um gerador para criar DTOs a partir de protobufs e outro para gerar validadores Zod. O protobuf definia um campo `user_id` como `string`, mas o gerador de validadores tinha uma regra hardcoded que tratava qualquer campo terminando em `_id` como `number`. O resultado? O código gerado compilaria, mas falharia em runtime com erro de tipo. A solução foi criar um arquivo de config central que mapeava explicitamente cada campo protobuf para seu tipo Zod correspondente, sobrescrevendo qualquer convenção automática.

Sinais de que você tem um conflito gerador

A primeira coisa que observei foram diffs estranhos no git. Arquivos que deveriam ser idempotentes mudavam a cada execução do gerador. Se você roda o comando duas vezes e o resultado é diferente na segunda vez, provavelmente tem um conflito de ordem de processamento ou de estado compartilhado entre geradores. Outro sinal clássico é quando templates dependem de variáveis globais que podem ser sobrescritas. Em projetos grandes, é comum ter um state object compartilhado entre todos os geradores, e se dois deles escrevem na mesma chave sem sincronização, o resultado é imprevisível.

👉 Clique no botão abaixo para saber mais sobre o assunto!

Estratégias de resolução

A abordagem mais straightforward é estabelecer uma hierarquia clara de namespaces de saída. Cada gerador deve ter seu próprio diretório ou prefixo de arquivo. Isso elimina 90% dos conflitos na maioria dos projetos. Eu costumo organizar assim: `src/generated/models/`, `src/generated/validators/`, `src/generated/api/`. Se dois geradores precisam do mesmo dado, eles leem da mesma fonte, mas escrevem em destinos separados. Para conflitos que envolvem a mesma entidade mas com propósitos diferentes, o padrão que funcionou melhor pra mim foi criar um layer de merge manual. Em vez de tentar fazer os geradores cooperarem automaticamente, eu gero tudo para arquivos temporários, executo um script de merge que combina os resultados baseado em regras definidas, e só então copio para o diretório final. O script de merge é simples: ele lê ambos os arquivos, identifica divergências, e aplica prioritariamente as definições do gerador de mais alta hierarquia.

Outra tática que uso frequentemente é o versionamento de schema. Cada gerador consome uma versão específica do schema de entrada, e mudanças no schema são feitas de forma backwards-compatible. Isso evita que atualizações em um gerador quebrem a geração de outro. Claramente, essa abordagem tem limitações: se você precisa de recursos novos que quebram compatibilidade, vai ter que fazer um migrate manual de todos os geradores envolvidos.

Pegadinhas comuns

O erro mais frequente que vejo em projetos novos é assumir que geradores são stateless. Na realidade, muitos geradores mantêm estado entre execuções, especialmente quando usam caching de templates ou memória compartilhada. Se você não limpa esse estado entre runs, conflitos aparecem de forma intermitente e são pesadelos de debugging. Outro ponto que não recebe atenção suficiente é a ordem de execução. Em pipelines com múltiplos geradores, a ordem importa. Se o gerador B depende de tipos definidos pelo gerador A, mas B é executado primeiro, você terá imports quebrados. A solução óbvia é topological sort baseada nas dependências entre geradores, mas pouquíssimos projetos implementam isso corretamente.

Uma limitação real que você precisa aceitar: conflito gerador não pode ser completamente eliminado em sistemas complexos. Às vezes, a única solução é aceitar duplicação controlada. Se dois geradores precisam de definições similares mas com propósitos diferentes, manter cópias separadas é mais seguro do que tentar forçar um mecanismo de herança que vai gerar conflitos no futuro. Sim, isso viola DRY, mas em geração de código, DRY cego custa mais do que economiza.

Checklist prático

Na hora de configurar seu sistema de geração, faça estas verificações básicas. Primeiro, liste todos os destinos de saída e identifique sobreposições. Segundo, defina uma ordem de execução explícita baseada em dependências. Terceiro, implemente um pré-check que compara hashes dos arquivos gerados entre runs consecutivos para detectar não-determinismo. Quarto, mantenha um log de quais fontes cada gerador consumiu, para facilitar rollback quando algo der errado. E quinto, teste com dados reais, não com fixtures perfeitas, porque é nos bordos que os conflitos aparecem. Isso geralmente corta o tempo de debugging de conflitos de horas para minutos, dependendo do tamanho do projeto. A regra geral é: quanto mais geradores você tiver, mais rigoroso precisa ser o controle de namespace e dependência. Projetos com menos de três geradores raramente precisam de toda essa infraestrutura, então avalie se o custo de implementação vale a pena para o seu caso.