MÓDULO 12 · CONCEITO 11 DE 12

Estratégias de Deploy — blue-green, canary, rolling e feature flags

Blue-green: switch instantâneo. Rolling: substituição gradual. Canary: percentagem de tráfego com observabilidade ativa. Feature flags como deploy desacoplado de release. O que cada estratégia garante em rollback. Database migrations sem downtime.

Tempo de leitura ~22 min Pré-requisito 04 · CI/CD Pipelines · 05 · GitOps Próximo 12 · Operação Dia 2 →

Em 2012, o Amazon Prime Video sofreu um incidente durante um deploy que derrubou o serviço por 49 minutos. A causa era um simples bug introduzido na versão nova que não havia sido detectado nos testes — e o deploy havia substituído todas as instâncias de uma vez. O rollback demorou porque o processo de deploy era lento. Se o deploy tivesse sido incremental — enviando tráfego progressivamente para a nova versão enquanto monitorava erros — o impacto teria sido limitado a uma fração dos usuários por poucos minutos antes da detecção.

A estratégia de deploy determina o perfil de risco de uma entrega: quanta superfície de usuários é exposta ao novo código e por quanto tempo antes que problemas possam ser detectados e revertidos. Não existe estratégia universalmente correta — cada uma tem trade-offs que dependem do contexto: tipo de mudança, volume de tráfego, tolerância a risco, e capacidade de rollback.

Rolling deploy — o padrão default do Kubernetes

Rolling deploy é o comportamento padrão de um Kubernetes Deployment: substituir gradualmente pods da versão antiga por pods da nova versão, mantendo um número mínimo de pods disponíveis durante o processo.

apiVersion: apps/v1
kind: Deployment
spec:
  replicas: 10
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 2          # criar até 2 pods além do desejado durante o update
      maxUnavailable: 0    # nunca remover um pod antes de ter um novo pronto

Com maxUnavailable: 0 e maxSurge: 2, o processo é: criar 2 novos pods (versão nova) → esperar ficarem prontos → remover 2 pods antigos → repetir até substituir todos. Em nenhum momento o sistema fica abaixo de 10 pods disponíveis.

O problema do rolling deploy: durante a transição, ambas as versões estão em produção simultaneamente. Se a nova versão introduz uma mudança incompatível no formato de resposta de uma API, usuários terão respostas inconsistentes dependendo de qual pod serviu a request. Mudanças backward-incompatíveis exigem uma das outras estratégias.

Blue-green — switch instantâneo

Blue-green mantém dois ambientes idênticos: blue (produção atual) e green (nova versão). O deploy é atomicamente feito mudando o switch de tráfego de blue para green:

                    ┌──────────────┐
               ┌────│ Load Balancer │────┐
               │    └──────────────┘    │
        100% →─┤                        ├─→ 0%
               │                        │
          ┌────▼────┐            ┌───────▼─────┐
          │  Blue   │            │    Green    │
          │(v1.0)   │            │  (v1.1)     │
          │ 10 pods │            │  10 pods    │
          └─────────┘            └─────────────┘
                     (switch → green)
          ┌──────────┐           ┌─────────────┐
          │  Blue    │           │    Green    │
          │(v1.0)    │      ←────│  (v1.1)     │
          │ mantido  │  0% traffic  100% traffic
          └──────────┘           └─────────────┘

O rollback em blue-green é instantâneo: basta apontar o load balancer de volta para blue. Isso é único — outras estratégias não oferecem rollback instantâneo com versão anterior completamente preservada.

Em Kubernetes, blue-green pode ser implementado com dois Deployments e alterando o selector do Service:

# Deployment blue (produção atual)
apiVersion: apps/v1
kind: Deployment
metadata:
  name: meu-app-blue
spec:
  template:
    metadata:
      labels:
        app: meu-app
        version: blue

---
# Deployment green (nova versão)
apiVersion: apps/v1
kind: Deployment
metadata:
  name: meu-app-green
spec:
  template:
    metadata:
      labels:
        app: meu-app
        version: green

---
# Service — aponta para blue ou green
apiVersion: v1
kind: Service
metadata:
  name: meu-app
spec:
  selector:
    app: meu-app
    version: blue    # ← mudar para "green" para o switch

