MÓDULO 2.5

🔄 Migração e Evolução

Sistemas vivos mudam — e a arte está em evoluir sem quebrar. Neste módulo, você vai aprender as estratégias comprovadas para migrar arquiteturas, trocar tecnologias e evoluir sistemas em produção com segurança.

7
Tópicos
35 min
Duração
Interm.
Nível
Estrat.
Tipo
1

🌿 Strangler Fig Pattern

A migração mais segura que existe: o novo sistema cresce ao redor do antigo, como uma figueira estranguladora. Cada nova feature vai para o sistema novo, cada módulo migrado é redirecionado. O sistema antigo encolhe gradualmente até desaparecer. Zero big-bang, zero downtime.

💡 Como Funciona

  • Proxy/Router layer — Um proxy roteia requests entre sistema antigo e novo
  • Migração gradual — Um módulo por vez é reimplementado no novo sistema
  • Rollback safety — Se algo falhar, o proxy volta a rotear para o antigo
  • Feature parity tracking — Dashboard mostra % do tráfego já migrado

📊 Caso Real: The Guardian

Martin Fowler documentou o padrão no caso do The Guardian:

  • Sistema antigo: Monolito Java legado com 10+ anos
  • Sistema novo: Scala/Play Framework
  • Estratégia: Cada seção do site migrada individualmente (esportes, cultura, notícias)
  • Timeline: Anos, não meses — e isso é esperado
  • Resultado: Zero downtime durante toda a migração

⚠️ Por que Reescritas Big-Bang Falham

Dados do Standish Group mostram que 70% das reescritas big-bang falham:

  • O sistema antigo continua recebendo features durante a reescrita
  • O novo sistema nunca alcança feature parity
  • O budget acaba antes da migração completar — ficam com dois sistemas

💡 Dica Prática

Comece pelo módulo com menor acoplamento e maior dor. Se o módulo de busca é lento e tem interface clara, migre primeiro. Vitórias rápidas criam momentum e confiança no processo.

2

🔀 Branch by Abstraction

Mudar implementação sem mudar interface. Introduza uma abstração entre consumidores e a implementação que vai mudar. Troque a implementação por trás da abstração. Remova a abstração quando a migração estiver completa. Ninguém que usa o componente percebe a mudança.

🛠️ 4 Passos do Branch by Abstraction

1

Criar abstração

Introduza uma interface entre os consumidores e a implementação atual. Todos passam a usar a interface.

2

Implementar nova versão

Crie a nova implementação atrás da mesma interface. Rode ambas em paralelo.

3

Comparar e validar

Compare resultados das duas implementações. Shadow traffic: requests reais vão para ambas, só a antiga responde ao usuário.

4

Switchover e cleanup

Aponte para a nova implementação. Remova a antiga. Opcionalmente, remova a abstração se não for mais necessária.

📊 Caso Real: Flickr

O Flickr migrou de MySQL para PostgreSQL usando Branch by Abstraction — criaram uma interface de “data store”, rodaram ambos em paralelo por semanas comparando resultados, e fizeram o switch quando tinham 100% de confiança que os resultados eram idênticos.

💡 Dica Prática

Branch by Abstraction funciona melhor quando a interface já existe naturalmente. Se você precisa criar uma abstração muito forçada, talvez Strangler Fig seja mais adequado. Escolha a estratégia que se encaixa na arquitetura atual.

3

🐦 Canary Releases

Testar em produção com rede de segurança. Deploy da nova versão para um pequeno percentual de usuários (1-5%). Monitorar métricas. Se tudo estiver ok, aumentar gradualmente. Se não, rollback instantâneo. Staging não pega tudo — produção tem dados, carga e comportamentos únicos.

💡 Progressive Rollout

  • 1% — Deploy inicial. Monitorar error rate, latência, conversão por 1-24h
  • 5% — Se métricas ok, expandir. Volume suficiente para detectar edge cases
  • 25% — Volume significativo. Se sobreviver aqui, provavelmente está ok
  • 100% — Rollout completo. Manter versão anterior pronta para rollback por 24-48h

