Ir para o conteúdo
14 de ago. de 2026·8 min de leitura

Um plano de migração de sistemas legados antes do código

Crie um plano de migração de sistemas legados com evidências, ordem de extração, testes de paridade e decisões de engenharia.

Um plano de migração de sistemas legados antes do código

Uma migração de legado fracassa muito antes de o código gerado parecer errado. Ela fracassa quando a equipe pede a um modelo, em um único prompt, que deduza o sistema, escolha uma nova arquitetura, preserve comportamentos ocultos, programe o trabalho e julgue o próprio resultado. Esse prompt pode produzir um repositório impressionante. Ele não consegue produzir uma cadeia defensável de decisões.

O plano precisa existir antes do primeiro arquivo na linguagem de destino. Refiro-me a algo maior do que um backlog com caixas chamadas "converter faturamento" e "migrar relatórios". Um plano útil identifica as evidências de cada comportamento, a ordem em que a equipe vai extraí-las, a fronteira de destino que vai substituí-lo e o teste que autoriza a próxima mudança. Se um desses campos estiver vazio, a geração deve esperar.

Essa distinção importa porque um modelo de linguagem gera a partir do contexto que recebe, enquanto um engenheiro é responsável por encontrar o contexto que ninguém pensou em incluir. Sistemas antigos escondem regras no controle de jobs, nos gatilhos do banco de dados, nos hábitos dos operadores, nos formatos de arquivos, nas configurações de impressora, nos scripts de repetição e nas exceções de fechamento mensal. O trabalho difícil é decidir o que conta como comportamento e provar que o substituto ainda o mantém.

Inventarie o comportamento antes dos arquivos

Um inventário de migração precisa descrever comportamentos observáveis, não apenas arquivos de origem e contagens de linguagens. Listas de arquivos ajudam a estimar o trabalho de análise, mas dizem muito pouco sobre o contrato do qual usuários e sistemas conectados dependem. Uma etapa JCL de doze linhas pode decidir se o arquivo de liquidação de ontem será reproduzido. Um módulo grande de relatórios pode produzir uma saída que ninguém lê.

Comece na fronteira do sistema. Registre todas as entradas, saídas, eventos agendados, ações de operadores, chamadas externas, armazenamentos persistentes e sinais de falha. Para cada item, capture um exemplo real e identifique quem pode dizer se está correto. Isso cria uma superfície comportamental que pode ser testada. Só depois mapeie unidades de código-fonte nessa superfície.

Uso quatro classes de evidência porque as equipes costumam misturá-las:

  • Evidência executada: solicitações de produção, entradas em lote, mudanças no banco de dados, arquivos, mensagens e saídas que o sistema atual realmente processou.
  • Comportamento declarado: manuais, acordos de interface, copybooks, esquemas, textos de ajuda e runbooks que dizem o que deveria acontecer.
  • Caminhos implementados: ramificações, consultas, jobs, gatilhos e tratadores de erro presentes no código.
  • Prática operacional: horários, correções manuais, pontos de reinício e exceções que os operadores aplicam fora do programa.

Essas classes podem discordar. A discordância é uma descoberta, não um incômodo a ser disfarçado. Se o manual diz que um campo é obrigatório, mas o tráfego registrado contém valores vazios, o plano de migração precisa decidir se vence a compatibilidade ou a regra escrita. Uma única geração costuma escolher o artefato que parece ter mais autoridade no contexto. Um engenheiro registra o conflito, encontra o responsável e transforma a decisão em teste.

Monte uma tabela de fronteiras antes de escrever o código de destino. Uma versão pequena poderia ser assim:

FronteiraEvidênciaResponsávelRegra de compatibilidadeVerificação
Arquivo noturno de contasTrês arquivos aceitos e um rejeitadoLíder de operaçõesPreservar larguras fixas e código de rejeição 17Reproduzir os quatro arquivos
Cálculo de impostosSolicitações gravadas mais tabela de alíquotas atualResponsável pelos sistemas financeirosIgualar total arredondado e campos de auditoriaComparar resposta e linhas do registro
Impressão de extratosAmostras de spool e configuração da impressoraLíder de atendimento ao clientePreservar quebras de página e ordemRenderizar e inspecionar casos identificados

A tabela expõe cedo a ausência de evidências. Ela também evita o erro comum de tratar um esquema de banco de dados como todo o modelo do domínio. Uma coluna pode aceitar nulo porque uma importação com falha estaciona dados parciais ali, enquanto toda transação normal exige um valor. O formato do esquema e o significado para o negócio são fatos diferentes.