O custo de blue-green é o dobro da infraestrutura durante o período de transição. Para sistemas grandes, isso pode ser proibitivo. Em cloud com auto-scaling, o custo é temporário — mas para databases ou sistemas stateful com custo fixo alto, blue-green pode não ser viável.

Canary deploy — exposição progressiva com dados

Canary deploy expõe a nova versão para uma fração do tráfego, monitorando métricas antes de expandir. O nome vem dos canários usados em minas de carvão — se o canário morre, há problema.

                    ┌──────────────┐
                    │ Load Balancer│
                    └──────┬───────┘
                           │
              90% ─────────┼────────── 10%
               ↓           │           ↓
          ┌─────────┐      │      ┌──────────┐
          │Stable   │      │      │ Canary   │
          │(v1.0)   │      │      │ (v1.1)   │
          │ 9 pods  │      │      │ 1 pod    │
          └─────────┘      │      └──────────┘
                           │
                    Monitorar:
                    - Error rate
                    - Latência p99
                    - Métricas de negócio

Em Kubernetes com Argo Rollouts (ou NGINX/Istio para controle de tráfego), o processo é automatizado com progressive delivery:

apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
  name: meu-app
spec:
  strategy:
    canary:
      steps:
        - setWeight: 10     # 10% do tráfego para a nova versão
        - pause: {duration: 10m}   # esperar 10 minutos
        - analysis:         # verificar métricas automaticamente
            templates:
              - templateName: success-rate
        - setWeight: 50     # 50% se análise passou
        - pause: {duration: 20m}
        - analysis:
            templates:
              - templateName: success-rate
        - setWeight: 100    # 100% — promoção completa

---
apiVersion: argoproj.io/v1alpha1
kind: AnalysisTemplate
metadata:
  name: success-rate
spec:
  metrics:
    - name: success-rate
      interval: 5m
      successCondition: result[0] >= 0.99   # 99% de sucesso
      failureLimit: 3
      provider:
        prometheus:
          address: http://prometheus:9090
          query: |
            sum(rate(http_requests_total{status!~"5..",app="meu-app",version="canary"}[5m]))
            /
            sum(rate(http_requests_total{app="meu-app",version="canary"}[5m]))

O Argo Rollouts reverte automaticamente se a análise falhar — sem intervenção humana. O canário detectou o problema antes de atingir 100% dos usuários.

Feature flags — deploy desacoplado de release

Feature flags (ver Conceito 04) são a estratégia ortogonal a todas as outras: você pode fazer um rolling deploy de código novo, mas a feature nova está desabilitada — o "deploy" e o "release" são eventos separados.

Esse desacoplamento resolve um problema fundamental: qualquer uma das estratégias acima muda a versão do código para todos os usuários ao mesmo tempo (ou progressivamente). Feature flags permitem rollout por segmento:

A combinação de canary deploy + feature flags é o estado da arte: o canary deploy garante que o código novo não tem regressões técnicas (erros, latência), e o feature flag controla a exposição de negócio da nova funcionalidade.

Database migrations sem downtime — expand-contract

O maior desafio de deploy sem downtime é banco de dados: migrations que renomeiam colunas, mudam tipos, ou removem campos são backward-incompatíveis com a versão anterior da aplicação.

O padrão expand-contract (também chamado de parallel change) resolve isso em três fases:

Fase 1 — Expand (deploy 1): adicionar a nova coluna, manter a antiga. A nova versão escreve em ambas, lê da antiga.

-- Migration: adicionar coluna nova, manter antiga
ALTER TABLE users ADD COLUMN full_name VARCHAR(500);

-- Application code v2 (durante Expand)
-- Escreve em ambas:
UPDATE users SET name = $1, full_name = $2 WHERE id = $3

-- Lê da antiga (compatível com v1 ainda em pods durante rolling deploy):
SELECT name FROM users WHERE id = $1

Fase 2 — Migrate (script ou deploy 2): preencher a nova coluna com dados da antiga para todos os registros existentes. A nova versão agora lê da nova coluna.

-- Backfill em batches para não causar lock
DO $$
DECLARE
  batch_size INT := 1000;
  offset_val INT := 0;
BEGIN
  LOOP
    UPDATE users
    SET full_name = name
    WHERE id IN (
      SELECT id FROM users
      WHERE full_name IS NULL
      LIMIT batch_size
      OFFSET offset_val
    );
    EXIT WHEN NOT FOUND;
    offset_val := offset_val + batch_size;
    PERFORM pg_sleep(0.1);   -- não exaurir I/O
  END LOOP;
