Pular para o conteúdo
Voltar para o acervo
Engenharia7 min de leituraPasso 20 de 22

Legado: caracterizar, costurar e estrangular

Três técnicas que permitem mexer com segurança num sistema que você não escreveu, não entende, e não pode parar.

A definição do Michael Feathers tem cinco palavras: código legado é código sem testes.

Não é código antigo. Código de 2011 com uma boa suíte é mais fácil de mexer do que o que você escreveu ontem sem nenhuma.

Essa definição é boa porque é acionável. "Esse código é ruim" é julgamento, gera briga e não aponta nada. "Esse módulo não tem rede de segurança" é fato verificável e diz o que fazer a seguir.

E porque tira a culpa da conversa. Quem escreveu aquilo provavelmente estava sob prazo impossível, com metade da informação. O problema não é a pessoa: é a ausência de retorno rápido quando alguém altera uma linha.

O ciclo vicioso

Sem teste você não refatora com segurança. Sem refatorar você não consegue testar, porque o código não tem por onde entrar.

Três técnicas quebram esse ciclo, e elas se usam nessa ordem.

Técnica 1: teste de caracterização

O truque é lindo de tão simples.

1. Escreva um teste esperando qualquer valor. Chute. assert resultado == 0

2. Ele falha. E a mensagem de falha te diz o comportamento real de hoje: "esperado 0, recebido 1847.32".

3. Congele esse valor no teste.

Pronto. Você travou o comportamento sem precisar entender a regra.

Repare no que aconteceu: o teste não verifica se o código está certo. Ele registra o que o código faz hoje, para você perceber se isso mudar amanhã. Que é exatamente o que você precisa antes de refatorar.

E se o comportamento atual estiver errado? Então o teste documenta o bug, e a correção passa a ser uma decisão explícita, com o teste sendo atualizado de propósito, em vez de uma mudança acidental que ninguém notou.

Um cuidado: deixe claro no nome que aquele teste descreve o legado, não o desejado. caracteriza_calculoDeJurosAtual em vez de deveCalcularJurosCorretamente.

O golden master

A versão industrial da mesma ideia.

Pegue mil entradas reais, anonimizadas. Rode pelo sistema atual. Salve todas as saídas num arquivo. Versione.

Em uma tarde, um módulo sem nenhum teste ganha uma rede de segurança densa, sem que ninguém precise ler a regra de negócio.

Depois de refatorar, cada diferença que aparecer é uma pergunta: "mudei de propósito, ou quebrei?"

Essa pergunta feita durante a refatoração, e não seis meses depois num chamado de suporte, é o que separa modernizar um sistema legado com método de mexer nele torcendo para dar certo.

Os dois cuidados que evitam frustração:

Normalize antes de congelar. Data de hoje, id aleatório, ordem instável de coleção e endereço de memória vão fazer o teste falhar por motivo errado. Substitua por valores fixos.

Prefira recortes com significado a um dump de mil linhas. Um arquivo gigante que ninguém sabe ler quando quebra é quase tão ruim quanto não ter teste.

Na minha experiência, é a técnica de maior alavancagem que existe em código legado: pouquíssimo código escrito, enorme aumento de segurança.

Técnica 2: costuras

Costura (seam, no original) é um ponto onde você consegue trocar o comportamento sem editar naquele lugar.

Código legado normalmente não tem nenhuma. Ele cria a conexão de banco, lê o relógio e instancia o cliente HTTP dentro do próprio método. Para testar, você precisaria subir meio sistema.

Os tipos de costura:

Por parâmetro. O relógio, o cliente HTTP e o gerador aleatório entram por argumento em vez de serem criados lá dentro.

Por subclasse. Extraia o trecho difícil para um método protegido e sobrescreva no teste. É feio e funciona como passo intermediário.

Por ligação. Trocar a implementação na configuração, no build ou no injetor.

Como abrir a primeira costura com segurança: use apenas refatorações comprovadamente seguras (extrair método, renomear, mover parâmetro) e de preferência pelo refatorador automático do editor, que erra menos que a mão e não pula nenhuma chamada.

Só depois de abrir a costura você escreve o teste de verdade.

E a primeira costura que eu abriria em qualquer sistema: o relógio.

Procure toda chamada direta a "agora" e troque por uma dependência injetada, com o relógio real como padrão. Nada quebra, e de repente dá para testar vencimento, expiração, juros e virada de mês sem esperar o calendário, e some boa parte dos testes instáveis.