O registro de migração é o plano de verdade

Um plano útil de migração de sistemas legados é um registro executável de afirmações, dependências e portões. Ele deve permitir que outro engenheiro responda a cinco perguntas sobre qualquer item de trabalho: o que estamos mudando, quais evidências definem o comportamento atual, o que deve existir antes, como vamos comparar antigo e novo e quem aceita uma diferença deliberada.

Um quadro de tickets sozinho não faz isso. Os tickets mudam, as notas de aceitação viram prosa e as dependências ficam na memória. Mantenha um registro legível por máquina no repositório e faça as revisões referenciarem seus identificadores. O formato pode ser YAML, JSON ou uma tabela de banco de dados. O importante é que a falta de comprovação continue visível.

- id: CALC-014
  boundary: POST /interest/accrue
  evidence:
    traffic_set: traffic/interest/month_end_2025_01.ndjson
    state_snapshot: snapshots/ledger_before.sql.zst
  depends_on: [DATA-006, CLOCK-002]
  target: services/interest/accrual.go
  invariants:
    - response.status == legacy.response.status
    - ledger.delta_cents == legacy.ledger.delta_cents
    - audit.event_type == legacy.audit.event_type
  allowed_differences:
    - field: response.request_id
      rule: compare_presence_only
      approved_by: architecture-review-27
  gate: parity/CALC-014

Esse fragmento evita três falhas. Ele impede que um desenvolvedor teste o cálculo sem o estado que o determina. Transforma identificadores de solicitação não determinísticos em uma regra explícita de comparação, em vez de uma lista crescente de itens ignorados. Também vincula a aprovação a uma diferença específica, para que ninguém amplie a exceção em silêncio mais tarde.

O registro deve separar descobertas de decisões. "O serviço legado retorna 200 para uma solicitação duplicada" é uma observação. "O substituto preservará essa resposta" é uma decisão de compatibilidade. "Deveríamos retornar 409" é uma preferência de projeto. Misturar essas frases permite que uma limpeza desejável se disfarce de migração fiel.

Não coloque percentuais como "módulo 80% migrado" no registro. Percentuais de progresso escondem o comportamento que falta. Conte portões fechados contra fronteiras nomeadas. Dez pequenas variantes de relatório não compensam um único caminho de lançamento sem verificação, e o plano deve deixar isso claro.

O plano também precisa de condições de parada. A geração para quando faltam evidências, uma dependência não tem um destino verificado, a comparação produz uma diferença sem explicação ou um responsável não aprovou uma mudança deliberada. Sem condições de parada, a pressão do prazo transforma cada resultado vermelho em um ticket futuro de limpeza.

Extraia das fronteiras para o centro

A ordem mais segura de extração começa nas fronteiras observáveis, passa pela semântica dos dados e pela orquestração e só depois chega aos algoritmos internos. Essa ordem dá a cada geração posterior um contrato e uma forma de medir sua saída. Começar pelo módulo mais independente parece eficiente, mas costuma criar uma ilha cujas interfaces foram adivinhadas.

Primeiro, capture entradas e saídas em pontos de ligação estáveis. Para um serviço online, isso pode incluir corpos de solicitação, corpos de resposta, cabeçalhos, efeitos no banco de dados e mensagens emitidas. Para um sistema em lote, inclui gerações de entrada, cartões de controle, códigos de retorno, saída de spool, arquivos criados e comportamento de reinício. Para um aplicativo de desktop, inclua ações do usuário, arquivos locais, relatórios, estado do registro ou da configuração e chamadas a bancos de dados compartilhados.

Depois, extraia o significado dos dados. Mapeie identificadores, unidades, codificações, regras de nulo, escala decimal, regras de datas e ciclos de vida dos registros. Ainda não normalize. Um campo decimal compactado, um código preenchido com espaços e um timestamp na hora local podem parecer feios, mas cada um pode carregar comportamento. Registre como cada valor entra no sistema e onde é observado antes de escolher uma representação mais limpa.

Em terceiro lugar, reconstrua a orquestração. Sistemas antigos costumam colocar a ordem de negócio fora dos módulos de negócio. O JCL define qual programa é executado após um código de retorno. Scripts CL trocam bibliotecas. Um wrapper de shell repete um comando, mas não o próximo. Um agendador fornece uma data de negócio diferente do relógio da máquina. Se a geração vê os programas chamados sem essa orquestração, ela constrói funções individualmente plausíveis na sequência errada.