END $$;

Fase 3 — Contract (deploy 3): remover a coluna antiga após confirmar que nenhum pod da versão anterior está rodando.

-- Só fazer isso após confirmar que todos os pods são v3
ALTER TABLE users DROP COLUMN name;

O expand-contract transforma uma migration backward-incompatível em três mudanças backward-compatíveis. O custo é o tempo (3 deploys em vez de 1) e o espaço temporário de manter duas colunas. Para tabelas muito grandes, a fase de backfill pode levar horas — nesse caso, ferramentas como pg_repack ou Percona Online Schema Change ajudam a fazer a mudança sem locks prolongados.

Comparação de estratégias

Estratégia Velocidade de rollback Custo de infra Complexidade Versões simultâneas
Rolling Lento (novo deploy) Baixo (+maxSurge temporário) Baixa Sim (durante transição)
Blue-green Instantâneo (switch) Alto (2x infra) Média Não (switch atômico)
Canary Rápido (reduzir tráfego) Baixo (poucos pods canário) Alta Sim (controlado)
Feature flags Instantâneo (toggle) Nenhum Média (dívida de código) Não (código único, comportamento variável)
recomendação por contexto

Para serviços de alta criticidade com SLA rigoroso: canary deploy automático com Argo Rollouts + feature flags para controle de release. Para serviços com banco de dados e mudanças de schema: expand-contract obrigatório, independente da estratégia de deploy do serviço. Para startups em estágio inicial: rolling deploy padrão do Kubernetes com testes sólidos é suficiente — canary e blue-green adicionam complexidade operacional que não compensa até ter volume de tráfego para detectar problemas estatisticamente. Para mudanças backward-incompatíveis de API: blue-green com versionamento de API é a única estratégia que evita versões simultâneas servindo respostas diferentes.

Decisões de engenharia

Rolling vs canary — quando a complexidade se justifica
Rolling deploy é suficiente para a maioria dos serviços: baixa complexidade, zero custo adicional de infra, e já está integrado ao Kubernetes. Canary se justifica quando: o serviço tem alto tráfego (suficiente para detectar problemas estatisticamente em 10% do tráfego rapidamente), o time tem observabilidade madura (Prometheus com SLIs por versão configurados), e o custo de um incidente completo é alto. Para serviços com menos de 1.000 req/hora, 10% do tráfego são 100 requests — volume insuficiente para detectar bugs de baixa taxa de ocorrência em tempo hábil. Nesse caso, testes sólidos antes do deploy são mais eficazes do que canary.
Blue-green vs canary — rollback vs custo
Blue-green quando a velocidade de rollback é crítica e você pode tolerar o custo de 2x infra temporariamente: mudanças backward-incompatíveis onde não pode ter duas versões servindo simultaneamente, APIs com contrato estrito onde inconsistência durante transição é inaceitável, ou sistemas com volume de tráfego baixo demais para canary ser útil. Canary quando você quer validar comportamento em produção real antes de 100% do tráfego, e o custo de 2x infra é proibitivo. O canary não tem rollback instantâneo — ele tem redução de impacto progressivo. Se o canary detecta um problema após 50% do tráfego já foi migrado, 50% dos usuários foram afetados antes da reversão.
Expand-contract vs maintenance window para migrations
Expand-contract para qualquer sistema com SLA de disponibilidade ou que não pode ter downtime agendado. O custo é engenharia adicional (3 PRs em vez de 1) e tempo de execução (semanas para migração completa de tabelas grandes). Maintenance window apenas quando: o sistema é internal-only com SLA de negócio baixo (pode ter 30 min de indisponibilidade às 2h da manhã), ou a migration é tão complexa que expand-contract seria impraticável (ex: mudança de engine de banco de dados). Para startups sem SLA contratual e volume baixo, maintenance window às madrugadas pode ser aceitável — mas implica cultura de deploy que não escala. Uma vez que você tem SLA contratual, migrate para expand-contract.
Feature flags para release vs deploy strategies para deploy técnico
Feature flags e deploy strategies são ortogonais — ambos são necessários, cada um cobre uma dimensão diferente. Deploy strategies (rolling, blue-green, canary) controlam como o código novo chega aos servidores: com que velocidade, com que risco de versões simultâneas, com que capacidade de rollback técnico. Feature flags controlam quando uma funcionalidade fica visível para usuários: separando o deploy (mudança de código) do release (mudança de comportamento). A combinação ideal: canary deploy garante que a nova versão não introduz regressões técnicas (erros, memória, latência), feature flag garante que a nova funcionalidade é exposta gradualmente por segmento de usuário. São camadas complementares de controle de risco.