📊 Caso Real: Google Gmail

O Google faz canary de toda mudança no Gmail:

  • 0.1% dos usuários primeiro, por 24 horas
  • Automatic rollback se error rate > baseline
  • 1.8 bilhões de usuários protegidos de bugs que staging não pegou
  • Observabilidade extrema — métricas, logs, traces em cada stage

✓ O que FAZER

  • Definir automatic rollback triggers antes do deploy
  • Monitorar business metrics além de métricas técnicas
  • Manter versão anterior pronta para rollback instantâneo

✗ O que NÃO fazer

  • Fazer canary sem observabilidade adequada
  • Pular de 1% para 100% porque “parece ok”
  • Fazer canary em sexta-feira à tarde
4

🏗️ Database Migration Strategies

O problema mais difícil de migração. Código é stateless — você reescreve e deploya. Banco de dados é stateful — tem dados reais, schemas complexos e dependências que não aparecem no diagrama. Migrar banco errado é a decisão mais cara para reverter.

💡 Estratégias de Migração de Banco

  • Dual-write — Escrever nos dois bancos simultaneamente durante a transição
  • Shadow traffic — Enviar reads para ambos, comparar resultados, servir do antigo
  • Schema versioning — Migrações backward-compatible, nunca breaking changes diretas
  • Expand-contract — Adicione nova coluna, migre dados, remova antiga (3 deploys)

📊 Caso Real: GitHub

O GitHub migrou de MySQL para Vitess (MySQL sharded) sem downtime para 100M+ repositórios:

  • Dual-write por 6 meses, comparando resultados antes de cortar
  • Shadow reads validavam consistência entre os dois sistemas
  • Zero downtime — usuários nunca perceberam a migração
  • Rollback plan testado semanalmente durante todo o processo

⚠️ Armadilhas de Migração de Banco

  • Stored procedures — Lógica de negócio escondida no banco que ninguém lembra
  • Implicit schemas — JSON columns sem validação; dados em formatos inesperados
  • Data volume — Migrar 1TB de dados leva tempo; planeje janelas de manutencão

💡 Dica Prática

Regra de ouro para migrações de schema: nunca faça breaking changes em um único deploy. Use o padrão expand-contract: (1) adicione nova coluna, (2) migre dados e código, (3) remova coluna antiga. Três deploys, zero downtime.

5

🚩 Feature Flags

O botão de liga/desliga em produção. Feature flags são lógica condicional que ativa ou desativa funcionalidades sem deploy. Permitem trunk-based development, A/B testing e kill switch para features problemáticas. Desacoplam deploy de release.

💡 Tipos de Feature Flags

  • Release flags — Ativa/desativa features novas. Vida curta (dias a semanas)
  • Experiment flags — A/B testing. Mostrar variante A ou B para grupos diferentes
  • Ops flags — Kill switches para situações de emergência. Vida longa
  • Permission flags — Funcionalidades por segmento (beta users, plano premium)

📊 Caso Real: Facebook (Gatekeeper)

O Facebook deploya código 3x por dia para 3B+ usuários — tudo atrás de feature flags:

  • Gatekeeper — Sistema interno de feature flags com milhões de configurações
  • Kill switch — Se algo quebra, desliga o flag em segundos, sem rollback de deploy
  • Gradual rollout — Nova feature para 1% → 10% → 50% → 100%
  • Targeting — Flags por país, dispositivo, segmento de usuário

✓ O que FAZER

  • Definir lifecycle para cada flag (data de remoção)
  • Tratar flags antigos como dívida técnica
  • Testar ambos os estados (flag on e flag off) na CI

✗ O que NÃO fazer

  • Acumular centenas de flags sem limpeza
  • Aninhar flags: if flag_a AND flag_b AND flag_c
  • Usar flags para lógica permanente de negócio
6

📐 Architecture Fitness Functions

Testes automatizados para sua arquitetura. Fitness functions verificam se a arquitetura continua atendendo seus atributos de qualidade: latência < 200ms? Zero dependências circulares? Cobertura > 80%? Sem fitness functions, degradação arquitetural é invisível até explodir.