Em quarto lugar, identifique transições de estado e invariantes. Descreva uma transação como estado anterior, estímulo, estado posterior e efeitos emitidos. Isso é mais preciso do que traduzir procedimentos um por um. Também revela tratamento de duplicatas, commits parciais, ações compensatórias e pontos de recuperação.

Somente depois que essas camadas existirem a equipe deve gerar implementações internas e arquitetura de destino. As fronteiras dos serviços de destino devem acompanhar a responsabilidade, as necessidades de consistência e os padrões de mudança. Elas não devem copiar a árvore de diretórios da origem. Uma tradução módulo a módulo preserva acoplamento acidental e chama isso de modernização.

Há uma exceção prática. Às vezes é necessário criar cedo um analisador ou emulador fino para ler evidências presas em um formato proprietário. Construa-o como ferramenta de extração, marque-o como descartável e teste-o com amostras conhecidas. Não deixe que esse componente conveniente se torne silenciosamente a arquitetura de produção.

Separe evidência, interpretação e decisões

Engenheiros precisam de registros separados para o que o sistema fez, o que eles acham que isso significa e o que o projeto escolhe preservar. Um prompt longo colapsa os três em uma prosa fluente. Depois disso, o leitor não consegue saber se um requisito gerado veio do tráfego, do código-fonte ou de uma inferência.

Use um registro de afirmação para cada comportamento que vai controlar a implementação. Ele não precisa de cerimônia. Precisa de procedência e status.

{
  "claim_id": "BATCH-031",
  "statement": "A rerun skips records already posted for the same business date",
  "kind": "observed",
  "evidence": ["run-884/input.dat", "run-884/ledger-after.csv", "ops-runbook-4.2"],
  "confidence": "confirmed",
  "decision": "preserve",
  "tests": ["parity/batch_031_first_run", "parity/batch_031_rerun"]
}

A distinção entre comportamento observado e inferido parece dispensável até causar um erro de dados. Suponha que o código consulte uma tabela de "já lançado" antes de escrever. Um gerador pode inferir idempotência. Evidências de produção podem mostrar que a tabela é apagada durante um procedimento específico de recuperação, fazendo com que algumas repetições lancem novamente de propósito. O caminho do código, a prática operacional e o contrato pretendido não coincidem. O registro obriga a equipe a resolver essa divergência.

Trate comentários como afirmações, não como verdade. O mesmo vale para nomes de variáveis, ramificações mortas, documentos antigos de projeto e testes que nunca rodaram com um estado parecido com produção. Cada um pode orientar a investigação. Nenhum deve superar evidências executadas sem uma decisão identificada.

Rótulos de confiança precisam ter significado operacional. "Confirmado" pode exigir dois tipos independentes de evidência e a revisão de um responsável. "Provisório" pode permitir trabalho no analisador, mas bloquear a implementação de destino. "Desconhecido" deve criar uma tarefa de extração. Se os rótulos apenas comunicarem uma impressão, vão ceder sob pressão de prazo.

Mantenha melhorias deliberadas em um registro de mudanças ligado à afirmação de compatibilidade. Os exemplos incluem rejeitar uma data inválida aceita pelo sistema antigo, substituir um método fraco de autenticação ou mudar o layout de um relatório. Teste separadamente o caminho preservado e o novo comportamento. Caso contrário, uma falha de paridade e uma melhoria intencional tornam-se indistinguíveis, o que dificulta revisão e rollback.

Toda geração termina em um portão de paridade

Troque estrutura por responsabilidade
A reescrita usa serviços Go, núcleos Rust, clientes TypeScript e Postgres onde cada um faz sentido.

Cada etapa de geração deve terminar com um portão de paridade que compare efeitos observáveis com o sistema atual. Revisão de código e testes unitários na linguagem de destino são necessários, mas não provam compatibilidade. Eles mostram se o novo código é razoável internamente, não se ele se comporta como o sistema que as pessoas usam.

Um ciclo útil tem cinco movimentos:

  1. Selecione um item do registro cujas dependências já tenham passado.
  2. Reúna apenas seu contexto aprovado: afirmações, esquemas, amostras de evidência, restrições de destino e diferenças permitidas.
  3. Gere ou revise a menor parte de destino capaz de satisfazer a fronteira.
  4. Reproduza o mesmo estímulo nos sistemas antigo e novo a partir de estados equivalentes.
  5. Classifique cada diferença e então aprove, revise, encaminhe ou altere o registro de decisão.