Perguntas de entrevista

Por que rolling deploy pode ser problemático para mudanças backward-incompatíveis, e como mitigar?

Durante um rolling deploy, há um período de transição em que ambas as versões do serviço estão rodando simultaneamente. Se um cliente faz duas requisições em sequência e cada uma vai para um pod diferente (uma v1 e uma v2), ele pode receber respostas inconsistentes — especialmente se a v2 mudou o formato do JSON, renomeou campos, ou alterou o contrato da API.

Exemplos de mudanças que quebram durante rolling: renomear um campo de resposta (user_iduserId), mudar o tipo de um campo (string → número), remover um campo que o cliente usa, ou mudar a semântica de um código de status HTTP. Clientes que fazem parse de respostas podem falhar dependendo de qual versão serviu.

As mitigações são: (1) backward compatibility por design — nunca remover ou renomear campos em uma única versão; introduza o novo campo, deprecie o antigo, remova apenas depois que todos os consumers migraram; (2) versionamento de API — /v1/users e /v2/users coexistem; migre consumers gradualmente; (3) blue-green deploy para mudanças incompatíveis — o switch atômico garante que todos os pods são da mesma versão ao mesmo tempo; (4) feature flags para desacoplar o deploy da mudança de comportamento.

Como o Argo Rollouts implementa canary deploy com análise automática, e o que acontece quando a análise falha?

O Argo Rollouts substitui o objeto Deployment do Kubernetes por um objeto Rollout, que adiciona estratégias de deploy avançadas. Para canary, você define uma sequência de passos: aumentar o peso do tráfego para o canary, pausar por um intervalo, executar uma análise automática, e repetir até 100%.

A análise é feita via AnalysisTemplate — um objeto que define métricas a verificar (Prometheus, Datadog, CloudWatch), intervalos de verificação, condições de sucesso, e limite de falhas. Por exemplo: a taxa de sucesso do canary deve ser >= 99% medida a cada 5 minutos, e pode falhar até 3 vezes antes de abortar. O Argo Rollouts consulta o Prometheus diretamente com a query configurada.

Quando a análise falha (taxa de sucesso abaixo do threshold, ou número de erros excede o limite), o Argo Rollouts automaticamente: interrompe o avanço do canary, reverte o peso do tráfego para 0% no canary, e marca o Rollout como degraded. Os pods da versão nova ficam pausados — o rollback para 100% na versão estável é instantâneo (o peso já voltou para 0%). Sem intervenção humana necessária para o rollback.

Explique o padrão expand-contract passo a passo para renomear uma coluna em uma tabela com 10M de registros sem downtime.

O problema: renomear users.name para users.full_name em uma tabela com 10M de registros. Um ALTER TABLE RENAME COLUMN direto causa lock na tabela durante a execução e é incompatível com a versão v1 da aplicação que ainda usa name.

Fase 1 — Expand (PR 1): Migration: ALTER TABLE users ADD COLUMN full_name VARCHAR(500) (sem NOT NULL, sem DEFAULT — adicionar coluna vazia é fast). Application v2: escreve em ambas as colunas (name e full_name) em toda operação de INSERT/UPDATE; continua lendo de name. Essa versão é compatível com v1 (que só conhece name) durante o rolling deploy. Deploy e aguardar que todos os pods sejam v2.

Fase 2 — Migrate: Script de backfill em batches de 1.000 registros: UPDATE users SET full_name = name WHERE full_name IS NULL LIMIT 1000, repetindo com sleep entre cada batch para não saturar I/O. Com 10M de registros e batches de 1.000 com 100ms de sleep, leva ~17 minutos. Application v3: lê de full_name, ainda escreve em ambas (compatível com v2 durante rolling).

Fase 3 — Contract (PR 3): Após confirmar que todos os pods são v3: migration ALTER TABLE users DROP COLUMN name. Application v4: remove referências à coluna antiga, escreve apenas em full_name. O lock do DROP COLUMN é muito mais rápido que o de RENAME porque não há transformação de dados.

