Programação Por Equipe DevBras

Capa do artigo: Passou no staging e travou em produção: como aproximar os dois ambientes

Passou no staging e travou em produção: como aproximar os dois ambientes

Descubra quais diferenças entre teste e produção escondem bugs, iguale versões, HTTPS, fuso e volume de dados sem copiar dado pessoal, e documente o que pode continuar diferente.

O relatório de pedidos abria em 0,2 segundo no staging. Em produção, na segunda de manhã, levou 40 segundos e derrubou o painel junto. O código era o mesmo. A diferença: o banco de staging tinha 5 mil pedidos, o de produção tinha 3 milhões, e a consulta fazia leitura sequencial da tabela inteira. O teste passou porque testou um primo do sistema, não o sistema.

A resposta curta: o staging precisa ser igual à produção em tudo que muda comportamento: versão principal de runtime e de banco, sistema operacional ou imagem, HTTPS e proxy, fuso horário, collation, conjunto de configurações e ordem de grandeza dos dados. Tamanho das máquinas, dados pessoais reais e integrações de terceiros podem e devem ser diferentes, desde que a diferença seja escrita e consciente.

Por que o staging se afasta da produção

Ninguém cria um staging diferente de propósito. Ele nasce parecido e vai se afastando. A produção recebe uma atualização de banco numa janela de manutenção, e ninguém lembra do staging. Alguém desliga o HTTPS no teste porque o certificado venceu. O cookie de sessão marcado como seguro para de funcionar em HTTP, então alguém desliga a flag "só no staging". Seis meses depois, o staging testa uma configuração que não existe em lugar nenhum.

O texto do Twelve-Factor App sobre paridade entre desenvolvimento e produção resume em três distâncias: de tempo (o código demora para chegar à produção), de pessoas (quem escreve não é quem publica) e de ferramentas (banco, fila e servidor diferentes). A terceira é a que mais engana, porque é invisível no código. O mesmo SQL roda em dois bancos e se comporta de dois jeitos.

Há uma pista boa para saber se você tem esse problema: se manter o staging parecido dá trabalho, é porque a produção é montada à mão. Ambiente descrito em código (imagens, compose, infraestrutura como código) fica parecido de graça, porque é o mesmo arquivo com outras variáveis.

O que precisa ser igual e o que pode ser diferente

Igual, porque muda comportamento

  • Versão principal do runtime (Node, .NET, PHP, Java) e do banco.
  • Imagem de container ou versão do sistema operacional.
  • Proxy reverso e HTTPS, com os mesmos cabeçalhos e redirecionamentos.
  • Fuso horário do servidor e do banco, locale e collation.
  • O mesmo conjunto de chaves de configuração, com valores próprios.
  • O mesmo caminho de migração do banco, aplicado do mesmo jeito.
  • Ordem de grandeza dos dados nas tabelas que crescem.

Diferente, de propósito

  • Tamanho e quantidade de máquinas, desde que o teste de carga leve isso em conta.
  • Dados pessoais: staging não recebe cópia de cliente real.
  • Gateways de pagamento, envio de e-mail e SMS: usam o ambiente de testes do fornecedor ou um destino que não alcança pessoas reais.

Como aproximar o staging, passo a passo

  1. Compare as versões dos dois ambientes com um script, não de memória.
  2. Fixe as versões no mesmo arquivo. Se a produção roda postgres:16.4, o staging também, nunca latest. O porquê está em a tag latest mente.
  3. Ligue HTTPS no staging com certificado válido, e reative toda flag de segurança que foi desligada "só no teste".
  4. Gere volume de dados sintético parecido com o da produção, em vez de copiar a base.
  5. Rode as migrações pelo mesmo pipeline, na mesma ordem em que vão rodar em produção.
  6. Escreva a lista de diferenças conscientes no repositório, ao lado da configuração.

Script para comparar os dois ambientes

