Manutenção Por Equipe DevBras

Capa do artigo: Voltar a versão anterior é um procedimento, não uma esperança

Voltar a versão anterior é um procedimento, não uma esperança

Escreva o plano de volta de cada release, mantenha a versão anterior disponível, trate o banco à parte e ensaie a volta medindo o tempo antes de precisar dela.

O deploy de sexta piorou o checkout e alguém diz "é só voltar". A versão anterior sobe e quebra na partida, porque a migração já apagou uma coluna que ela lê. Ou a imagem anterior não existe mais, porque a limpeza do registry levou. A frase era conforto, não plano. A resposta curta: rollback é um procedimento escrito antes do deploy, com três partes. Saber exatamente qual é a versão anterior e garantir que ela continua disponível. Garantir que o banco depois da migração ainda serve para o código anterior. E ter ensaiado a volta, com o tempo medido, num ambiente parecido com produção.

Por que a volta falha quando você mais precisa

Código e dado não voltam do mesmo jeito. Voltar o código é trocar uma imagem por outra; leva segundos. Voltar o dado é outra história: uma coluna apagada, uma tabela convertida ou um formato de JSON alterado não se desfazem trocando imagem. Se a release mudou o banco de forma incompatível, o código anterior não funciona mais com ele, e restaurar backup significa perder tudo o que foi gravado desde então.

Os outros motivos são operacionais. A versão anterior não tem nome, porque produção usava uma tag que muda. A imagem foi removida pela limpeza do servidor ou pela política de retenção do registry. O passo a passo existe só na cabeça de quem fez o deploy, e essa pessoa está de folga. A configuração também mudou junto com o código, e ninguém lembrou de voltar a variável.

Nenhum desses problemas aparece num ensaio que nunca foi feito.

Como montar um rollback que funciona

  1. Escreva o plano de volta junto com a release. Uma frase verdadeira sobre como desfazer, incluindo dado e configuração.
  2. Use versões com nome. Cada imagem tem uma tag única, e o deploy registra qual estava no ar antes. Veja por que latest não é uma versão.
  3. Mantenha a anterior disponível. A retenção do registry e a limpeza dos servidores preservam pelo menos as últimas versões que estiveram em produção.
  4. Faça a mudança de banco compatível com o código anterior. Adicionar antes, remover depois, em releases separadas. O padrão está em migração em dois passos.
  5. Defina o critério de volta antes do deploy. Por exemplo: taxa de erro acima de 2% por cinco minutos, ou checkout falhando no teste de fumaça. Critério decidido na hora vira discussão.
  6. Ensaie e meça. Faça a volta em homologação, cronometre, e use esse tempo no anúncio da janela.

Um modelo de plano de volta

Release: api 1.5.0
Versão anterior: api 1.4.2
Banco: adiciona a coluna clientes.nome_completo (nullable). A 1.4.2 ignora a coluna.
Configuração: nova variável FRETE_URL. A 1.4.2 não a lê; pode ficar.
Critério de volta: erro 5xx acima de 2% por 5 min ou falha no teste de checkout.
Como voltar: ./rollback.sh (troca a tag para 1.4.2 e recria o serviço api).
Dado gravado na 1.5.0 que se perde na volta: nenhum.
Tempo de volta medido em homologação: 1 min 40 s.
Responsável: plantão da semana.

A linha mais importante é a do dado. Se a resposta for "pedidos criados com o novo campo de frete ficam sem esse dado", tudo bem, desde que esteja escrito e aceito antes. Se a resposta for "não sei", a release ainda não está pronta.

Scripts de deploy e de volta

Com Compose, a tag da imagem fica num arquivo .env ao lado do compose.yaml, com uma linha API_TAG=1.4.2, e o serviço usa image: registry.exemplo.com.br/loja/api:${API_TAG}. O deploy guarda a versão que estava no ar antes de trocar:

#!/bin/sh
set -eu
cd /srv/loja
NOVA="$1"
ATUAL=$(grep '^API_TAG=' .env | cut -d= -f2)
echo "$ATUAL" > versao-anterior
sed -i "s/^API_TAG=.*/API_TAG=$NOVA/" .env
docker compose pull api
docker compose up -d api

A volta lê esse registro, troca a tag e confere a versão que respondeu:

#!/bin/sh
set -eu
cd /srv/loja
ANTERIOR=$(cat versao-anterior)
sed -i "s/^API_TAG=.*/API_TAG=$ANTERIOR/" .env
docker compose up -d api
sleep 10
curl -fsS http://127.0.0.1:8080/versao

O curl no final é a prova. Se a rota devolve a versão anterior, a volta terminou. Se devolve erro, você sabe na hora, não pelo cliente. Em pipelines com ambientes, como os deployments do GitHub Actions, o equivalente é reexecutar o deploy da versão anterior, que precisa estar disponível como artefato.

Quando a mudança de banco não volta

Algumas mudanças são irreversíveis por natureza: apagar coluna, converter dado com perda, juntar tabelas. Para essas, a regra é separar. A release que muda o código vai primeiro e fica estável por um tempo combinado. A mudança destrutiva vai depois, sozinha, com backup feito imediatamente antes e uma restauração já testada, como em backup que restaura. Meça quanto tempo a restauração leva: num banco grande, ela pode ser a maior parte do tempo de volta.

Outra ferramenta é a flag de funcionalidade. A release sobe com o comportamento novo desligado, e você liga aos poucos. Se der problema, desligar a flag é mais rápido que qualquer rollback de imagem, e não mexe no banco.

Como ensaiar sem drama

Escolha um ambiente com o mesmo processo de deploy de produção e um banco com volume parecido. Faça o deploy da release nova, rode a migração, gere alguns registros e execute o plano de volta exatamente como escrito, sem atalhos. Cronometre do momento da decisão até o teste de fumaça passar. Anote tudo o que precisou improvisar: cada improviso é uma linha que falta no plano. Repita a cada release que mexe em banco ou configuração, e pelo menos uma vez por trimestre nas outras.

Erros comuns

  • Migração destrutiva na mesma release do código. A volta do código encontra um banco que ele não entende, e o único caminho é restaurar backup perdendo dado.
  • Não registrar a versão anterior. Na hora da pressão, alguém procura a tag certa no histórico do registry e escolhe errado.
  • Limpeza agressiva de imagens. Um docker image prune -a agendado ou uma retenção curta no registry apagam justamente a imagem para onde você queria voltar.
  • Esquecer a configuração. A imagem volta, mas a variável nova continua apontando para um serviço que a versão anterior não sabe usar.
  • Critério de volta decidido no incidente. Meia hora de discussão sobre "se está ruim o suficiente" com o checkout fora do ar.
  • Nunca ter ensaiado. O primeiro rollback acontece numa sexta à noite, com o script quebrado e ninguém sabendo.

Checklist

  • A última release tem um plano de volta escrito, com a linha sobre dado preenchida.
  • A versão anterior tem tag única e existe no registry e no servidor.
  • O deploy registra automaticamente qual versão estava no ar.
  • A migração da release é compatível com o código anterior.
  • O critério de volta está definido antes do deploy.
  • O script de volta foi executado em homologação, com o tempo medido.
  • A restauração do backup foi testada e cronometrada.

Abra a última release que foi para produção. Ela tem uma frase verdadeira sobre como desfazê-la, incluindo o dado?

Avalie este artigo

Referência: https://docs.github.com/en/actions/deployment/about-deployments