Como revisar código gerado por IA que ninguém pode ler
Aprenda a revisar código gerado por IA em escala com testes de propriedades, comparação diferencial, invariantes e inspeção humana direcionada.

Um modelo consegue produzir em uma tarde mais código do que uma equipe competente pode inspecionar em uma semana. Tentar manter o antigo ritual de revisão adicionando mais revisores não resolve esse descompasso. Isso cria uma fila, incentiva aprovações superficiais e ainda deixa passar falhas distribuídas por centenas de funções que parecem plausíveis quando vistas isoladamente.
O padrão viável é a evidência, não a cobertura visual completa. Os revisores devem provar que o novo sistema preserva o comportamento exigido, obedece às regras que precisam valer sempre e não contém decisões inaceitáveis nos poucos pontos em que o julgamento humano não pode ser automatizado. Ler continua sendo necessário, mas a leitura sai de todas as linhas e se concentra nos lugares em que uma linha pode alterar autoridade, dinheiro, dados ou recuperação.
Classifique o risco antes de escolher o método de revisão
A quantidade de código diz muito pouco sobre a quantidade de revisão necessária. O esforço deve acompanhar a consequência de uma decisão errada e a facilidade com que os testes conseguem observá-la. Um analisador gerado a partir de uma gramática precisa pode merecer mais casos automatizados e menos leitura linha por linha do que uma função curta de autorização cuja intenção está em documentos de política e exceções.
Comece dividindo a mudança em superfícies de comportamento. Uma superfície reúne entradas, estado e saídas que podem ser testados como um único contrato: cálculo de fatura, elegibilidade de conta, conversão de arquivo, avaliação de permissão, reinício de lote ou migração de banco de dados. Não divida o trabalho pela quantidade de arquivos gerados. Os modelos costumam espalhar uma decisão entre adaptadores, funções auxiliares e tipos, enquanto um grande mapeador de dados gerado pode conter quase nenhuma decisão independente.
Registre quatro fatos para cada superfície: a consequência da falha, o comportamento de referência disponível, os invariantes aplicáveis e os pontos do código que exercem autoridade. A consequência determina quanta evidência independente é necessária. A referência mostra se o teste diferencial é possível. Os invariantes expõem falhas mesmo quando a implementação de referência as compartilha. Os pontos de autoridade indicam onde as pessoas precisam ler.
Uma matriz de revisão útil se parece com isto:
- Cálculo de impostos: compare o serviço antigo com exemplos aprovados, reproduza casos, verifique a conservação e inspecione o arredondamento e a seleção da alíquota.
- Importação de CSV: use a especificação de formato e amostras de produção, gere entradas malformadas e inspecione erros e limites de recursos.
- Verificação de função: monte uma tabela de decisão a partir das políticas, teste as negações e inspecione todo caminho que concede acesso.
- Reinício de lote: reproduza o histórico registrado dos trabalhos, injete falhas, verifique a idempotência e inspecione os limites de transação.
Essa classificação também responde se é seguro integrar código escrito por um modelo. Ele é seguro o bastante apenas quando as evidências correspondem às consequências da falha e as diferenças não resolvidas têm responsáveis. Nenhuma pontuação do modelo, contagem de testes ou confiança do revisor substitui essa decisão. Um formatador de baixo risco pode passar com propriedades e amostras. Um caminho de pagamento ou permissão precisa de especificações independentes, casos adversos e inspeção direta.
Escreva o contrato observável antes de ler a implementação
O revisor precisa de uma descrição externa do comportamento correto antes de abrir o código gerado. Caso contrário, a implementação vira discretamente sua própria especificação, e uma estrutura plausível é confundida com a intenção correta. O contrato deve descrever o que os chamadores conseguem observar, incluindo valores retornados, mudanças de estado, erros, limites de tempo relevantes e efeitos externos.
Extraia o contrato de fontes que existiam antes da geração: definições de interface, manuais de operação, requisições e respostas de produção, restrições do banco de dados, dados de teste aceitos, regulamentos e entrevistas com as pessoas que tratam exceções. O código antigo é uma fonte, não a única. Os comentários podem estar desatualizados, os testes podem registrar acidentes, e os operadores atuais podem depender de um comportamento que ninguém documentou.
Escreva explicitamente os casos incômodos. O que acontece com uma entrada vazia? Qual fuso horário controla um prazo? Uma nova tentativa repete um e-mail ou reutiliza o resultado anterior? Um campo ausente é diferente de um valor zero? Qual regra decimal vale exatamente entre dois valores representáveis? A ordem tem significado mesmo quando uma API diz que não? Essas perguntas causam mais falhas de migração do que erros de sintaxe ou tipo.
O contrato também precisa marcar as mudanças permitidas. Algumas reescritas devem preservar a saída byte por byte. Outras podem normalizar espaços, substituir um identificador interno ou retornar um erro mais claro sem mudar a categoria. Se os revisores não definirem uma relação de equivalência, a ferramenta de comparação relatará ruído inofensivo ou, pior, normalizará um defeito real até escondê-lo.
Mantenha as declarações do contrato testáveis. “Processa faturas corretamente” é inútil. “Para uma fatura aceita, o débito lançado é igual à soma dos créditos lançados na moeda do livro contábil” pode virar uma propriedade. “As novas tentativas são seguras” é vago. “Repetir o mesmo identificador de solicitação não produz nenhum efeito externo adicional” dá ao mecanismo de testes algo para medir.
Faça isso antes da leitura porque o código gerado é persuasivo. Ele tem nomes, ramificações, verificações e comentários organizados em formas familiares. Quando o revisor vê uma implementação bem arrumada, começa a explicar por que ela faz sentido em vez de perguntar se implementa a regra exigida. O contrato mantém o ônus da prova fora do texto gerado.
Testes de propriedades cobrem um espaço de entradas
Os testes de propriedades funcionam melhor quando você pode declarar uma regra válida para muitas entradas, mas não consegue enumerar todas elas. Eles não provam que um programa está correto, e uma propriedade fraca pode aprovar um absurdo. Sua força está em obrigar o revisor a nomear relações que os exemplos escondem.
Escolha propriedades do domínio, não da implementação. As propriedades de ida e volta servem para codificadores quando decodificar um valor válido codificado deve recuperar o original. As propriedades de conservação servem para dinheiro, estoque e contagens de registros. A idempotência se aplica a novas tentativas, normalização e atualizações que se comportam como conjuntos. A monotonicidade vale quando adicionar um item elegível não pode reduzir um total. As propriedades metamórficas comparam entradas relacionadas, como reordenar registros que o contrato diz não terem ordem.
Um teste de fuzzing compacto em Go para um limite de normalização pode ser assim:
func FuzzNormalizeAccount(f *testing.F) {
f.Add(" ab-123 ")
f.Add("AB123")
f.Fuzz(func(t *testing.T, raw string) {
got, err := NormalizeAccount(raw)
if err != nil {
return
}
again, err := NormalizeAccount(got)
if err != nil {
t.Fatalf("normalized value rejected: %q", got)
}
if again != got {
t.Fatalf("not idempotent: first=%q second=%q", got, again)
}
if strings.ContainsAny(got, " -\t\n") {
t.Fatalf("separator survived: %q", got)
}
})
}
Esse teste verifica duas regras reais: uma normalização bem-sucedida é idempotente, e a saída não contém separadores proibidos. Ele não afirma de propósito que toda string precisa funcionar. Isso transformaria uma política de entrada desconhecida em um requisito inventado. Adicione um gerador separado para formatos de conta válidos se a gramática aceita for conhecida, e exija sucesso apenas desse gerador.
A redução dos casos importa. Quando um gerador encontra uma falha em uma carga de 4.000 caracteres, o artefato útil é a menor entrada que ainda falha. Guarde esse caso reduzido como um dado comum de regressão. A busca aleatória encontra a lacuna; o caso fixo impede que ela volte e torna a revisão repetível. Guarde a semente quando o framework a expuser, mas nunca dependa só dela, pois mudanças no gerador ou no ambiente de execução podem alterar a sequência.
Cuidado com propriedades que apenas repetem o código. Testar que SortRecords devolve o mesmo resultado que outra chamada de SortRecords diz pouco. Testar que a saída está ordenada, contém o mesmo multiconjunto de registros e não muda após uma segunda ordenação verifica fatos independentes. Os testes de mutação podem revelar conjuntos fracos: se mudar uma comparação ou remover uma ramificação de validação deixa todas as propriedades verdes, o conjunto ainda não merece confiança.
Testes diferenciais encontram desvios esquecidos pela especificação
Os testes diferenciais executam as implementações antiga e nova com as mesmas entradas e depois comparam os resultados observáveis segundo uma política explícita de equivalência. Para reescrever um sistema legado, essa costuma ser a maneira mais rápida de descobrir comportamentos ocultos, porque a produção já explorou combinações que um plano de teste recém-escrito não verá.
Construa o mecanismo em torno de um limite, não de funções privadas. Capture uma solicitação ou entrada de trabalho, o estado inicial relevante, o resultado retornado, as mudanças duradouras de estado e os efeitos externos. Depois execute os dois sistemas a partir de condições iniciais equivalentes. Substitua relógios, fontes aleatórias, identificadores e serviços remotos por adaptadores controlados para que o não determinismo não inunde a comparação.
Um registro de comparação deve ser inspecionável, em vez de apenas um contador verde ou vermelho:
{
"case_id": "replay-01842",
"input_hash": "sha256:...",
"old": {"status": "accepted", "total_minor": 1250, "events": ["invoice_posted"]},
"new": {"status": "accepted", "total_minor": 1250, "events": ["invoice_posted"]},
"normalizations": ["generated_id", "timestamp_within_1s"],
"result": "equal"
}
Registre toda normalização. Se o mecanismo remove datas, ordena coleções, mascara identificadores ou mapeia textos de erro em categorias, essa política pertence à revisão. Um normalizador amplo pode apagar exatamente a diferença que você precisa ver. Por exemplo, ordenar todos os arrays pode esconder uma mudança na ordem de lançamentos que afeta o processamento posterior, enquanto comparar identificadores gerados sem tratamento cria falhas sem sentido.
O tráfego de produção exige captura cuidadosa. Remova ou tokenize os campos confidenciais antes que eles entrem em um ambiente geral de testes. Preserve relações das quais o comportamento depende, como o mesmo cliente aparecendo em várias solicitações. Um conjunto de exemplos desconectados e excessivamente limpos pode parecer seguro enquanto perde semântica de sessão, ordem e nova tentativa. Em ambientes regulados, mantenha captura, execução e artefatos dentro do perímetro aprovado.
A cobertura deve descrever o comportamento representado, não apenas o número de reproduções. Divida os casos por operação, resultado, sinalizadores importantes, categoria de erro, formato dos dados e condição de limite. Dez mil leituras bem-sucedidas não compensam a ausência de uma atualização que falhou, um envio duplicado, uma transição de fechamento mensal ou um reinício após uma gravação parcial. Registre partições vazias como dívida explícita de revisão.
Execute o mecanismo continuamente durante a geração e depois das edições humanas. Uma última reprodução encontra limpezas bem-intencionadas que mudam o comportamento depois que o trabalho do modelo termina. Mantenha as divergências como registros duradouros com uma decisão: novo defeito, defeito antigo preservado de propósito, defeito antigo corrigido de propósito, mudança de política esperada ou falha do mecanismo. Uma divergência sem explicação não é uma falha de teste a ser dispensada; é uma decisão inacabada.
O sistema antigo é testemunha, não oráculo
Imitar exatamente a implementação antiga pode preservar defeitos, padrões inseguros e soluções provisórias cujo motivo original desapareceu. A igualdade diferencial comprova compatibilidade, não correção. Você precisa de uma regra independente para todo comportamento com consequências sérias.
Essa distinção fica concreta quando o sistema antigo aceita um estado impossível. Suponha que um lote consiga marcar uma fatura como paga depois de gravar o lançamento contábil, mas antes de confirmar a referência do pagamento. A reprodução da produção mostra essa sequência, então a reescrita a repete. Uma barreira apenas de paridade relata sucesso. Uma propriedade de conservação também pode passar porque o dinheiro fecha. Somente um invariante que exija uma referência confirmada para o estado pago revela a transição inválida.
Também não corrija silenciosamente toda estranheza. Um defeito pode ter virado uma interface. Relatórios posteriores podem esperar um arredondamento incomum, os operadores podem usar um código de erro específico para encaminhar trabalho, ou os clientes podem tentar de novo somente depois de um determinado status. Corrigir esse comportamento sem uma decisão de migração pode causar uma falha maior do que preservá-lo temporariamente.
Use um registro de diferenças com cinco campos: comportamento observado, regra esperada independente, consumidores afetados, decisão escolhida e responsável. Se a nova versão diferir de propósito, adicione um teste para a nova regra e uma nota de versão ou mudança operacional quando necessário. Se o defeito precisar permanecer por compatibilidade, isole-o atrás de uma regra de compatibilidade com nome para que futuros mantenedores não o “limpem” sem querer.
A recomendação popular de fazer o novo conjunto passar por todos os testes antigos é incompleta. Os testes antigos são ótimas testemunhas do comportamento conhecido, mas carregam os mesmos pontos cegos e suposições erradas que o sistema. Trate-os como um conjunto de evidências. Adicione propriedades derivadas de regras de negócio, testes negativos derivados da análise de ameaças e verificações de transição derivadas das restrições dos dados.
O revisor deve desconfiar quando a paridade chega a 100 por cento com facilidade demais. O limite pode ser estreito demais, os dados de teste podem omitir casos difíceis, ou o comparador pode ignorar diferenças demais. Inspecione uma amostra dos rastros brutos antigos e novos, incluindo falhas, antes de confiar no agregado. Uma boa evidência continua legível quando você a abre.
Os invariantes devem sobreviver a toda entrada e saída
Um invariante é uma condição que precisa valer em todos os estados válidos do sistema, não apenas uma asserção presa ao caminho feliz. Coloque verificações de invariantes nos limites em que o estado entra, muda e sai: manipuladores de API, consumidores de mensagens, confirmações de transação, importações de arquivos, pontos de recuperação de trabalhos e serializadores.
Classifique os invariantes pelo escopo. Os locais limitam um valor, como uma quantidade que não pode ser negativa. Os agregados relacionam uma coleção, como débitos iguais a créditos. Os temporais limitam a ordem, como a aprovação anterior ao desembolso. Os de autoridade limitam os atores, como impedir um usuário de aprovar uma solicitação que criou. Os de recuperação limitam novas tentativas e reinícios, como um único efeito duradouro por token de idempotência.
As restrições do banco de dados fornecem aplicação forte e independente para algumas regras. Uma restrição de unicidade pode impedir identificadores duplicados mesmo que todos os chamadores cometam o mesmo erro. Uma chave estrangeira evita um registro órfão. Uma restrição de verificação pode rejeitar uma combinação inválida de status e campo. Os testes da aplicação ainda devem percorrer o caminho de erro resultante, pois uma rejeição tecnicamente segura pode virar interrupção operacional se o worker tentar para sempre.
As máquinas de estado merecem testes explícitos de transição. Gere sequências válidas e confirme que cada transição mantém todos os invariantes. Depois gere uma transição inválida em cada estado e exija rejeição sem mudança parcial. Uma asserção apenas do estado final perde danos temporários, mensagens duplicadas e gravações que sobrevivem a uma resposta de erro. Capture o estado antes e depois da tentativa.
Posicione as asserções de execução conforme a consequência. Verificações baratas em entradas não confiáveis podem rodar em toda solicitação. Uma conciliação cara entre tabelas pode rodar na confirmação, em um processo paralelo ou como barreira de implantação. Não remova um invariante útil apenas porque ele custa caro no caminho mais movimentado; mova-o para o ponto mais próximo onde ainda detecta a falha antes que o dano se espalhe.
Monitore as violações por identidade, não como exceções genéricas. O alerta deve informar a regra, a entidade afetada, a operação e a versão. Evite registrar a carga confidencial usada para detectá-la. Uma contagem sem identidade não orienta reversão nem diagnóstico, enquanto uma carga completa pode criar uma exposição de dados separada.
Os revisores devem perguntar quem responde por cada invariante. Se todos concordam que os saldos precisam fechar, mas nenhum teste, restrição, verificação em execução ou conciliação programada impõe isso, o invariante só existe na conversa. Atribua pelo menos um controle executável e uma resposta clara a toda regra cuja violação tenha importância.
A revisão humana pertence aos pontos semânticos decisivos
As pessoas devem ler o código onde a intenção não pode ser deduzida apenas de exemplos de entrada e saída ou onde uma decisão pequena controla uma consequência grande. Esses pontos semânticos decisivos incluem autorização, criptografia, limites de transação, migração de esquema, controle de concorrência, exclusão de dados, efeitos externos, recuperação de erros e todo adaptador que normaliza evidências de teste.
Leia todos os caminhos que permitem acesso no código de autorização. Um grande conjunto de negações ajuda, mas o significado da política costuma depender de herança de funções, limites entre clientes, autoridade delegada e comportamento padrão quando falta contexto. Revise a fonte da regra ao lado do código. Confirme que o padrão nega, que as decisões em cache têm o escopo correto e que os registros não revelam dados protegidos.
Leia os limites de transação e de nova tentativa como uma unidade. Encontre o ponto em que a operação se torna duradoura e siga todos os erros que podem ocorrer antes e depois. Verifique se uma nova tentativa repete uma gravação, envia uma segunda mensagem ou observa estado parcialmente confirmado. O código gerado costuma tratar cada erro localmente de forma plausível enquanto perde a sequência entre funções que gera duplicidade.
Leia o comparador e o mecanismo com mais ceticismo do que um auxiliar de teste comum. A evidência só é tão honesta quanto a máquina que chama duas execuções de iguais. Um modelo que escreveu o código de produção não deve ser o único autor e juiz da própria política de equivalência. Uma pessoa deve aprovar campos ignorados, tolerâncias, ordem canônica e mapeamentos de categorias de erro.
Leia as mudanças de dependências e da configuração gerada. Os testes talvez nunca exercitem uma opção insegura do analisador, uma permissão de rede ampla demais, uma verificação de certificado desativada ou um conjunto ilimitado de workers. Inspecione arquivos de bloqueio, scripts de compilação, privilégios de contêiner, permissões do banco, configurações de desserialização, tempos limite e limites de recursos. Essas escolhas podem mudar a superfície de ataque sem alterar a saída funcional comum.
Amostre o código comum apenas depois de cobrir os pontos decisivos. Use uma amostragem ponderada por risco: inspecione um caminho vertical completo para algumas operações representativas, além de código com alta complexidade, incerteza incomum do modelo, reparos manuais repetidos ou pouco alcance dos testes. Amostrar linhas aleatórias cria aparência de cuidado, mas raramente acompanha uma decisão o suficiente para julgá-la.
A revisão humana ainda termina com uma decisão escrita. O revisor deve dizer o que inspecionou, em quais evidências confiou, o que excluiu e qual risco residual permanece. “Parece bom” não é um registro de aprovação para uma mudança gerada que ninguém conseguiu ler por inteiro.
A evidência precisa da própria cadeia de custódia
Grandes mudanças geradas exigem um registro de revisão que conecte requisitos, testes, partições de reprodução, divergências, descobertas humanas e a compilação exata sob análise. Sem essa conexão, as equipes acumulam milhares de resultados aprovados que podem pertencer a outros commits, dados de teste, normalizadores ou versões de dependências.
Dê uma identidade estável a cada artefato. Registre a revisão de origem, a revisão gerada, as entradas de compilação, a versão do mecanismo, o estado dos dados de teste, as sementes aleatórias relevantes e a configuração do ambiente que afeta o comportamento. Calcule o hash das entradas capturadas depois da remoção aprovada para que os revisores saibam se dois relatórios usaram o mesmo corpus sem reter dados brutos proibidos.
Mapeie cada regra do contrato para evidências. Uma regra pode ter um teste de propriedade, uma restrição do banco, uma partição de reprodução e uma nota de inspeção manual. Outra pode ter apenas uma aprovação manual porque a automação não consegue observar o julgamento de negócio. Mapeamentos vazios são úteis: eles mostram exatamente onde a aprovação se apoia em suposição. Um painel cheio de contagens verdes esconde essa lacuna.
Coloque evidências instáveis em quarentena em vez de executar novamente até ficarem verdes. Registre o caso com falha, determine se o não determinismo vem do produto ou do mecanismo e corrija a causa. Repetições mudam o significado da barreira de “a compilação passou” para “uma tentativa passou”, uma afirmação muito mais fraca. Se um teste ainda não puder bloquear, marque-o como não bloqueante e mantenha suas falhas visíveis.
Preserve os contraexemplos e as decisões sobre divergências junto com a mudança. Eles explicam o comportamento melhor do que um comentário gerado porque contêm uma entrada, uma observação e um resultado aprovado. Quando uma reescrita posterior mudar a mesma superfície, esses artefatos virarão uma memória institucional compacta que não depende da presença do revisor original.
A CodeHero usa esse princípio durante reescritas de sistemas legados ao verificar o comportamento com um mecanismo de paridade contra tráfego de produção registrado, enquanto a arquitetura é modernizada em vez de copiar a estrutura antiga linha por linha. Essa afirmação só tem utilidade quando o corpus de reprodução, a política de comparação e as decisões sobre divergências permanecem abertos à revisão do cliente.
O registro deve poder ser reproduzido por alguém que não gerou o código. Se um segundo engenheiro não consegue executar as verificações especificadas e obter o relatório, a equipe tem uma apresentação, não evidências. A reprodutibilidade também limita a dependência das explicações do modelo, que podem soar coerentes sem corresponder à compilação.
A evidência expira quando o sistema ao redor muda. Um relatório de reprodução coletado antes de uma migração de esquema, atualização de dependência ou edição do comparador não aprova a compilação posterior. Defina regras de invalidação no registro: mudanças no código contratual repetem as propriedades afetadas, mudanças de normalização exigem revisão das igualdades anteriores e mudanças de persistência repetem as sequências de recuperação. Assim um relatório que já foi verde não vira permissão permanente.
Mantenha as evidências perto da superfície proprietária. Um relatório de entrega enorme dificulta saber qual verificação protege qual comportamento e incentiva os revisores a aprovar o pacote como um objeto único. Registros por superfície permitem que a equipe substitua um componente, repita sua comprovação e deixe evidências não relacionadas intactas. Eles também expõem controles compartilhados. Se cinco superfícies dependem do mesmo adaptador de relógio ou regra de comparação, esse componente merece inspeção direta porque um erro pode corromper cinco conclusões.
Revise o processo de evidência depois de defeitos que escaparam. Pergunte qual cláusula faltava, qual gerador não conseguia produzir o caso, qual partição estava vazia, qual invariante não existia ou qual inspeção humana pulou a ramificação decisiva. Adicione o menor controle duradouro que teria detectado essa classe de erro. Pedir que os revisores leiam mais linhas arbitrárias aumenta o custo sem corrigir o ponto cego.
Uma política de retenção deve corresponder à necessidade de reproduzir a aprovação e à sensibilidade dos dados capturados. Mantenha contraexemplos reduzidos e metadados quando possível. Restrinja ou descarte cargas brutas de produção de acordo com as regras do cliente. A capacidade de explicar uma decisão não concede permissão para guardar cada byte usado para tomá-la.
A aprovação deve declarar o risco residual
Uma reescrita gerada está pronta quando o comportamento exigido tem evidências independentes, as decisões perigosas receberam inspeção humana e a incerteza restante cabe no orçamento de falhas do sistema. Conclusão não é uma porcentagem de linhas lidas. É uma declaração defensável sobre o que ainda pode dar errado e como a equipe detectará ou conterá o problema.
Defina barreiras segundo as consequências. Para um conversor interno de baixo impacto, exemplos representativos, propriedades do analisador e reversão podem bastar. Para movimentação de dinheiro, controle de acesso, registros regulados ou exclusão irreversível, exija fontes contratuais independentes, testes negativos, aplicação de invariantes, cobertura diferencial de partições importantes, inspeção direta dos pontos decisivos e uma resposta operacional para violações.
Não combine toda a evidência em uma única pontuação. Uma taxa alta de correspondência não compensa um padrão de autorização sem revisão. Uma boa cobertura de propriedades não compensa um comparador que mascara a ordem. Uma leitura cuidadosa não compensa a ausência de testes de nova tentativa. Mantenha as condições de veto visíveis para que um agregado atraente não enterre uma lacuna séria.
Use um registro curto de aprovação:
- Informe a compilação, a versão do contrato e o estado das evidências.
- Liste as barreiras exigidas e seus resultados.
- Liste cada divergência aceita e cada teste não bloqueante.
- Informe os pontos revisados por pessoas e os revisores.
- Declare riscos residuais, controles de detecção, limites de reversão e responsáveis.
Interrompa a entrega quando um comportamento de grande consequência não tiver um oráculo independente, um invariante ou revisão direta. Às vezes, a decisão honesta é reduzir o escopo: migrar caminhos de leitura antes dos de escrita, executar decisões em paralelo sem agir ou manter uma operação perigosa na implementação antiga até entender seu contrato. Isso é controle de engenharia, não falta de ambição.
A saída dos modelos muda a economia da escrita de código, mas não muda quem sofre a consequência de uma entrega ruim. Peça aos revisores que aprovem evidências que conseguem reproduzir e riscos que conseguem nomear. Não peça que abençoem um volume de texto que ninguém conseguiria absorver com responsabilidade.
Perguntas frequentes
A revisão de código gerado por IA pode ser automatizada?
Grande parte da coleta de evidências pode ser automatizada, incluindo propriedades, comparações de reprodução, verificações de invariantes e partições de cobertura. A aprovação não pode ser totalmente automatizada quando a intenção de uma política, a autoridade, os efeitos irreversíveis ou o risco aceitável exigem julgamento humano.
Os revisores precisam ler toda linha escrita por um modelo?
Não. Eles devem ler cada ponto semântico decisivo e caminhos completos suficientes para avaliar a estrutura, enquanto as evidências automatizadas cobrem o comportamento amplo. Ler linhas aleatórias em uma mudança enorme oferece pouca garantia e desperdiça atenção.
Qual é a diferença entre testes de propriedades e testes diferenciais?
Os testes de propriedades verificam uma regra que deve valer para muitas entradas geradas. Os testes diferenciais comparam duas implementações com a mesma entrada, então encontram desvios de comportamento, mas também podem preservar um defeito antigo.
Quanto tráfego de produção um teste diferencial deve reproduzir?
Não existe uma contagem universal honesta. Divida o tráfego por operação, resultado, limite, erro e transição de estado, e mostre as partições importantes sem cobertura em vez de comemorar um total bruto alto.
Igualar a implementação antiga basta em uma reescrita legada?
Não. A igualdade comprova compatibilidade com o comportamento observado, inclusive defeitos do sistema antigo. Adicione invariantes independentes e testes derivados de políticas sempre que um resultado errado tiver consequências sérias.
O que forma um bom invariante para código gerado?
Um bom invariante declara uma condição que precisa valer em todo estado válido e pode ser verificada independentemente da implementação. Exemplos incluem lançamentos equilibrados, isolamento entre clientes, transições válidas e um efeito duradouro por identificador de solicitação.
Onde as pessoas devem focar ao revisar código escrito por modelo?
Concentre-se em autorização, transações, concorrência, exclusão, criptografia, mudanças de esquema, novas tentativas, efeitos externos e no próprio mecanismo de comparação. Esses lugares concentram muito significado do sistema em relativamente pouco código.
Como tratar uma diferença encontrada durante a reprodução?
Registre o resultado antigo, o novo, a regra esperada, os consumidores afetados, o responsável e a decisão escolhida. Depois adicione um teste para o resultado aprovado e nunca esconda a diferença em uma normalização ampla ou dispensa sem explicação.
É possível confiar nos testes escritos pelo mesmo modelo?
Trate-os como rascunhos úteis, não como prova independente. Derive propriedades de regras externas, inspecione comparadores e geradores, use testes de mutação quando fizer sentido e peça a uma pessoa que aprove as suposições capazes de esconder uma falha.
Quando uma reescrita gerada deve ser impedida de chegar à produção?
Interrompa quando um comportamento de grande consequência não tiver evidência independente, um caminho perigoso não tiver revisão direta ou uma divergência séria não tiver decisão. Um escopo de migração menor é mais seguro do que aprovar uma incerteza sem responsável.