Costuma caber num PR pequeno, e é a refatoração com melhor relação entre esforço e ganho em base legada.

Os sinais de que não há costura, para você reconhecer em revisão: new de cliente HTTP dentro do método; chamada estática a relógio, ambiente ou singleton; método de oitocentas linhas que só recebe um id; herança profunda em que o construtor da classe-mãe já abre conexão.

Técnica 3: Strangler Fig

Para substituir um sistema inteiro sem parar nada. O nome vem da figueira-mata-pau, que cresce em volta da árvore hospedeira até substituí-la.

1. Ponha um interceptor. Todo tráfego passa por um ponto que você controla: um proxy, um gateway, uma camada de roteamento.

2. Recrie uma fatia. Escolha a de menor risco e reimplemente só ela.

3. Desvie o tráfego. Aquela rota passa a ir para o novo. As outras continuam no velho.

4. Repita. Quando a última rota migrar, o velho morre de inanição.

Por que é melhor que reescrever do zero:

Entrega valor desde a primeira fatia, e não daqui a dezoito meses. Cada passo é reversível: basta apontar a rota de volta. O risco fica dividido. E sobrevive a mudança de prioridade: se o projeto parar no meio, o que já migrou continua de pé.

A reescrita completa, por outro lado, persegue um alvo que anda: enquanto você reescreve, o sistema antigo continua ganhando funcionalidade. Joel Spolsky chamou isso de "o pior erro estratégico" que uma empresa de software pode cometer, e não é exagero.

O que dá errado:

O banco compartilhado. Os dois sistemas escrevendo na mesma tabela. É aqui que a maioria trava. Os caminhos usuais: o novo lê do banco antigo por um tempo; o antigo publica eventos via CDC que o novo consome; ou você separa por fatia de clientes, migrando dado junto com tráfego.

Nunca chegar ao fim. Sobram as três rotas mais difíceis, e você mantém dois sistemas para sempre, com o dobro do custo de manutenção.

Contra isso: publique um número visível ("78% do tráfego já está no novo") num painel, com data de desligamento acordada. Sem isso, a migração morre na fatia dos 60%, que é onde ficam as rotas chatas que ninguém quer pegar.

Como escolher a primeira fatia: não comece pelo núcleo, nem pela parte mais irrelevante. Escolha algo com pouca dependência de dado, tráfego real suficiente para provar o caminho, e reversão fácil. Leitura de catálogo e relatório costumam ser bons candidatos.

O objetivo da primeira fatia não é entregar muito valor: é montar o interceptor, o desvio, a observabilidade e o processo de reversão. Da segunda em diante, tudo isso já existe.

  1. 1CaracterizarCongele o comportamento de hoje sem entender a regra. O golden master faz isso em massa, numa tarde.
  2. 2CosturarAbra um ponto onde dá para trocar o comportamento sem editar ali. Comece pelo relógio.
  3. 3EstrangularInterceptor na frente, uma fatia de cada vez, com número visível e data de desligamento.
Nessa ordem: a rede de segurança primeiro, o ponto de troca depois, a substituição por último.

Como vender isso para o negócio

A pergunta que sempre vem.

Não peça "uma sprint de refatoração". Esse pedido perde para qualquer funcionalidade, e com razão.

Embuta o custo na estimativa da tarefa que já estava priorizada, e fale em prazo de entrega, não em elegância:

"Mexer aqui hoje leva três dias porque não há teste. Com dois dias de preparo, as próximas cinco tarefas nessa área passam a levar meio dia cada."

Refatoração vendida como investimento com retorno calculável é aprovada. Vendida como gosto pessoal, nunca é.

Por onde começar amanhã

Cruze duas listas que você já tem: quais arquivos mudam com mais frequência (git log) e quais concentram mais bugs.

O que aparece nas duas é onde a falta de teste dói agora. Comece por aí, com um golden master.

É o inverso do impulso natural, que é começar pelo módulo mais feio, e o módulo mais feio pode estar parado há três anos sem incomodar ninguém.

Leia depois disto

Fala comigo

Dúvida sobre o artigo? Me chama no WhatsApp

Sem formulário e sem lista de e-mail. Se você discorda de alguma coisa que eu escrevi, ou quer contar como resolveu aí, a conversa é direta comigo.

Abrir conversa