ArquiteturaIntermediário8 min de leitura5 seções2004, Data aproximada

Strangler Fig

A alternativa à reescrita big-bang, que quase sempre fracassa. Em vez de parar tudo e virar a chave num dia, coloca-se um roteador na frente do sistema antigo e migra-se pedaço a pedaço: cada rota reescrita passa a ir ao novo, o legado encolhe, e o usuário nunca vê uma virada de chave. O nome vem da figueira que cresce sobre a árvore hospedeira até substituí-la — e o custo é conviver com os dois sistemas durante a transição.

Em 10 segundos

Strangler Fig substitui um legado sem a reescrita big-bang: um roteador na frente decide, rota a rota, o que já foi migrado (vai ao novo) e o que ainda não (vai ao velho). O novo cresce em volta do antigo até assumir tudo, e então o legado é desligado — sem dia de virada, em passos pequenos e reversíveis. O custo é conviver com os dois sistemas e sincronizar os dados que ambos tocam durante a transição.

Antes de ter nome

A estratégia de substituir um sistema aos poucos, por fora, em vez de num big-bang, é tão antiga quanto o medo — justificado — das reescritas totais.

  • #arquitetura
  • #migracao
  • #legado
  • #incremental
  • #refatoracao
Analogia

A figueira que dá nome ao padrão

A figueira estranguladora começa como uma muda no alto de uma árvore hospedeira e vai descendo raízes por fora do tronco dela. Aos poucos, essas raízes envolvem a árvore inteira, assumindo o trabalho de sustentar a copa. Quando a hospedeira finalmente morre e apodrece, a figueira já está de pé sozinha, no formato exato da árvore que substituiu — sem que a floresta jamais visse um buraco onde havia uma árvore. A migração Strangler Fig faz isso com software: o novo cresce em volta do velho até poder ficar de pé sem ele.

01Conceito

Por que não o big-bang

A reescrita total falha por uma razão estrutural: ela concentra todo o risco num único evento futuro. Durante meses não há entrega, o novo persegue um legado que continua mudando, e requisitos escondidos no velho só aparecem quando já é tarde. O dia da virada é tudo-ou-nada.

O Strangler Fig troca esse evento único por uma sequência de migrações pequenas, cada uma em produção e reversível. Se uma rota migrada dá problema, você a aponta de volta para o legado — o raio de dano é uma funcionalidade, não o sistema inteiro.

A peça técnica que viabiliza tudo é a fachada de roteamento na frente dos dois sistemas: ela recebe todas as requisições e decide, por rota, quem atende. No começo, tudo vai ao legado; a cada release, mais rotas apontam para o novo; no fim, nada sobra no velho e ele é desligado. Um API Gateway costuma ser exatamente esse roteador, o que faz o padrão se encaixar naturalmente em quem já tem um.

O ponto mais espinhoso não é o código, são os dados. Enquanto rotas do legado e do novo coexistem, ambos leem e escrevem dados relacionados, e é preciso mantê-los consistentes: por um banco compartilhado temporário, por sincronização, ou por dupla escrita durante a transição. É aqui que entra a camada anticorrupção — a fronteira que traduz o modelo do legado para o do sistema novo, para que o novo não nasça já contaminado pelo modelo que está substituindo. Sem cuidar dos dados, o Strangler Fig vira dois sistemas discordando sobre a verdade; com esse cuidado, ele é a forma mais segura conhecida de aposentar um legado.

02No mundo real

Como isso aparece na prática

O monólito legado aposentado sem parar

Cenário

Uma empresa precisava substituir um monólito de quinze anos, mas o negócio não podia parar e uma reescrita big-bang anterior já havia sido cancelada depois de um ano sem entregar nada.

Como o padrão entra

Um roteador foi posto na frente do monólito, e as funcionalidades foram reescritas uma a uma no sistema novo, migrando rota a rota. A cada release, o legado atendia menos; ao fim, foi desligado.

Trade-off: A migração durou bem mais que um big-bang teórico, e exigiu manter e sincronizar os dois sistemas o tempo todo. Em troca, o negócio nunca parou, e cada passo era reversível se desse errado.

A rota migrada que voltou para o legado

Cenário