#!/usr/bin/env bash
# staging e producao são aliases definidos no ~/.ssh/config
for amb in staging producao; do
  echo "== $amb"
  ssh "$amb" '
    node --version
    psql -tAc "show server_version" loja
    psql -tAc "show timezone" loja
    date +%Z
    grep VERSION_ID /etc/os-release
  '
done

Na loja, a saída mostrou duas diferenças que ninguém sabia: o banco de staging estava em UTC e o de produção em America/Sao_Paulo, e o Node do staging estava uma versão principal atrás. A primeira explicava um bug antigo de pedidos feitos depois das 21h aparecendo no dia seguinte no relatório de teste. Fuso horário é um assunto à parte, tratado em fuso horário no PHP.

Volume parecido, sem dado pessoal

Copiar a base de produção para o staging resolve o volume e cria outro problema: dado pessoal de cliente num ambiente com menos controle de acesso. Gerar dados sintéticos é mais seguro e, no PostgreSQL, rápido:

INSERT INTO pedidos (cliente_id, criado_em, total_centavos)
SELECT
  (random() * 50000)::int + 1,
  now() - (random() * interval '730 days'),
  (random() * 100000)::int
FROM generate_series(1, 3000000);

ANALYZE pedidos;

Com três milhões de linhas, o mesmo relatório que abria em 0,2 segundo passou a demorar no staging também, e o EXPLAIN mostrou o problema antes de chegar ao cliente:

EXPLAIN (ANALYZE, BUFFERS)
SELECT id, total_centavos FROM pedidos
WHERE criado_em >= now() - interval '30 days'
ORDER BY criado_em DESC
LIMIT 50;

-- Antes: Seq Scan on pedidos
CREATE INDEX CONCURRENTLY idx_pedidos_criado_em ON pedidos (criado_em);
-- Depois: Index Scan Backward using idx_pedidos_criado_em

Se o índice for criado em produção numa tabela grande, faça a mudança de esquema com o cuidado descrito em migração em dois passos.

A lista de diferenças conscientes

Um arquivo curto no repositório, revisado a cada mudança de infraestrutura:

# docs/staging-vs-producao.md
- Máquinas: staging 2 vCPU, produção 8 vCPU (teste de carga não vale no staging)
- Dados: sintéticos, gerados por scripts/seed-staging.sql
- Pagamento: ambiente de testes do gateway
- E-mail: todo envio vai para a caixa de captura do time
- Igual em tudo o resto (versões fixadas em compose.yaml)

Erros comuns

  • Staging com poucos dados. Consulta sem índice, paginação lenta e relatório pesado passam no teste e travam em produção.
  • Desligar segurança "só no staging". Cookie seguro, HTTPS e validação de certificado desligados escondem exatamente os bugs que aparecem quando estão ligados.
  • Copiar a base de produção. Dado pessoal de cliente vai para um ambiente com acesso amplo, e um vazamento no staging é um vazamento de verdade.
  • Versão solta. Imagem com latest no staging e versão fixa em produção faz cada ambiente testar uma coisa diferente sem ninguém perceber.
  • Mock no lugar do serviço real sem nenhum teste contra o sandbox do fornecedor. O teste fica verde e a integração quebra, como mostra mock que mente.
  • Migração aplicada à mão no staging. Em produção ela roda pelo pipeline, em outra ordem, e falha no meio.

Checklist

  • Um script compara versão de runtime, banco, sistema e fuso nos dois ambientes.
  • As versões estão fixadas no mesmo arquivo, sem latest.
  • Staging tem HTTPS válido e as mesmas flags de segurança da produção.
  • As tabelas que crescem têm volume sintético na mesma ordem de grandeza.
  • Nenhum dado pessoal real foi copiado para o staging.
  • Migrações chegam ao staging pelo mesmo pipeline da produção.
  • Existe uma lista escrita das diferenças conscientes, com o motivo de cada uma.

Rode hoje o mesmo comando de versão no staging e na produção e compare a saída: qual diferença apareceu que ninguém tinha escrito, e qual delas já causou um susto numa segunda-feira?

Avalie este artigo

Referência: https://12factor.net/dev-prod-parity