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
- Compare as versões dos dois ambientes com um script, não de memória.
- Fixe as versões no mesmo arquivo. Se a produção roda
postgres:16.4, o staging também, nuncalatest. O porquê está em a tag latest mente. - Ligue HTTPS no staging com certificado válido, e reative toda flag de segurança que foi desligada "só no teste".
- Gere volume de dados sintético parecido com o da produção, em vez de copiar a base.
- Rode as migrações pelo mesmo pipeline, na mesma ordem em que vão rodar em produção.
- 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
latestno 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?
Referência: https://12factor.net/dev-prod-parity