Durante a migração, uma funcionalidade reescrita apresentou um bug sutil de cálculo que só apareceu com tráfego real, ameaçando dados de clientes.

Como o padrão entra

Como o roteador controlava o destino por rota, aquela funcionalidade foi reapontada para o legado em minutos, enquanto o bug era corrigido no novo, sem afetar o resto da migração.

Trade-off: Aquela rota ficou temporariamente no sistema antigo, atrasando um pouco o cronograma. É exatamente o valor do padrão: o problema ficou contido numa funcionalidade e foi revertido, em vez de derrubar uma virada inteira.

03Cuidado

Onde isto dá errado

  1. Ignorar a sincronização de dados

    Enquanto legado e novo coexistem, os dois tocam os mesmos dados. Sem sincronização, banco compartilhado temporário ou dupla escrita, os dois sistemas passam a discordar sobre a verdade — o ponto mais espinhoso da migração, e o mais esquecido.

  2. Deixar a migração parar no meio

    Conviver com dois sistemas é caro e deveria ser temporário. Sem dono e prazo, a migração estaciona e o roteador vira permanente, com metade no velho e metade no novo — o custo da transição sem o benefício da conclusão.

  3. Deixar o modelo do legado contaminar o novo

    Se o sistema novo lê o legado sem uma camada anticorrupção, o modelo torto do velho vaza para o novo, que nasce já com os vícios que deveria eliminar. A tradução na fronteira é o que preserva o modelo limpo.

04Implementação

Rotear por rota, migrar aos poucos

// A reescrita big-bang: parar tudo, reescrever por meses, virar a chave
// num dia. Historicamente, a forma mais confiavel de matar um sistema.

// STRANGLER FIG: um roteador na frente decide, rota a rota, o que ja
// foi migrado (vai ao novo) e o que ainda nao (vai ao legado).
function rotear(req: Request) {
  const migradas = ["/pedidos", "/carrinho"]; // ja reescritas no novo
  if (migradas.some((p) => req.path.startsWith(p))) {
    return encaminhar(req, "http://sistema-novo");   // <- novo
  }
  return encaminhar(req, "http://sistema-legado");    // <- velho, ainda
}

// A cada release, mais uma rota migra: a lista "migradas" cresce,
// o legado encolhe. O usuario nunca ve um big-bang — ve o sistema
// funcionando o tempo todo, enquanto por dentro ele e substituido.
// Quando a ultima rota migrar, o legado e desligado. Sem virada de chave.
05Decisão

Quando usar × quando evitar

Use quando

3
  • Ao substituir um legado grande demais para um big-bang seguro.
  • Quando o sistema precisa funcionar e evoluir durante a migração.
  • Quando reduzir risco em passos reversíveis vale mais que a velocidade.

Evite quando

2
  • Em sistemas pequenos, onde a reescrita direta é de baixo risco.
  • Quando manter e sincronizar os dois lados custaria mais que um corte limpo.

Testar o que ficou

3 armadilhas de Strangler Fig viram perguntas, com o nome do padrão escondido, misturadas com padrões parecidos.

Onde isto aparece de verdade

Você já usa isto, talvez sem saber o nome.

  • o proxy que roteia velho × novoUm roteador na frente decide, rota a rota, o que já foi migrado (vai ao novo) e o que ainda não (vai ao legado).
  • migração por rota, não big-bangSubstituir o sistema antigo pedaço a pedaço, com o novo crescendo em volta dele, até o velho poder ser desligado.
  • o legado que encolhe até sumirO sistema antigo vai perdendo funções para o novo até restar nada — a figueira que estrangulou a árvore hospedeira.

O padrão em uma linha

Sem nome de domínio, sem cerimônia. Só a ideia.

// Novo cresce ao redor; legado encolhe.
proxy.rota("/checkout", podeNovo ? novo : legado);

O que ele cobra

1 salto a mais entre a chamada e o efeito.

  • Manter os dois sistemas vivos e o roteador de fronteira durante toda a migração, que pode durar bastante
  • Dados que os dois lados leem e escrevem exigem sincronização ou dupla escrita enquanto durar a transição

Não vale se: o sistema é pequeno e a reescrita direta é rápida e de baixo risco — aí conviver com o legado custa mais do que um corte limpo.

Onde isto aparece

Continue por aqui