Capítulo 12 — Sistemas legados: compreender antes de transformar
O problema não é apenas código antigo
Section titled “O problema não é apenas código antigo”“Legado” costuma ser usado como sinônimo de:
- framework velho;
- linguagem antiga;
- arquitetura ruim;
- falta de testes;
- dependências obsoletas.
Mas o risco real está em outra coisa:
o sistema continua carregando comportamento que alguém depende e que ninguém compreende completamente.
Isso muda a pergunta.
Não é:
“como reescrever?”
É:
“o que não podemos quebrar enquanto aprendemos?”
Antes de mudar, construa observabilidade comportamental
Section titled “Antes de mudar, construa observabilidade comportamental”Fowler é direto sobre a relação entre refactoring e testes: quando o código existente não é self-testing, o primeiro passo é criar uma base de testes antes de refatorar. 1
A função desses testes não é provar que o design atual é bom.
É detectar quando a transformação altera comportamento que ainda precisa ser preservado.
No método deste livro, chamamos essa primeira camada de characterization cage.
Ela cerca o comportamento atual antes da cirurgia.
Characterization test não é aprovação do legado
Section titled “Characterization test não é aprovação do legado”Um teste de caracterização pode registrar um comportamento estranho.
Isso não significa que o comportamento esteja correto do ponto de vista do negócio.
Significa apenas:
“é isso que o sistema faz hoje.”
Depois precisamos classificar:
- comportamento intencional;
- bug conhecido;
- comportamento desconhecido;
- comportamento dependido por terceiros;
- comportamento que pode ser removido.
Essa distinção evita um erro comum: congelar para sempre todas as anomalias do sistema só porque foram capturadas em testes.
Legacy Map
Section titled “Legacy Map”O primeiro artefato operacional deste capítulo é o Legacy Map.
Ele descreve o sistema antes da transformação.
Estrutura mínima:
- entry points;
- módulos;
- dados;
- integrações;
- jobs;
- filas;
- eventos;
- invariantes conhecidos;
- owners;
- interfaces externas;
- pontos sem cobertura;
- risco operacional;
- áreas de alto acoplamento.
O mapa não precisa estar perfeito para ser útil.
Ele precisa deixar explícito o que sabemos e o que ainda é hipótese.
Confidence Map
Section titled “Confidence Map”Cada área do mapa recebe um nível de confiança.
Exemplo:
- alta: coberta por testes + documentação + owner;
- média: testes parciais + comportamento observado;
- baixa: sem testes, pouco uso conhecido;
- desconhecida: impacto não mapeado.
Isso é método deste livro.
A função é orientar onde o agente pode agir com maior autonomia e onde precisa primeiro investigar.
Reverse engineering como coleta de evidência
Section titled “Reverse engineering como coleta de evidência”Reverse engineering, neste método, não significa necessariamente decompilar ou reconstruir internals.
É recuperar estrutura e intenção a partir de evidências.
Fontes possíveis:
- código;
- testes existentes;
- logs;
- banco;
- schemas;
- tráfego;
- documentação;
- tickets;
- commits;
- especialistas;
- usuários;
- scripts operacionais;
- dashboards.
Evans descreve knowledge crunching como combinação de múltiplas fontes — especialistas, usuários, documentos, sistemas existentes e feedback de implementação — para transformar informação dispersa em modelo compartilhado. 2
Isso é particularmente importante em legado.
O domínio pode estar escondido em:
- ifs;
- stored procedures;
- nomes de colunas;
- flags;
- cron jobs;
- validações duplicadas;
- convenções não documentadas.
Código contém conhecimento, mas não contém todo o conhecimento
Section titled “Código contém conhecimento, mas não contém todo o conhecimento”Uma regra pode existir no código sem explicar por que existe.
Exemplo:
if amount > limit * 1.1: reject()O código mostra um limite.
Não mostra necessariamente:
- quem definiu;
- por que 10%;
- se ainda é válido;
- quais exceções existem;
- se outros sistemas dependem disso.
Por isso, reverse engineering técnico precisa encontrar o domínio.
Caso contrário, modernizamos a estrutura e perdemos a regra.
Preserve invariants, não estrutura
Section titled “Preserve invariants, não estrutura”Em modernização, a implementação pode mudar radicalmente.
O que precisa sobreviver são invariantes relevantes.
Exemplos:
- saldo nunca negativo;
- pedido não pode ser faturado duas vezes;
- identificador externo não muda;
- evento deve manter ordem;
- autorização precisa preceder execução.
O Legacy Map deve promover esses invariantes a artefatos explícitos.
Depois, testes e contracts os protegem.
Pequenos passos reduzem salto de fé
Section titled “Pequenos passos reduzem salto de fé”Beck descreve refactoring seguro como uma sequência de passos pequenos apoiados por feedback concreto, justamente para evitar mudanças baseadas em longas cadeias de raciocínio não verificadas. 3
Para agentes, essa disciplina fica ainda mais importante.
Um agente pode produzir uma grande transformação rapidamente.
Velocidade de geração não reduz o risco de:
- esquecer uma exceção;
- perder uma regra implícita;
- alterar timing;
- mudar formato;
- quebrar integração.
Por isso, o método prefere:
characterize ↓isolate ↓small change ↓test ↓inspect diff ↓repeatStructural change e behavioral change não deveriam se misturar
Section titled “Structural change e behavioral change não deveriam se misturar”Quando possível, primeiro altere estrutura preservando comportamento.
Depois altere comportamento com Spec explícita.
Exemplo:
- extrair função sem mudar resultado;
- adicionar testes;
- introduzir interface;
- só então mudar regra de negócio.
Isso torna regressões mais localizáveis.
Um seam é um ponto onde conseguimos observar ou substituir comportamento sem reescrever tudo.
No método deste livro, procuramos seams como:
- função;
- interface;
- adapter;
- HTTP boundary;
- queue;
- repository;
- CLI;
- database view;
- feature flag.
Criar seams reduz o raio da transformação.
Legacy Map + Characterization Cage
Section titled “Legacy Map + Characterization Cage”Os dois artefatos trabalham juntos.
Legacy Map
Section titled “Legacy Map”Explica onde estão as partes, relações e riscos.
Characterization Cage
Section titled “Characterization Cage”Transforma comportamento observado em feedback executável.
O mapa orienta onde investigar.
A cage sinaliza quando uma mudança escapou do comportamento conhecido.
Golden paths primeiro
Section titled “Golden paths primeiro”Não precisamos caracterizar o sistema inteiro antes de qualquer trabalho.
Priorize fluxos que concentram valor ou risco.
Exemplo:
- login;
- checkout;
- pagamento;
- emissão;
- sincronização;
- exportação;
- fechamento.
Depois expanda por dependência.
Isso mantém a modernização incremental.
Observability como fonte de verdade temporária
Section titled “Observability como fonte de verdade temporária”Em sistemas pouco testados, produção pode revelar comportamento que o código não torna óbvio.
Logs, traces e métricas ajudam a responder:
- quais endpoints ainda são usados;
- quais flags estão ativas;
- quais jobs realmente rodam;
- quais integrações recebem tráfego;
- quais erros são recorrentes.
Mas evidência de runtime precisa ser tratado com cuidado.
Ausência de tráfego não prova ausência de uso.
Shadow behavior
Section titled “Shadow behavior”Uma técnica possível é executar nova lógica sem substituir a antiga.
Exemplo:
current_result = legacy(input)candidate_result = new_path(input)compare(current_result, candidate_result)Sem publicar o candidate_result.
Isso permite comparar comportamento antes do cutover.
No método deste livro, isso é uma opção de método deste livro, não uma recomendação universal.
Strangler por boundary
Section titled “Strangler por boundary”Quando existe interface relativamente estável, podemos mover comportamento atrás dela aos poucos.
A sequência pode ser:
- capturar contrato;
- estabilizar adapter;
- caracterizar resposta;
- implementar caminho novo;
- comparar;
- rotear pequena parcela;
- observar;
- expandir.
O ponto central é evitar big-bang.
Modernização não é migração de tecnologia
Section titled “Modernização não é migração de tecnologia”Trocar:
- linguagem;
- framework;
- banco;
- cloud;
sem recuperar regras não resolve o problema.
Podemos apenas recriar o legado em stack nova.
A mudança técnica precisa ser guiada por:
- invariants;
- domain model;
- acceptance criteria;
- evidência.
Compression maps
Section titled “Compression maps”Quando o sistema é grande demais para caber em uma única janela, precisamos comprimir.
O método usa um Legacy Skeleton Map como método deste livro:
entry points→ critical flows→ domain rules→ state stores→ external contracts→ risk zones→ evidência pointersEsse mapa é navegação, não verdade completa.
Compressão precisa manter provenance
Section titled “Compressão precisa manter provenance”Um resumo sem ponteiro para a fonte vira opinião.
Cada item importante do skeleton map deve apontar para:
- arquivo;
- teste;
- query;
- log;
- schema;
- ticket;
- entrevista;
- commit.
Assim, o agente pode expandir contexto sob demanda.
Hipótese antes de alteração
Section titled “Hipótese antes de alteração”Em legado, muitas interpretações começam como hipótese.
Exemplo:
Hipótese: este job é responsável por reconciliar pagamentos atrasados.
Antes de remover:
- procurar caller;
- observar scheduler;
- consultar logs;
- revisar banco;
- perguntar owner;
- capturar teste.
O método diferencia:
- known;
- inferred;
- unknown.
Isso impede certeza artificial.
Refactoring plan
Section titled “Refactoring plan”Uma mudança em legado deve registrar:
Behavior to preserve
Section titled “Behavior to preserve”Quais resultados atuais precisam sobreviver?
Behavior to change
Section titled “Behavior to change”Qual mudança é intencional?
Characterization evidência
Section titled “Characterization evidência”Quais testes observam o estado atual?
Onde isolaremos a mudança?
O que pode quebrar fora do módulo?
Rollback
Section titled “Rollback”Como voltar?
Cutover
Section titled “Cutover”Quando o caminho novo assume?
Agentes devem investigar antes de editar
Section titled “Agentes devem investigar antes de editar”Um anti-pattern é:
- abrir repositório;
- localizar arquivo parecido;
- reescrever;
- rodar testes existentes.
Em legado, testes existentes podem não representar o comportamento crítico.
O agente precisa primeiro descobrir:
- quem chama;
- quem consome;
- quais dados toca;
- quais regras carrega;
- quais efeitos externos produz.
Falha típica: “os testes passaram”
Section titled “Falha típica: “os testes passaram””Em sistema com cobertura parcial, verde não significa segurança.
Pode significar apenas que a área afetada não está sendo observada.
Por isso, antes de confiar no gate, pergunte:
que comportamento este teste realmente cobre?
Falha típica: limpeza estética antes de entendimento
Section titled “Falha típica: limpeza estética antes de entendimento”Renomear, mover e reorganizar pode parecer inofensivo.
Mas em legado:
- reflection;
- scripts;
- imports dinâmicos;
- configs;
- paths;
- deploy tooling;
podem depender da estrutura atual.
Primeiro capture dependências.
Depois limpe.
Falha típica: rewrite total
Section titled “Falha típica: rewrite total”Rewrite total promete simplicidade.
Mas também remove feedback incremental.
Durante meses, dois sistemas passam a divergir:
- regras novas entram no legado;
- o rewrite fica atrás;
- comportamento não documentado reaparece tarde.
Quando possível, prefira transformação por fatias verificáveis.
Falha típica: domínio reconstruído só pelo código
Section titled “Falha típica: domínio reconstruído só pelo código”Código representa implementação.
Domínio representa significado.
Reconstruir regra apenas por nomes técnicos pode perder exceções e práticas humanas.
Evans enfatiza colaboração entre desenvolvedores e especialistas para transformar informação em conhecimento de domínio compartilhado. 2
Human checkpoints
Section titled “Human checkpoints”Há pontos onde a decisão não deve ser automatizada:
- decidir se comportamento estranho é bug ou contrato;
- escolher regra canônica quando fontes divergem;
- aprovar remoção de integração;
- confirmar invariant de negócio;
- autorizar cutover irreversível.
O agente pode preparar evidência.
A decisão continua explícita.
Um protocolo de compreensão
Section titled “Um protocolo de compreensão”O método deste livro usa a seguinte sequência para legado:
- Map
- Observe
- Characterize
- Model
- Isolate
- Change
- Verify
- Compare
- Cut over
- Learn
Essa sequência é método deste livro.
Ela não afirma que toda modernização precisa seguir exatamente dez passos.
Ela força a compreensão a aparecer antes da transformação.
Da compreensão à arquitetura de evidência
Section titled “Da compreensão à arquitetura de evidência”Legacy comprehension deixa uma conclusão importante: testes, logs, contracts, runtime observations e maps não são acessórios dispersos. Juntos, eles formam a base para decidir se uma mudança pode ser aceita.
O próximo capítulo transforma essa prática em arquitetura deliberada.
A pergunta deixa de ser apenas “temos testes?” e passa a ser:
“qual conjunto de evidências cobre os riscos desta mudança, e o que ainda permanece sem prova?”
É a passagem de compreensão local para Evidence Architecture.
Fronteira de confiança
Section titled “Fronteira de confiança”Em legado, cada interpretação é uma trust boundary. Código antigo, documentação, logs, tickets e memória humana podem divergir. Nenhuma fonte deve ganhar autoridade automática; conflitos precisam ser registrados e resolvidos por evidência e ownership.
Evidência exigida
Section titled “Evidência exigida”Antes de transformação estrutural, o sistema precisa de evidência suficiente do comportamento relevante: characterization tests, contracts, runtime observations, schemas e invariants. O Legacy Map deve distinguir fato observado, inferência e desconhecido.
Rollback e recuperação
Section titled “Rollback e recuperação”Mudanças em legado devem ser pequenas e reversíveis sempre que possível. Cada etapa deve permitir retorno ao estado anterior; cutovers precisam de estratégia explícita, especialmente quando envolvem dados, protocolos ou integrações externas.
Proveniência das fontes
Section titled “Proveniência das fontes”Rastreabilidade deste capítulo:
- CLM-044 — Fowler, necessidade de tornar código existente self-testing antes de refactoring;
- CLM-045 — Beck, refactoring em passos pequenos com feedback concreto;
- CLM-046 — Evans, knowledge crunching a partir de especialistas, usuários, documentos, sistemas legados e feedback de implementação.
Legacy Map, Confidence Map, Characterization Cage, Legacy Skeleton Map e protocolo Map→Observe→Characterize→Model→Isolate→Change→Verify→Compare→Cut over→Learn são método deste livro.
Os comparáveis de Chris Ford e Jayaratchagan permanecem fonte limitada e não foram usados como autoridade factual neste texto.
Fontes de acesso limitado não foram usadas como autoridade factual neste capítulo.
Recursos complementares
Section titled “Recursos complementares”Recursos relacionados:
- Legacy Map template;
- Characterization Cage checklist;
- Confidence Map;
- Legacy Skeleton Map;
- modernization plan;
- cutover checklist;
- futura comparação legacy vs candidate behavior;
- futura visualização de provenance e confidence por subsystem.
Footnotes
Section titled “Footnotes”-
Martin Fowler, Refactoring (1999), Chapter 4. Building Tests, pp. 74–75. Rastreabilidade interna: CLM-044. ↩
-
Eric Evans, Domain-Driven Design (2004), Knowledge Crunching, pp. 47–48. Rastreabilidade interna: CLM-046. ↩ ↩2
-
Kent Beck, Test-Driven Development: By Example (2002), Chapter 31: Refactoring, pp. 202–203. Rastreabilidade interna: CLM-045. ↩