Por que usuários de staging nunca chegam à produção: ambientes isolados para auth

Ambientes na Nulvless são armazenamentos separados, não uma coluna. Veja o que isso impede e o que passa de um ambiente para outro.

Gleisson MatosFundador e CEO4 min de leitura
Nesta página

A maioria dos produtos modela ambientes como uma coluna. Usuários, sessões e configurações ficam nas mesmas tabelas com um environment_id ao lado, e cada consulta lembra de filtrar por ele. Isso funciona até uma consulta esquecer.

Na Nulvless, um ambiente é uma cópia completa do projeto, com seus próprios usuários, sessões, identidades, provedores, templates e chaves. Este post explica por que escolhemos isso, quanto custa e o que passa entre ambientes no lugar dos dados.

Uma coluna é uma promessa que toda consulta precisa cumprir

Quando os ambientes dividem o armazenamento, o isolamento é uma propriedade do seu código, não dos seus dados. Um filtro faltando em um script de suporte, um endpoint de admin que faz join pela chave errada ou um cache indexado só pelo id do usuário bastam para mostrar uma conta de teste em produção ou mandar para um cliente real um e-mail vindo de staging.

Esses bugs são raros, e é isso que os torna caros. Eles não aparecem nos testes, porque os testes rodam em um ambiente por vez.

Um armazenamento separado torna o bug impossível

Cada ambiente responde na sua própria URL de emissor:

organization
└── project · acme
    ├── environment · main      → acme.nulvless.co
    ├── environment · staging   → acme-staging.nulvless.co
    └── environment · develop   → acme-develop.nulvless.co

Um usuário criado em develop não existe em produção, e nunca vai existir, porque nunca foi gravado naquele armazenamento. Não há filtro para esquecer.

A URL do emissor também é o primeiro argumento de createClient, então apontar um app para outro ambiente é trocar uma URL e uma chave. As chaves também são por ambiente: uma chave publicável de staging não é aceita por produção.

O que passa: configuração, como diff

Dados nunca passam entre ambientes. Configuração passa, mas só quando você promove, e uma promoção começa como um diff, não como uma escrita.

A tela Compare recebe uma origem e um destino e lista cada configuração que difere: regras de cadastro, métodos de login, política de senha, MFA, tokens, provedores sociais, modo sudo, proteção contra ataques, o OAuth server, rate limits e notificações. Cada linha mostra o valor atual do destino e o valor que está chegando. Ler essa lista é a revisão.

Por baixo, o diff tem duas seções, auth e communications, cada uma uma lista de field, before e after. O agrupamento que você vê no console é só apresentação. A unidade que passa é um campo de configuração.

O que uma promoção deixa para trás de propósito

Algumas coisas nunca vão junto em um merge, e nenhuma delas é erro:

  • Credenciais e segredos. Chaves de API de provedores, senhas de SMTP e segredos de clientes OAuth são configurados à mão no destino. Uma chave de staging nunca deve virar chave de produção por acidente.
  • Usuários, sessões e identidades. A promoção move configuração, nunca dados.
  • Edge functions. Uma função pertence ao projeto, não a um ambiente, então não tem lugar em um diff de ambiente.

A primeira é a que mais surpreende. Um provedor pode ser promovido como ativo enquanto as credenciais ainda faltam no destino, e aí ele falha na hora de entregar, não na hora do merge. Por isso a tela Compare avisa, e por isso o fluxo recomendado configura as credenciais uma vez, antes da primeira promoção que precisar delas.

Aplicando

O merge é protegido pelo modo sudo: ao confirmar, a pessoa precisa provar que ainda está no teclado, e essa confirmação vale por uma hora, então uma sequência de promoções pergunta uma vez só.

Depois o merge roda como um pipeline e gera um deployment, com cada etapa cronometrada: inicialização, autenticação, comunicações. Uma execução típica termina em cerca de três segundos.

Um merge não pode ser desfeito, então o histórico importa. Cada merge registra quem pediu, um log de eventos com horário e as linhas exatas de antes e depois que foram revisadas. Meses depois, esse registro responde "quem mudou isso, e qual era o valor antes?" sem que ninguém precise ter anotado.

O custo, e por que aceitamos

Armazenamentos separados não são de graça. Um ambiente novo começa vazio: sem usuários, sem provedores, sem nada da configuração que você montou em outro lugar. Essa é a ideia, e também a primeira surpresa da maioria das equipes.

Achamos que essa troca é a certa para identidade. Uma tabela compartilhada economiza alguns cliques na configuração e deixa uma classe de incidente aberta para sempre. Um armazenamento separado custa uma promoção deliberada, e em troca o pior bug de ambiente vira um que não pode acontecer.

O formato que recomendamos é simples: mude as coisas em develop, compare com staging e leia o diff, e promova para main por último. Se o diff ficar grande, promova com mais frequência. O guia de ambientes cobre cada etapa em detalhe.

Continue lendo

Engenharia5 min de leitura

Apresentando as Edge Functions na Nulvless Network

O que são edge functions, como executam seu TypeScript na região mais próxima de cada usuário e quando usar no lugar do serverless de uma região só.

Gleisson Matos