💡 Tipos de Fitness Functions

  • Structural — Regras de dependência entre camadas (ArchUnit, NetArchTest)
  • Performance — Budgets de latência, throughput e bundle size na CI
  • Security — Scan de dependências vulneráveis, checagem de secrets no código
  • Observability — Todo endpoint tem health check? Todos os erros têm correlation ID?

📊 Exemplos Práticos

  • ArchUnit (Java)noClasses().that().resideInPackage("..controller..").should().dependOnClassesThat().resideInPackage("..repository..")
  • Bundle size check — CI falha se o JavaScript bundle ultrapassar 200KB gzipped
  • API response time — Testes de contrato que validam p95 < 200ms
  • Dependency graph — Alertas se dependências circulares forem introduzidas

💡 Dica Prática

Comece com 3 fitness functions simples: (1) nenhuma dependência circular, (2) bundle size < threshold, (3) nenhuma dependência com vulnerabilidade crítica. Rode na CI. Adicione mais conforme a maturidade do time cresce.

7

🔮 Evolutionary Architecture

Projetada para mudar. Evolutionary Architecture suporta mudanças incrementais guiadas em múltiplas dimensões. Não é “arquitetura que prevê o futuro” — é “arquitetura que aceita que não prevemos o futuro” e por isso facilita a adaptação contínua.

💡 Princípios da Evolutionary Architecture

  • Guided incremental change — Mudanças pequenas e frequentes, não revoluções
  • Last Responsible Moment — Adie decisões irreversíveis até ter informação suficiente
  • Fitness functions — Testes automatizados que guardam a integridade da arquitetura
  • Sacrificial architecture — Aceitar que partes do sistema serão substituídas é libertário

📊 Caso Real: Netflix — 7 Migrações em 15 Anos

A Netflix fez 7 grandes migrações arquiteturais, cada uma incremental:

  • 2007 — Datacenter próprio (monolito)
  • 2009 — Início da migração para AWS
  • 2012 — Microsserviços (centenas de serviços)
  • 2016 — Edge computing (Open Connect CDN)
  • 2019 — Serverless parcial para workloads específicos
  • 2022+ — IA/ML integrada no pipeline de encoding e recomendação

Nenhuma foi big-bang. Cada migração levou anos e foi guiada por fitness functions e métricas de negócio.

💬 Kent Beck

“Make the change easy, then make the easy change.”

Primeiro, refatore para que a mudança seja simples. Depois, faça a mudança simples. Dois passos pequenos em vez de um salto arriscado.

💡 Dica Prática

Pergunte-se regularmente: “Se precisássemos trocar [componente X] amanhã, quanto custaria?” Se a resposta é “meses de trabalho”, você tem acoplamento excessivo. Evolutionary architecture mantém o custo de mudança baixo e previsível.

📋 Resumo do Módulo

Strangler Fig é a migração mais segura — Novo sistema cresce ao redor do antigo; zero big-bang, zero downtime
Branch by Abstraction troca implementação sem mudar interface — Ideal para migrar componentes internos de forma transparente
Canary Releases testam em produção com segurança — 1% → 5% → 25% → 100% com rollback automático
Migração de banco é o problema mais difícil — Dual-write, shadow traffic e expand-contract são seus aliados
Feature Flags desacoplam deploy de release — Liga/desliga features sem deploy, mas gerencie o lifecycle
Fitness Functions guardam a arquitetura — Testes automatizados que detectam degradação antes que exploda
Evolutionary Architecture aceita a mudança — Projetada para adaptação contínua, não para prever o futuro

🏆 Parabéns! Você completou a Trilha 2!

Você agora domina os frameworks, trade-offs e estratégias para tomar decisões arquiteturais com confiança. De frameworks de decisão a ADRs, de análise de trade-offs a migrações seguras — você tem o toolkit completo do arquiteto.

Próxima trilha: Na Trilha 3, você vai aprender como IA e Large Language Models estão transformando a arquitetura de software — e como projetar sistemas que integram IA de forma segura e escalável.