“Sobe em homologação primeiro.” “Isso já tá em prod?” “Testou no TEST?” Todo mundo falava desse jeito, como se fosse óbvio. E é óbvio, depois que você sabe. Antes disso é um monte de sigla.

O que me travava não era o conceito. Eu entendia, mais ou menos, que existia um servidor pra brincar e um servidor de verdade. O que eu não conseguia enxergar era o que isso significava dentro do código. Eu guardo duas cópias do arquivo? Duas branches? Troco o endereço na mão antes de subir? (Spoiler: fiz os três, em momentos diferentes.)

Então vamos pela versão que ninguém me deu:

  • DEV é a sua máquina. Quebra à vontade.
  • TEST é onde a suíte de testes roda. Banco descartável, dado inventado, nasce e morre a cada execução. Quem usa o TEST é a máquina.
  • HML, homologação, é a cópia mais parecida possível com a produção, onde gente valida antes de liberar. É uma sigla bem brasileira; lá fora chamam de staging, ou UAT. Mesmo bicho.
  • PRD é onde estão os seus clientes.
Caminho de uma mudança
DEV

Sua máquina

Só você vê o que acontece aqui.

TEST

A suíte de testes

Banco descartável, dado inventado, rodado por máquina.

HML

Homologação

Cópia da produção, validada por gente.

PRD

Produção

Onde estão os seus clientes.

O caminho feliz. A mudança passa nos testes, o time valida em homologação e vai pro ar num horário combinado. É o que acontece na maioria das vezes, e ninguém comenta.

A automação entrou em loop e disparou e-mail sem parar. Aqui isso custa uma esteira vermelha e vinte minutos. Sem o TEST, custa a caixa de entrada de todos os seus clientes.

O código está certo, a regra é que está errada. Um campo obrigatório novo trava o formulário pra metade dos clientes, e só o gestor de atendimento saberia disso. Ele viu numa terça-feira, não na noite do go-live.

Clique em um cenário. O TEST pega o que a máquina consegue provar; o HML pega o que só uma pessoa percebe.

Vinte scripts em R e um caminho de arquivo

Antes de entender qualquer uma dessas siglas, eu já tinha pagado caro por elas.

Meu primeiro projeto sério foi um ETL escrito em R. Uns vinte arquivos de script, cada um puxando planilha, tratando, cruzando e cuspindo resultado. Funcionava. Rodava lindo. Na minha máquina.

Aí eu precisei rodar em outra máquina.

O caminho dos arquivos estava cravado no meio do código. Não num lugar, em vinte. C:/Users/leandro/Documents/dados/... espalhado por funções que eu tinha escrito meses antes e já nem lembrava direito. Passei o dia no Ctrl+F trocando um por um. Rodava, quebrava, achava mais um. Trocava, rodava, quebrava de novo. O pior estava dentro de uma função que só era chamada no fim do processo, então o erro aparecia quarenta minutos depois de começar. Quarenta minutos pra descobrir que faltava uma barra.

Nada disso era culpa do R. Era eu misturando duas coisas que não têm nada a ver uma com a outra: o que o programa faz, e onde ele está rodando.

O Programador Pragmático

Alguns anos depois li O Programador Pragmático, do Hunt e do Thomas, e cheguei na parte sobre configuração. O argumento é simples: tudo que pode mudar sem que a lógica mude não deveria morar dentro da lógica. Caminho de arquivo, credencial, URL, porta, número de tentativas, timeout.

Do jeito que eu li, a régua é essa: se você precisa mexer no código pra rodar o programa em outra máquina, tem coisa guardada no lugar errado.

Fazia todo sentido. O engraçado é que eu já tinha aprendido isso na marra, com juros, anos antes. Ler foi só colocar nome no perrengue.

Só que saber o porquê não me deu o como. Minha primeira tentativa foi a que todo mundo faz:

if ENV == "HML":
    BASE_URL = "https://hml.api.empresa.com"
    DB_HOST  = "servidor-hml"
    TIMEOUT  = 60
elif ENV == "PRD":
    BASE_URL = "https://api.empresa.com"
    DB_HOST  = "servidor-prod"
    TIMEOUT  = 10

É melhor que caminho cravado, sem dúvida. Mas envelhece mal. Cada configuração nova é mais um if. E os ifs não ficam quietos num arquivo só, eles vazam. Seis meses depois tem um if ENV == "PRD" no meio da função que dispara e-mail, e você só descobre porque um cliente de verdade recebeu uma mensagem de teste.

O padrão do Miguel Grinberg

O que fez a ficha cair de vez foi o livro do Miguel Grinberg sobre Flask. No capítulo de configuração ele faz uma coisa simples e bonita: cada ambiente vira uma classe.

class Config:
    SECRET_KEY = os.environ.get("SECRET_KEY")
    TIMEOUT = 30

    @staticmethod
    def init_app(app):
        pass


class DevelopmentConfig(Config):
    DEBUG = True
    BASE_URL = "http://localhost:8000"
    DATABASE_URL = os.environ.get("DEV_DATABASE_URL")


class TestingConfig(Config):
    TESTING = True
    BASE_URL = "http://localhost:8000"
    DATABASE_URL = "sqlite://"