Estado equivalente merece atenção. Se a execução legada começa com trinta anos de histórico de clientes e o substituto começa com fixtures feitas à mão, respostas iguais provam pouco. Faça um snapshot do estado relevante, mascare-o quando necessário, preserve relações referenciais e documente qualquer estado que não possa reproduzir. Para sistemas que não podem executar duas vezes contra o mesmo estado, clone o estado ou registre efeitos em um ponto de ligação.

A comparação deve ser semântica, não um diff de texto bruto. Normalize campos somente por motivos registrados no ledger. Você pode comparar timestamps dentro de uma tolerância aprovada, ignorar o valor de identificadores gerados enquanto exige sua presença, ordenar canonicamente objetos JSON ou comparar um PDF pelo texto extraído e pela geometria da página. Nunca adicione uma regra global de exclusão apenas para transformar uma build vermelha em verde.

Um relatório de paridade deve mostrar o item, conjunto de evidências, resultado antigo, resultado novo, regras de normalização, diferenças e encaminhamento. Este formato compacto basta para automação e revisão:

gate=CALC-014 evidence=month_end_2025_01
cases=184 matched=183 different=1 errored=0
difference[1].path=ledger.entries[2].amount_cents
difference[1].legacy=1250
difference[1].target=1249
difference[1].rule=exact
status=FAIL

A diferença de um centavo não está "perto o suficiente". Ela aponta para ordem de arredondamento, representação decimal ou incompatibilidade de estado. Um engenheiro a acompanha pelos valores intermediários, verifica a semântica aritmética da origem e adiciona o menor teste que isole a causa. Gerar novamente o módulo inteiro com uma instrução mais forte costuma alterar comportamentos sem relação e destruir o rastro de evidências.

Execute os portões continuamente, não em uma fase final de aceitação. Uma fronteira aprovada torna-se uma restrição para o trabalho posterior. Quando um mapeamento de dados compartilhado muda, o grafo de dependências identifica quais portões devem rodar de novo. Por isso o registro e o harness pertencem um ao outro: um diz o que pode mudar e o outro mostra o que mudou.

Um prompt gigante falha de maneiras previsíveis

Não apenas traduza a arquitetura
A CodeHero moderniza fronteiras de serviços em vez de copiar a árvore de módulos em nova sintaxe.

Um único prompt de migração falha porque suas tarefas entram em conflito e seu contexto não tem mecanismo de aplicação. Mais contexto pode melhorar a lembrança, mas não cria procedência de evidências, ordem de dependências, julgamento independente nem um portão de teste durável. O repositório gerado pode ser coerente e ainda estar errado em toda fronteira que ficou fora do prompt.

Considere uma cadeia noturna de faturamento. O agendador fornece uma data de negócio. Uma etapa JCL ordena transações com uma colação específica de localidade. Um programa lança linhas válidas e grava rejeições. Um código de retorno decide se os extratos serão executados. A operação pode reiniciar após o lançamento sem repeti-lo. Os módulos de origem contêm partes desse comportamento, mas nenhum módulo possui o contrato completo.

Um prompt grande pede uma reescrita em Go e inclui programas, copybooks, arquivos de amostra e uma frase dizendo "preserve o comportamento". A saída usa a data da máquina, ordena strings com o padrão do runtime de destino, envolve o lote em uma única transação de banco de dados e trata qualquer rejeição como erro fatal. Cada escolha seria defensável em um sistema novo. Juntas, elas quebram o fechamento mensal.

O primeiro arquivo de teste contém registros comuns, por isso os dois sistemas calculam os mesmos totais. A equipe comemora. O primeiro reinício repete lançamentos porque o destino não tem um checkpoint correspondente à etapa legada. Um arquivo com sufixo de conta em branco é ordenado de maneira diferente, alterando o agrupamento. Uma única linha inválida agora desfaz trabalho válido que a cadeia antiga teria lançado. Nenhum desses erros parece um defeito de sintaxe ou uma escolha de projeto obviamente ruim.

Uma extração planejada os teria capturado em ordem. A captura da fronteira registra a data de negócio e os códigos de retorno. A análise de dados registra a colação e o preenchimento com espaços. A análise da orquestração registra os pontos de commit e reinício. A reprodução inclui execuções comuns, rejeitadas e retomadas. A arquitetura de destino ainda pode melhorar a implementação, mas não pode apagar esses contratos por acidente.