Em que cenários blue-green é claramente superior ao canary, e quando o custo não se justifica?

Blue-green é claramente superior ao canary em três cenários: (1) mudanças backward-incompatíveis de API onde coexistência de versões durante canary causaria erros de consistência — blue-green troca de forma atômica; (2) requisito de rollback instantâneo contratual — "se algo der errado, voltar em 30 segundos" é realista com blue-green, não com canary que exige reduzir peso gradualmente; (3) volume de tráfego baixo onde 10% do tráfego para canary não é estatisticamente significativo — um serviço com 50 req/hora tem 5 req/hora no canary, insuficiente para detectar bugs de baixa taxa.

O custo de blue-green não se justifica quando: o serviço tem custo fixo alto (banco de dados gerenciado com preço de instância — duplicar não é opção), a infra é muito grande (2x de 1.000 pods por horas é custoso), ou quando a mudança é pequena e pode ser protegida por feature flags + testes sólidos sem custo de infra adicional. Para um deploy de bugfix de 3 linhas que passou em 100% do pipeline de testes, blue-green é overhead sem benefício proporcional.

Como a combinação de canary deploy + feature flags representa o estado da arte em deploy seguro, e como cada um resolve diferentes dimensões de risco?

Canary deploy e feature flags são ortogonais — cada um resolve uma dimensão diferente de risco, e a combinação cobre ambas.

Canary deploy controla a dimensão técnica: verifica que a nova versão do código não introduz regressões de performance (latência p99 aumentou?), confiabilidade (error rate aumentou?), ou recursos (memory leak?). O canary expõe 10% do tráfego real à nova versão e verifica métricas objetivas automaticamente — se algo quebrar tecnicamente, o rollback é automático antes de 100% dos usuários serem afetados. Canary valida que "o código funciona corretamente".

Feature flags controlam a dimensão de produto: verifica que a nova funcionalidade tem o comportamento de negócio esperado (usuários estão convertendo mais? tempo na tela aumentou? suporte recebeu mais tickets?). Feature flags permitem rollout por segmento (1% → 5% → 20% → 100%), targeting por perfil de usuário (beta testers primeiro), e rollback de negócio sem deploy de código. Feature flags validam que "a funcionalidade gera o valor esperado".

A combinação: o canary deploy garante que o código novo chega à produção sem regressões técnicas; o feature flag garante que a nova funcionalidade é exposta gradualmente e pode ser desabilitada instantaneamente se o comportamento de negócio for inesperado — sem precisar de um rollback de código (que leva minutos) ou de um novo canary deploy.

Exercícios práticos

01 · Rolling deploy com zero downtime e readiness probe

Configure um Deployment com 5 réplicas usando maxUnavailable: 0 e maxSurge: 1. Implemente uma readiness probe que simula um tempo de warmup de 10 segundos (o endpoint /ready retorna 503 por 10 segundos após o start, depois retorna 200). Faça um rolling deploy para uma nova versão da imagem e verifique usando kubectl get pods -w que novos pods só recebem tráfego após passar na readiness probe, e que o número de pods disponíveis nunca cai abaixo de 5. Meça a latência das requisições durante o deploy para confirmar zero downtime.

Critério: Durante o deploy, o número de pods no estado Ready nunca cai abaixo de 5; o endpoint do serviço não retorna erros durante a transição; os novos pods só aparecem como Ready após o período de warmup da readiness probe.
02 · Canary deploy com Argo Rollouts e AnalysisTemplate

Instale o Argo Rollouts em um cluster local. Converta um Deployment existente para um Rollout com estratégia canary: 10% → pause 5min → análise → 50% → pause 10min → análise → 100%. Crie um AnalysisTemplate que consulta o Prometheus (instale kube-prometheus-stack) verificando que a error rate do canary é menor que 1%. Simule uma falha: faça uma versão nova que retorna 50% de errors 500, e verifique que o Argo Rollouts reverte automaticamente para a versão estável sem intervenção manual. Em seguida, faça uma versão boa e verifique a promoção automática.

Critério: Uma versão com alta error rate é revertida automaticamente pelo Argo Rollouts antes de chegar a 50% do tráfego; uma versão saudável é promovida para 100% automaticamente após as análises passarem; o dashboard do Argo Rollouts mostra o histórico de análises com os valores de métrica coletados.
03 · Expand-contract migration em PostgreSQL