class StagingConfig(Config):
    BASE_URL = "https://hml.api.empresa.com"
    DATABASE_URL = os.environ.get("HML_DATABASE_URL")
    TIMEOUT = 60


class ProductionConfig(Config):
    BASE_URL = "https://api.empresa.com"
    DATABASE_URL = os.environ.get("DATABASE_URL")
    TIMEOUT = 10


config = {
    "development": DevelopmentConfig,
    "testing": TestingConfig,
    "staging": StagingConfig,
    "production": ProductionConfig,
    "default": DevelopmentConfig,
}

E na hora de subir a aplicação, uma linha:

app.config.from_object(config[os.environ.get("APP_ENV", "default")])

A resposta pra pergunta “o que muda entre os ambientes?” cabe num arquivo só. Você abre e vê. Não precisa procurar.

A herança faz o trabalho chato. O que é igual mora na classe base e ninguém repete. O que é diferente é sobrescrito, e só o que é diferente aparece na classe filha. Se TIMEOUT não está escrito no DevelopmentConfig, é porque vale o da base, e esse silêncio também é informação.

O resto do código nunca pergunta em que ambiente está. Ele lê app.config["TIMEOUT"] e segue a vida. O if virou uma busca em dicionário e sumiu sem ninguém precisar de disciplina. Ambiente novo é uma classe nova e mais uma linha no dicionário.

E o segredo nunca mora no arquivo. os.environ.get significa que o arquivo descreve o formato e a máquina preenche o valor. Assim dá pra commitar o arquivo sem commitar a senha.

Sigo esse padrão em todo projeto desde então. A linguagem muda, a ideia não. Em TypeScript vira um objeto por ambiente e um tipo que garante que todos carregam as mesmas chaves. Em .NET já vem pronto, com appsettings.Production.json sobrescrevendo o appsettings.json, que é a mesma herança com outro nome.

Vale mais um aviso do mesmo livro, que eu já escrevi por aqui: não cole seu código nos IDs do ambiente em que ele roda. Sandbox e produção costumam ter os mesmos campos, mas cada campo carrega um ID diferente em cada lugar. Deixa o código descobrir esses IDs sozinho.

Isso não é só sobre código

Hoje boa parte do meu trabalho é CRM. E ali a mesma história aparece, só que com preço na etiqueta.

Aviso honesto antes: a Red Lotus é parceira Freshworks, então leia a recomendação sabendo disso.

A maioria dos fornecedores de CRM trata o sandbox como recurso premium, liberado só no plano Enterprise. E essa é uma das poucas coisas sobre as quais eu tenho opinião sólida: empresa que leva qualidade a sério e não quer atrapalhar o cliente deveria ir de Enterprise. O sandbox sozinho já paga a diferença.

Três motivos, e nenhum deles é técnico.

Velocidade. Quando o único lugar pra construir é a produção, toda mudança vira negociação. O time começa a evitar mexer, e o CRM para de evoluir. Esse é um problema muito mais caro que a diferença entre os planos, e demora anos até alguém perceber que está pagando por ele.

Bug antes do usuário. Uma automação que entra em loop e dispara e-mail sem parar é engraçada no sandbox. Na produção ela é um pedido de desculpas pra base inteira e um dia de fila.

Governança. Antes de qualquer coisa ir pro ar, os stakeholders clicam. O gestor de atendimento vê o formulário novo, o pessoal de compliance vê onde o campo cai, e a objeção aparece com tempo de sobra pra ajustar.

Agora a parte que o vendedor não conta.

Sandbox não é clone. Ele não vem com os seus dados de produção, e na maioria das ferramentas ele não sincroniza sozinho de volta. Você promove a configuração na mão, ou usa um “sandbox sync” que cobre alguns tipos de objeto e ignora outros caladinho. Peça essa lista antes de prometer prazo pra alguém.

As integrações ficam de fora. O webhook, o middleware, a API do ERP: cada um precisa do próprio ambiente de teste, e tem fornecedor que simplesmente não tem. Aí você acaba testando contra a produção do sistema de outra pessoa, que é exatamente o que você estava tentando evitar.

E o preço é real. Enterprise não é um degrauzinho, e se você quiser os números eu destrinchei plano por plano do Freshdesk Omni. Se vocês são quatro pessoas com uma fila de e-mail, o “não” é honesto. O argumento vale quando existe algo pra quebrar: volume, integração, processo do qual gente depende.

Ainda assim tem rollback

Não seja ingênuo. Mesmo com quatro ambientes, esteira verde, homologação assinada e go-live marcado, de vez em quando um deploy tem rollback. Alguma coisa que só aparece com volume de verdade e um usuário fazendo o que nenhum plano de teste previu.

A diferença é que isso passa a ser raro, e quando acontece ninguém está descobrindo o procedimento na hora. Existe versão anterior, existe plano, alguém aperta o botão. O cliente pega meia hora ruim e a coisa acaba ali.

Meu ETL em R nunca teve homologação nenhuma. Se eu voltasse hoje, começaria pelo arquivo de configuração antes de escrever o primeiro script, e ainda assim ia quebrar em algum lugar que eu não previ. É a natureza do software.