A resposta popular é tornar o prompt maior. Isso ajuda apenas quando a omissão é o único problema. Torna a resolução de conflitos mais difícil, esconde qual evidência sustentou cada instrução e ainda deixa o gerador julgar o próprio trabalho. Dividir o prompt entre agentes também não resolve por si só. Vários geradores precisam do mesmo registro, das mesmas regras de responsabilidade e dos mesmos portões de paridade, ou apenas distribuem mais depressa premissas sem rastreamento.

Use a geração como uma operação de implementação delimitada. Dê a ela uma parte de destino, restrições explícitas, as evidências necessárias para essa parte e a saída de paridade com falha da tentativa anterior. Depois inspecione a mudança e execute o portão fora do processo de geração. Essa divisão mantém o modelo produtivo sem dar a ele uma autoridade que não consegue assumir com responsabilidade.

O engenheiro responde pelo que não foi resolvido

Um engenheiro faz o trabalho que não pode ser reduzido a produzir código plausível: encontrar evidências ausentes, decidir qual contradição importa, escolher uma fronteira, negociar mudanças deliberadas e aceitar riscos. Essas não são lacunas temporárias que desaparecem quando os modelos aumentam. São atos de responsabilidade dentro de uma organização específica.

O engenheiro pergunta quem sofre quando dois artefatos discordam. Se o código-fonte permite um saldo negativo, mas a política financeira o proíbe, a resposta exige uma decisão de produto e conformidade, não uma síntese ponderada por probabilidade. Se os operadores dependem de um truque de reinício que o novo projeto deveria eliminar, o engenheiro precisa entender por que ele existe, projetar um caminho de recuperação mais seguro e obter concordância para quebrar a compatibilidade de propósito.

O engenheiro também controla a decomposição. Um gerador tende a seguir a estrutura que enxerga. O engenheiro pode reconhecer que seis programas formam uma fronteira única de consistência ou que um monólito contém quatro capacidades com responsáveis independentes. Esse julgamento vem da semântica de transações, das necessidades de implantação, do histórico de incidentes e das pessoas que vão manter o destino.

A revisão deve se concentrar nas afirmações e nos efeitos antes do estilo. Pergunte qual item do registro a mudança fecha, quais evidências usa, qual decisão de destino incorpora e o que diz o relatório de paridade. Um serviço perfeitamente idiomático com uma diferença de saída sem explicação continua inacabado. Um adaptador desajeitado que preserva uma fronteira difícil pode ser exatamente o componente temporário correto.

Engenheiros também precisam proteger o harness para que não vire um carimbo automático. Toda regra de normalização precisa de um motivo. Todo arquivo de referência precisa de procedência. Todo resultado esperado alterado precisa da mesma revisão que uma mudança de comportamento em produção. Se a equipe atualiza snapshots sempre que um teste falha, criou uma máquina de aprovação, não um harness de paridade.

Ainda há espaço para velocidade. Gere analisadores, adaptadores, testes, código de mapeamento e candidatos de implementação em paralelo quando as dependências do registro permitirem. Mantenha centralizadas a aquisição de evidências e os resultados dos portões. Produção paralela de código é útil; verdade paralela é uma contradição.

A arquitetura muda apenas atrás de contratos provados

Traga o monólito inteiro
Sistemas com mais de um milhão de linhas são lidos como uma base conectada, não como fragmentos de prompt.

A modernização deve mudar a arquitetura atrás de contratos preservados, não traduzir a estrutura antiga linha por linha. Quando uma fronteira tem evidências e um portão de paridade, a equipe pode substituir estado compartilhado por serviços explícitos, isolar núcleos numéricos, mover dados para Postgres ou construir um cliente TypeScript sem adivinhar se o novo formato mudou o comportamento visível.

Decida a arquitetura no menor nível em que houver evidência suficiente. Algumas decisões pertencem ao início: restrições do runtime de destino, limites de segurança, ambiente de implantação, residência dos dados e quais sistemas precisam coexistir durante a transição. Outras decisões devem esperar: divisões de serviços, posição de caches, fronteiras assíncronas e limpeza de esquema costumam depender do comportamento descoberto durante a extração.