Em um banco PostgreSQL com uma tabela users com coluna email (varchar), implemente a migration expand-contract completa para adicionar a coluna email_normalized (email em lowercase) sem downtime. Fase 1 (Expand): adicionar coluna email_normalized, atualizar a aplicação para escrever nas duas colunas, ler da antiga. Fase 2 (Migrate): script de backfill em batches de 500 registros com 50ms de sleep entre batches, depois atualizar aplicação para ler da nova. Fase 3 (Contract): remover coluna antiga. Meça o tempo de cada fase e confirme que a tabela nunca ficou locked por mais de 1 segundo.

Critério: Após a fase 3, a coluna email_normalized contém todos os emails em lowercase; nenhuma query na aplicação falhou durante o processo de migração; o backfill processou registros em batches sem causar lock da tabela por mais de 1 segundo.
04 · Blue-green deploy com switch de Service selector

Implemente blue-green deploy manual em Kubernetes: crie dois Deployments (app-blue com versão v1 e app-green com versão v2) com labels version: blue e version: green respectivamente. Configure um Service que seleciona apenas version: blue. Gere carga contínua no serviço (usando hey ou k6) enquanto executa o switch do selector do Service de blue para green. Meça a latência durante o switch e verifique que o número de erros 5xx é zero. Execute o rollback (switch de volta para blue) e confirme que é instantâneo.

Critério: O switch de blue para green acontece sem nenhum erro 5xx para os clientes; o rollback (green → blue) é executado em menos de 5 segundos; após o switch, 100% do tráfego vai para a versão green confirmado por logs de acesso ou métricas.
05 · Pipeline de deploy com análise automática e rollback

Construa um pipeline completo de deploy seguro combinando GitHub Actions + Argo Rollouts + feature flags. O pipeline deve: (1) fazer deploy da nova imagem como canary com 10% do tráfego via Argo Rollouts; (2) aguardar a análise automática (AnalysisTemplate com Prometheus); (3) se a análise passar, promover para 100%; (4) se falhar, reverter automaticamente e criar um GitHub Issue com os valores de métrica que falharam. A feature nova no código deve ser controlada por um feature flag (Unleash ou equivalente) que começa desabilitado — habilitar o flag é um passo separado após o canary ser promovido.

Critério: O pipeline completo (push → canary → análise → promoção) roda de forma automatizada sem intervenção humana para o caso happy path; uma versão com erro é revertida e um GitHub Issue é criado com os valores das métricas que causaram a falha; a feature nova só fica visível para usuários após o flag ser habilitado explicitamente.

Referências

  1. docs Argo Rollouts — Progressive Delivery argo-rollouts.readthedocs.io · guia completo de canary e blue-green com AnalysisTemplate automático
  2. article Martin Fowler — BlueGreenDeployment martinfowler.com · 2010 · definição canônica do padrão blue-green e quando usar
  3. article Martin Fowler — Expand Contract martinfowler.com · o padrão expand-contract para database migrations backward-compatíveis
  4. article Google — Patterns for Scalable and Resilient Apps: Deployments cloud.google.com/architecture · exemplos de canary e progressive delivery em GKE
  5. book Jez Humble & David Farley — Continuous Delivery Addison-Wesley · 2010 · capítulo de deployment pipelines e estratégias de zero-downtime deploy
  6. docs Flagger — Progressive Delivery for Kubernetes flagger.app · alternativa ao Argo Rollouts com suporte a Istio, NGINX, e Linkerd para canary
  7. article Netflix TechBlog — Automated Canary Analysis at Netflix netflixtechblog.com · como o Netflix implementa canary analysis com Kayenta e Spinnaker
  8. article GitHub Engineering — How we deploy safely at GitHub github.blog · processo de deploy do GitHub com feature flags, canary e rollback
  9. article Atlassian — Blue-green deployments vs canary releases atlassian.com/blog · comparação prática de estratégias de deploy com trade-offs por contexto
  10. docs Kubernetes — Deployment Strategies kubernetes.io/docs · rolling update parameters, maxSurge, maxUnavailable e readiness gates
  11. article Stripe — Migrating a database with zero downtime stripe.com/blog · caso real de expand-contract para migração de schema em produção de alta escala
  12. article PlanetScale — Online Schema Changes planetscale.com/blog · vitess e gh-ost para DDL sem lock em tabelas de alta escala