- Desenvolvedores da Anthropic migraram, no último mês, 10 pacotes com dezenas a centenas de milhares de linhas usando Claude Fable 5, Claude Opus 4.8 e workflows dinâmicos; em vez de corrigir código individualmente, melhoraram o processo iterativo que gera o código
- A migração Zig→Rust do Bun gerou 1 milhão de linhas em menos de 2 semanas e passou em 100% dos testes existentes antes do merge; um projeto Python→TypeScript moveu 165.000 linhas ao longo de um fim de semana, usando centenas de agentes, 8 gates de etapas e 3 revisões adversariais
- Migrações em grande escala são adequadas para criar um loop de validação objetivo, pois o trabalho pode ser paralelizado, o código existente serve como especificação e resposta correta, e falhas de compilação/teste geram automaticamente a próxima fila de tarefas
- O processo avança em etapas: preparar critérios de julgamento, criar rulebook, mapa de dependências e inventário de lacunas, fazer teste de estresse das regras, traduzir tudo, compilar, executar e comparar comportamento; erros recorrentes não são corrigidos arquivo por arquivo, mas sim por ajuste das regras superiores e regeneração
- O custo ainda fica na casa de dezenas a centenas de milhares de dólares ou mais, mas é possível descartar branches fracassadas e tentar novamente; a migração do Bun consumiu cerca de US$ 165.000 a preços de API e resultou em menor uso de memória, binários 19% menores e melhoria de 2–5% em workloads reais
Melhorando o loop de geração, não o código
- Migração de código com IA é a abordagem em que agentes movem uma base de código de produção para uma nova linguagem ou framework
- Em vez de engenheiros traduzirem arquivos diretamente, eles escrevem regras de migração e loops de validação
- Os agentes repetem tradução, compilação e testes até que o comportamento do novo código corresponda ao original
- Projetos que antes levavam vários anos podem ser reduzidos para uma escala de semanas
- Na Anthropic, Claude Fable 5, Claude Opus 4.8 e workflows dinâmicos foram usados para migrar, em um mês, 10 pacotes com dezenas a centenas de milhares de linhas de código
- O princípio operacional central é não remendar diretamente o código gerado, mas corrigir o loop que criou esse código
Casos reais de migração
-
Migração Zig→Rust do Bun
- Jarred Sumner migrou o Bun de Zig para Rust com Claude Code
- Gerou 1 milhão de linhas de código em menos de 2 semanas
- Antes do merge, passou em 100% da suíte de testes existente do Bun no CI
- As 19 regressões encontradas depois do merge foram todas corrigidas
- O port para Rust entrou no Claude Code em junho
- O Bun tem mais de 10 milhões de downloads mensais e também é usado amplamente dentro do Claude Code
-
Migração Python→TypeScript
- Mike Krieger migrou uma base de código Python para 165.000 linhas de TypeScript durante um fim de semana
- Usou centenas de agentes, 8 gates de etapas e 3 revisões adversariais
- Realizou uma verificação final de equivalência comparando a saída de todos os comandos com a versão original em Python
- Repetiu o processo de descartar todo o resultado da migração e ajustar regras e workflow; adotou o resultado da terceira execução
Quando reavaliar uma migração de linguagem
- Vale considerar uma migração se, desde o desenvolvimento inicial, o ambiente técnico mudou a ponto de compromissos antigos virarem limitações, abordagens melhores surgirem ou o ecossistema original encolher
- Zig oferecia desempenho no nível de C e simplicidade, o que era adequado ao contexto inicial em que Bun era desenvolvido por uma pessoa só, mas essa simplicidade tinha trade-offs conhecidos
- No passado, migrar de linguagem exigia interromper o roadmap e alocar recursos por vários trimestres
- Podia ser necessário manter duas bases de código em paralelo por vários trimestres ou anos
- Se a equivalência final de comportamento ficasse em 90%, o problema de manutenção poderia ser maior do que antes de começar
- Agora existe a opção de apagar uma branch fracassada e executar novamente
- Migrar 1 milhão de linhas já não exige necessariamente US$ 3 milhões a US$ 4 milhões em custo de engenharia ao longo de 4 anos, mas ainda pode custar de dezenas a centenas de milhares de dólares ou mais
- A migração do Bun consumiu 5,9 bilhões de tokens de entrada não cacheados e 690 milhões de tokens de saída
- A custo de API, o valor foi de cerca de US$ 165.000
- No port de Mike, o trecho central usou 27 milhões de tokens
- A justificativa de negócio para a migração já não precisa necessariamente ser existencial; um ano corrigindo bugs recorrentes de memória ou um único gargalo crônico pode bastar
-
Remoção do gargalo de build em Python
- A ferramenta interna de Mike era entregue aos usuários como um único binário, mas criar binários por plataforma com a toolchain Python levava cerca de 8 minutos
- Na matriz completa de build, era preciso esperar cerca de 30 minutos a cada release
- Depois da migração para TypeScript, a compilação caiu para cerca de 2 segundos, a inicialização do binário ficou 6 vezes mais rápida e uma pipeline de distribuição separada foi descartada
Por que agentes de IA são adequados para migrações
- Trabalho paralelo é possível
- O código pode ser dividido em milhares de unidades independentes, como arquivos e crates, para vários agentes processarem ao mesmo tempo
- O código existente funciona como uma especificação clara e abrangente
- Também pode servir como referência central para criar instruções para agentes de tradução
- A suíte de testes atua como juiz embutido
- Se a validação é objetiva, o modelo pode iterar por dias com base na resposta correta, sem que humanos arbitrem qualidade continuamente
- Falhas de compilação ou teste se tornam automaticamente próximos itens de trabalho, reduzindo a necessidade de escrever uma fila de tarefas separada
- Consistência e tratamento de exceções podem ser incorporados ao loop
- Revisores associam cada problema à regra violada
- A forma de resolver uma exceção vira uma regra que todos os agentes passam a seguir
- Em vez de divergências silenciosas de comportamento, violações de regras viram itens de trabalho explícitos
- Fable e Opus 4.8 foram usados para delegar, coordenar e verificar o trabalho paralelo de subagentes, além de encontrar múltiplos caminhos até o objetivo
- O uso de tokens foi otimizado com um padrão consultivo que combina vários níveis de modelos
Pré-requisito: julgar equivalência entre original e port
- Antes de iniciar a migração, é necessário um juiz forte que avalie o código original e o código-alvo pelos mesmos critérios
- Sem juiz, não há critério de sucesso nem condição de término
- Testes que dependem de funções internas da linguagem original podem não rodar sem alterações no código-alvo
- Classifique os testes existentes entre testes que podem ser expressos como chamadas externas e testes dependentes de implementação interna que não serão portados
- Reescreva testes de comportamento externo como asserções que possam rodar tanto no original quanto no port
- Um agente adversarial verifica se as asserções não foram enfraquecidas no processo de reescrita
- Execute o juiz no código original para confirmar que passa e depois verifique se ele falha em código quebrado intencionalmente
- Jarred tinha uma grande suíte de testes escrita em uma terceira linguagem, TypeScript
- Mike criou um harness de equivalência com 7 cenários de uso reais e tratou toda mudança de comportamento como bug a ser corrigido
Etapa 1: rulebook, mapa de dependências e inventário de lacunas
- Os artefatos de base não são uma simples tradução, mas uma lista de locais a refatorar, um rulebook de como traduzir e um mapa de dependências que define a ordem de trabalho
- A ordem de criação importa
- Primeiro é preciso definir os padrões do rulebook para então identificar, no inventário de lacunas, os itens que não podem ser tratados por esses padrões
- O rulebook e o inventário de lacunas são validados juntos por auditoria conjunta
-
Rulebook
- A forma do rulebook depende de o novo código preservar a estrutura antiga ou ser completamente redesenhado
- Ao preservar a estrutura, como Jarred, o centro é uma tabela de correspondência entre tipos e expressões idiomáticas das linguagens, e os componentes difíceis de traduzir remetem ao inventário de lacunas
- Ao redesenhar, como Mike, o rulebook funciona como documento de design
- Jarred conversou com Claude para criar políticas para cada área ambígua e configurou 8 subagentes para revisar, cada um, uma das 8 categorias de erro esperadas
-
Mapa de dependências
- Em migrações paralelas, é necessário entender dependências entre arquivos para decidir quais mover primeiro e quais agrupar no mesmo lote
- Em código legado sem manifesto explícito e em bases C/C++, Python etc., as dependências precisam ser descobertas e mapeadas diretamente
- Agentes do Claude Code podem gerar esse mapa em um loop que escreve e executa scripts determinísticos, revisa e corrige os resultados
- Um exemplo generalizado está no prompt de mapa de dependências
-
Inventário de lacunas entre linguagens e revisor cético
- O inventário de lacunas registra conhecimentos que existem implicitamente no código atual, mas precisam ser explicitados na linguagem-alvo
- Em Zig→Rust, a principal lacuna era o modelo de gerenciamento de memória
- Em Zig, o fato de o chamador precisar liberar um buffer pode existir apenas em comentários; mesmo se a liberação for esquecida, o código compila e o vazamento só aparece em execução
- Em Rust, a propriedade é transferida para o chamador e a memória é liberada automaticamente; uso após move ou dupla liberação não compila
- Em Python→TypeScript, interfaces e contratos eram a principal lacuna
- Python não exige declarar o formato dos objetos recebidos nem dos valores retornados
- Em TypeScript, é preciso escrever contratos de métodos, argumentos e formas de retorno para compilar
- Jarred listou as lacunas antes da tradução, enquanto Mike traduziu primeiro e criou a lista durante a auditoria; ambos os enfoques podem ser usados conforme o projeto
- Um exemplo generalizado está no prompt de criação de inventário de lacunas
Etapa 2: teste de estresse das regras
- Antes da migração completa, faça uma pequena migração experimental para pressionar o rulebook e encontrar problemas
- Jarred comparou três trabalhos de agentes
- O primeiro agente traduziu 3 arquivos seguindo o rulebook
- O segundo agente traduziu o mesmo escopo como se fosse um engenheiro Rust experiente
- O terceiro agente escreveu novas regras de tradução com base nas diferenças entre os dois resultados
- Esse processo encontrou 2 problemas graves antes que se espalhassem por todos os 1.448 arquivos
- Essa abordagem só funciona em migrações que preservam a estrutura, nas quais é possível comparar linha a linha duas traduções do mesmo arquivo
- Em redesenhos como o de Mike, revisores adversariais atacam o documento de design, e a validação deve vir de execuções end-to-end descartáveis
- Todos os arquivos traduzidos no teste devem ser descartados; o objetivo é melhorar as regras, não avançar gradualmente no código
- Um exemplo generalizado de tarefa está no prompt de teste de estresse
Etapa 3: tradução de todo o código
- Todas as etapas seguintes usam um loop multiagente de implementar→revisar→corrigir
- A implementação em massa pode ficar com modelos menores, enquanto a revisão fica com modelos maiores
- Mike usou 12 subagentes Claude Sonnet ao paralelizar a migração central
- A fila de trabalho é gerenciada mecanicamente
- Um script de lote considera uma tarefa concluída pela existência, em disco, do arquivo traduzido
- Os arquivos restantes são divididos em lotes para agentes de implementação
- A cada execução, a fila é reconstruída a partir do estado do disco, permitindo retomar após interrupções por padrão
- Se o agente processar pouco por excesso de cautela, pode-se instruí-lo de forma mais direta, contextualizando que o compilador vai capturar erros na próxima etapa
- Itens que não puderam ser tratados com confiança são marcados como
// TODO(port): <reason>e resolvidos na etapa 4 - A lista de trabalho posterior é gerada automaticamente a partir de erros de compilação, falhas em smoke tests e falhas de testes
-
Revisão adversarial e atualização de regras
- Dois revisores adversariais em contextos independentes avaliam o resultado da implementação; se discordarem, um terceiro agente decide
- Quando o mesmo erro se repete em vários arquivos, os arquivos não são corrigidos individualmente
- Adiciona-se uma frase ao rulebook e os lotes afetados são regenerados
- O rulebook continua crescendo também durante a etapa de tradução, e código que contraria as regras não é remendado manualmente
-
Onde colocar o compilador
- Se a compilação é rápida, ela pode ser incluída no loop de tradução
- Mike executava a compilação TypeScript em todos os loops, pois cada unidade terminava em segundos
- Se a compilação demora, deixe-a para a etapa seguinte
- Jarred proibiu o uso do compilador no loop de tradução porque executar
cargolevava minutos - A partir desta etapa, os prompts ficam mais curtos; um exemplo está no prompt de início de tradução
Etapas 4–6: compilar, executar e verificar equivalência de comportamento
- As três etapas compartilham a mesma estrutura de loop, e quanto mais avançam, menos julgamento humano é necessário
- Dependendo da linguagem e do tamanho do projeto, a etapa de compilação pode ser absorvida pela etapa de tradução completa
-
Etapa 4: compilação
- Jarred configurou um script orquestrador para executar o compilador uma vez em todo o workspace
- Agentes de correção processavam a lista de erros em paralelo, passavam por revisão adversarial e então repetiam o build
- A revisão da lista de erros era usada para identificar problemas sistêmicos, não erros individuais
- Depois de corrigir imports circulares permitidos pela compilação preguiçosa de Zig, surgiram milhares de erros de módulos Rust
- A lógica para classificar quais dependências apagar, mover ou quais limites reestruturar foi adicionada ao loop
-
Etapa 5: execução e smoke tests
- Crashes em smoke tests atuam como a mesma resposta mecanicamente correta das listas de erros de compilação
- Em vez de tratar problemas individualmente, eles são agrupados por causa raiz e revisados por subagentes adversariais
-
Etapa 6: comparação de comportamento com o original
- Divida o código que já passou por tradução, compilação e smoke tests, e execute a suíte de testes preparada na etapa preliminar tanto no original quanto no port
- Agentes de correção analisam juntos os testes falhos e as duas bases de código, enquanto revisores adversariais verificam as correções
- Restrinja a reconstrução do binário apenas ao daemon de build
- Agentes de correção escrevem patches, e o daemon agrega os patches e faz uma única recompilação
- Os testes afetados são reexecutados e os resultados retornados
- O trabalho é serializado para impedir que vários agentes executem builds caros separadamente
- Se a mesma falha se repete em vários testes, corrija a regra superior que criou o bug e regenere apenas os arquivos afetados por essa regra
-
Quando não há suíte de testes
- Mike usou Claude para gerar um pequeno script que executava 7 cenários reais no código Python original e no novo port, comparando resultados
- Para cada cenário com falha, designou um agente de correção separado e iterou até todos os 7 passarem
- Claude também projetou uma suíte própria de testes end-to-end e a executou autonomamente durante a noite
- O ciclo de corrigir erros e executar novamente se repetiu por quatro noites
- A lista de cenários previamente escrita também encontrou pequenos problemas de usabilidade difíceis de prever
- Mesmo sem testes existentes, Claude pode criar um juiz usando a base de código original como resposta correta
Princípios operacionais observados em execuções repetidas
- Em vez de seguir o guia literalmente, planeje a migração com Claude conforme as características do projeto antes de começar
- Deixe agentes de correção tratarem falhas individuais, enquanto humanos focam em padrões de erro recorrentes
- Estruture revisão de forma adversarial e validação de forma mecânica
- Revisão adversarial é útil em trabalhos longos e pode valer o consumo adicional de tokens
- Use scripts como compilador, diff e suíte de testes como juízes finais
- Não use o maior modelo em todas as tarefas
- Modelos menores são úteis para paralelizar implementação em massa
- O maior modelo deve se concentrar em revisão e na escrita de regras que outros agentes seguirão
- O tempo humano deve ser investido antecipadamente no rulebook e nos testes de estresse; o restante é, em grande parte, trabalho de esvaziar filas
- O estado de conclusão deve ser mecanicamente determinável, como “o arquivo de saída existe em disco”, e a fila deve permitir retomada
Resultados e limitações da migração do Bun
- O port Rust do Bun está rodando em produção, mas cerca de 4% do código Rust fica dentro de blocos
unsafe- A maior parte são operações de ponteiro de uma linha nas fronteiras C/C++
- Todos os vazamentos de memória detectáveis por ferramentas foram corrigidos
- Em um benchmark que repetia o build 2.000 vezes, o uso de memória caiu de 6.745 MB para 609 MB
- O tamanho dos binários Linux e Windows diminuiu 19%
- Otimizações entre linguagens melhoraram em 2–5% o desempenho de workloads reais, como serviços HTTP,
next buildetsc - Em migrações grandes, é preciso revisar os resultados e padrões iterativos criados pelo loop, mais do que cada linha de código gerada
Materiais relacionados
- Migration starter kit: template que generaliza o procedimento do texto; os dois ports reais não foram executados com esse kit
- Code-modernization plugin: plugin para modernização de legado e upgrades de frameworks, não para ports de linguagem
- Dynamic workflows in Claude Code: introdução aos workflows dinâmicos no Claude Code
Ainda não há comentários.