Evite dois extremos. Congelar todos os detalhes do destino antes da descoberta produz um plano elegante para um sistema imaginado. Deixar cada geração inventar arquitetura produz fronteiras inconsistentes e infraestrutura duplicada. Registre decisões vinculantes, mantenha decisões adiadas visíveis e indique quais evidências vão encerrá-las.

O planejamento da transição pertence ao mesmo registro. Identifique a fonte da verdade durante a transição, a direção de sincronização, a consulta de conciliação, o ponto de rollback e a interrupção máxima aceita. Um substituto que passa em testes isolados de paridade ainda pode falhar na operação se os dois sistemas gravarem os mesmos registros ou se o rollback não puder recuperar mudanças que só existem no destino.

A CodeHero usa essa forma de trabalho quando lê uma árvore legada inteira, reescreve-a em Go, Rust, TypeScript e Postgres e verifica o comportamento com um harness de paridade contra tráfego de produção gravado. A promessa de entrega em menos de 30 dias depende de uma ordem estreita entre extração e verificação; ela não torna o plano opcional.

Antes de aprovar o primeiro arquivo de destino gerado, exija uma entrada completa do registro com evidências reais, dependências explícitas, uma regra de comparação e um responsável identificado pelas diferenças. Se a equipe não consegue produzir essa entrada, não está pronta para migrar. Está pronta apenas para gerar código que parece migrado.

Perguntas frequentes

Por que um único prompt longo não consegue migrar um sistema legado?

Um prompt longo pode gerar uma base de código aparentemente coerente, mas não consegue estabelecer quais evidências têm autoridade nem aprovar conflitos entre código, operação e políticas. Também não pode emitir um julgamento independente sobre compatibilidade ao avaliar o próprio resultado.

O que um plano de migração de sistema legado deve conter?

Para cada fronteira de comportamento, registre evidências, dependências, destino, invariantes, diferenças permitidas, portão de verificação e responsável pela decisão. O plano também deve definir condições de parada para que evidências ausentes ou diferenças sem explicação bloqueiem a geração.

O que uma equipe deve extrair primeiro do código legado?

Comece por entradas, saídas, eventos agendados, ações de operadores, efeitos persistentes e sinais de falha observáveis. Depois extraia a semântica dos dados e a orquestração antes de implementar algoritmos internos, pois essas fronteiras dão um contrato ao trabalho posterior.

Como verificar a reescrita de um sistema legado?

Reproduza o mesmo estímulo nos sistemas antigo e novo a partir de estados equivalentes e compare cada efeito observável segundo regras de normalização escritas. Classifique cada diferença como defeito, mudança aprovada, problema de evidência ou problema de ambiente.

Testes unitários bastam para uma migração de legado?

Não. Testes unitários mostram que o código de destino se comporta como seus autores esperam, enquanto testes de paridade mostram se ele corresponde ao sistema substituído. Você precisa dos dois, pois uma implementação limpa pode reproduzir fielmente uma premissa errada.

Agentes de IA podem planejar uma migração completa de código?

Agentes podem inventariar código, propor mapeamentos, gerar partes delimitadas e investigar testes com falha. Engenheiros continuam responsáveis pela qualidade das evidências, ordem das dependências, decisões de arquitetura, resolução de conflitos e aceitação de mudanças deliberadas de comportamento.

Uma modernização deve preservar todo comportamento legado?

Não, mas cada diferença deve ser intencional. Preserve o caminho antigo em um teste de paridade, registre separadamente a mudança aprovada e teste a nova regra para que uma melhoria desejada não esconda outro defeito de compatibilidade.

Como tratar campos não determinísticos em testes de paridade?

Escreva uma regra de comparação limitada, como exigir que um identificador exista sem exigir o valor exato ou permitir uma tolerância de tempo documentada. Nunca use uma lista ampla de itens ignorados, pois ela esconderá diferenças relevantes em outros pontos.

Quando a arquitetura de destino deve ser decidida?

Defina cedo as restrições rígidas e os limites de segurança e adie escolhas que dependam do comportamento descoberto. Decida divisões de serviços, limpeza de dados e fluxos assíncronos somente quando as evidências mostrarem os limites de consistência e responsabilidade que precisam ser respeitados.

Qual é o primeiro portão antes de gerar código de migração?

Complete uma entrada do registro com evidências reais de entrada e saída, dependências conhecidas, invariantes explícitos, regras de comparação e um responsável por resultados contestados. Se o registro estiver incompleto, a equipe deve continuar a extração em vez de gerar código de produção.