Variáveis de ambiente permitem configurar aplicativos sem alterar o código. Elas separam os dados externos da lógica do aplicativo, o que pode permanecer bastante enigmático para desenvolvedores iniciantes (e até para alguns experientes).
Através deste guia prático, levantaremos o véu sobre as variáveis de ambiente para que você possa entender o que elas implicam, por que são importantes e como aproveitar as variáveis de ambiente com confiança.
Pegue sua bebida favorita (e talvez alguns biscoitos) porque vamos começar. Vamos desempacotar os conceitos de variáveis ambientais desde o início.
O que São Variáveis de Ambiente?
Variáveis de ambiente são valores nomeados dinâmicos que podem afetar o comportamento de processos em execução em um computador. Algumas propriedades chave das variáveis de ambiente são:
- Nomeadas: Tenha nomes de variáveis descritivos como APP_MODE e DB_URL.
- Externas: Os valores são definidos fora do código do aplicativo por meio de arquivos, linhas de comando e sistemas.
- Dinâmicas: Podem atualizar variáveis sem reiniciar aplicativos.
- Configuradas: O código depende de variáveis, mas não as define.
- Desacopladas: Não é necessário alterar configurações do código uma vez que as variáveis estão definidas.
Aqui está uma analogia. Imagine que você está seguindo uma receita de cookies de chocolate. A receita pode dizer:
- Adicione 1 xícara de açúcar
- Adicione 1 barra de manteiga amolecida
- Adicione 2 ovos
Em vez desses valores codificados, você poderia usar variáveis de ambiente:
- Adicione $SUGAR xícara de açúcar
- Adicione $BUTTER barras de manteiga amolecida
- Adicione $EGGS ovos
Antes de fazer os cookies, você definiria os nomes das variáveis de ambiente com valores de sua escolha:
SUGAR=1
BUTTER=1
EGGS=2
Então, ao seguir a receita, seus ingredientes seriam:
- Adicione 1 xícara de açúcar
- Adicione 1 barra de manteiga amolecida
- Adicione 2 ovos
Isso permite que você configure a receita do cookie sem alterar o código da receita.
O mesmo conceito se aplica à computação e desenvolvimento. Variáveis de ambiente permitem que você altere o ambiente no qual um processo é executado sem mudar o código subjacente. Aqui estão alguns exemplos comuns:
- Configurando o ambiente para “development” ou “production”
- Configurando chaves API para serviços externos
- Inserindo chaves secretas ou credenciais
- Alternando certas funcionalidades entre ativo e inativo
As variáveis de ambiente oferecem grande flexibilidade. Você pode implantar o mesmo código em vários ambientes sem alterar o código em si. Mas vamos entender melhor por que elas são valiosas.
Por que as Variáveis de Ambiente são Valiosas?
Considere as variáveis de ambiente como botões de aplicativos usados para ajustar preferências. Exploraremos casos de uso excelentes em breve.
Vamos consolidar a intuição sobre por que as variáveis de ambiente são importantes!
Razão #1: Eles Separam o Código da Aplicação das Configurações
Codificar configurações e credenciais diretamente no seu código pode causar todo tipo de problemas:
- Commits acidentais no controle de fonte
- Reconstrução e redespacho de código apenas para alterar um valor
- Problemas de configuração ao promover entre ambientes
Isso também leva a um código confuso:
import os
# Configuração codificada
DB_USER = 'appuser'
DB_PASS = 'password123'
DB_HOST = 'localhost'
DB_NAME = 'myappdb'
def connect_to_db():
print(f"Conectando a {DB_USER}:{DB_PASS}@{DB_HOST}/{DB_NAME}")
connect_to_db()
Isso entrelaça a lógica de negócios com detalhes de configuração. O acoplamento apertado torna a manutenção árdua ao longo do tempo:
- As mudanças requerem a modificação do código-fonte
- Risco de vazar segredos no controle de fonte
Usar variáveis de ambiente reduz esses problemas. Por exemplo, você pode definir as variáveis de ambiente DB_USER e DB_NAME.
# Arquivo .env
DB_USER=appuser
DB_PASS=password123
DB_HOST=localhost
DB_NAME=myappdb
O código da aplicação pode acessar as variáveis de ambiente sempre que necessário, mantendo o código limpo e simples.
import os
# Carregar configuração do ambiente
DB_USER = os.environ['DB_USER']
DB_PASS = os.environ['DB_PASS']
DB_HOST = os.environ['DB_HOST']
DB_NAME = os.environ['DB_NAME']
def connect_to_db():
print(f"Conectando a {DB_USER}:{DB_PASS}@{DB_HOST}/{DB_NAME}")
connect_to_db()
As variáveis de ambiente separam claramente a configuração do código, mantendo os valores sensíveis abstraídos no ambiente.
Você pode implementar o mesmo código do desenvolvimento para a produção sem alterar nada. As variáveis de ambiente podem variar entre os ambientes sem impactar o código de forma alguma.
Razão #2: Eles Simplificam a Configuração de Aplicações
Variáveis de ambiente simplificam ajustes nas configurações sem alterar o código:
# Arquivo .env:
DEBUG=true
Aqui está como podemos usá-lo dentro do arquivo de script:
# Conteúdo do Script:
import os
DEBUG = os.environ.get('DEBUG') == 'true'
if DEBUG:
print("No modo DEBUG")
Alternar o modo de depuração requer apenas a atualização do arquivo .env — não são necessárias alterações de código, reconstrução ou redespliegue. “Env vars”, abreviatura de variáveis de ambiente, também ajudam a implantar em diferentes ambientes de forma contínua:
import os
# Recupera a variável de ambiente para determinar o ambiente atual (produção ou staging)
current_env = os.getenv('APP_ENV', 'staging') # Padrão para 'staging' se não estiver definido
# Chave da API de Produção
PROD_API_KEY = os.environ['PROD_API_KEY']
# Chave da API de Staging
STG_API_KEY = os.environ['STG_API_KEY']
# Lógica que define api_key baseada no ambiente atual
if current_env == 'production':
api_key = PROD_API_KEY
else:
api_key = STG_API_KEY
# Inicializa o cliente API com a chave API apropriada
api = ApiClient(api_key)
O mesmo código pode usar chaves de API separadas para produção vs staging sem quaisquer alterações.
E, por último, eles ativam alternâncias de funcionalidades sem novas implementações:
NOVO_RECURSO = os.environ['NOVO_RECURSO'] == 'true'
if NOVO_RECURSO:
habilitarNovoRecurso()
Alterar a variável NEW_FEATURE ativa a funcionalidade instantaneamente dentro do nosso código. A interface para atualizar configurações depende dos sistemas:
- Plataformas de Cloud como Heroku usam painéis web
- Servidores usam ferramentas de comando de OS
- Desenvolvimento local pode usar arquivos .env
As variáveis de ambiente são benéficas ao criar aplicações, permitindo que os usuários configurem elementos de acordo com suas necessidades.
Razão #3: Eles ajudam a gerenciar segredos e credenciais
Armazenar segredos como chaves de API, senhas e chaves privadas diretamente no código-fonte apresenta riscos de segurança substanciais:
# Evite expor segredos no código!
STRIPE_KEY = 'sk_live_1234abc'
DB_PASSWORD = 'password123'
stripe.api_key = STRIPE_KEY
db.connect(DB_PASSWORD)
Essas credenciais agora estão expostas se esse código for incluído em um repositório público GitHub!
Variáveis de ambiente impedem vazamentos ao externalizar segredos:
import os
STRIPE_KEY = os.environ.get('STRIPE_KEY')
DB_PASS = os.environ.get('DB_PASS')
stripe.api_key = STRIPE_KEY
db.connect(DB_PASS)
Os valores secretos reais são definidos em um arquivo local .env.
# .env file
STRIPE_KEY=sk_live_1234abc
DB_PASS=password123
Não se esqueça de .gitignore
o arquivo .env para manter segredos fora do controle de fonte. Isso envolve definir o arquivo .env em um arquivo .gitignore na raiz de qualquer repositório, o que indica ao git para ignorar o arquivo durante a criação de commits.
Isso separa as definições secretas do código da aplicação, carregando-as de forma segura a partir de ambientes protegidos durante a execução. O risco de expor acidentalmente as credenciais reduz-se drasticamente.
Razão #4: Eles Promovem Consistência
Imagine ter diferentes arquivos de configuração para os ambientes de desenvolvimento, QA e produção:
# Desenvolvimento
DB_HOST = 'localhost'
DB_NAME = 'appdb_dev'
# Produção
DB_HOST = 'db.myapp.com'
DB_NAME = 'appdb_prod'
Essa discrepância introduz bugs sutis que são difíceis de detectar. Código que funciona perfeitamente em desenvolvimento pode repentinamente quebrar a produção devido a configurações incompatíveis.
Variáveis de ambiente resolvem isso centralizando a configuração em um único local:
DB_HOST=db.myapp.com
DB_NAME=appdb_prod
Agora, as mesmas variáveis são usadas de forma consistente em todos os ambientes. Você não precisa mais se preocupar com configurações aleatórias ou incorretas sendo ativadas.
O código da aplicação simplesmente referencia as variáveis:
import os
db_host = os.environ['DB_HOST']
db_name = os.environ['DB_NAME']
db.connect(db_host, db_name)
Independentemente de a aplicação ser executada localmente ou em um servidor de produção, ela sempre usa o host do banco de dados e o nome corretos.
Essa uniformidade reduz erros, melhora a previsibilidade e torna o aplicativo mais robusto de forma geral. Os desenvolvedores podem ter confiança de que o código se comportará de maneira idêntica em todos os ambientes.
Como Você Pode Definir Variáveis de Ambiente
As variáveis de ambiente podem ser definidas em vários locais, permitindo flexibilidade na configuração e acesso a elas entre processos e sistemas.
1. Variáveis de Ambiente do Sistema Operacional
A maioria dos sistemas operacionais oferece mecanismos integrados para definir variáveis globais. Isso torna as variáveis acessíveis em todo o sistema para todos os usuários, aplicações, etc.
Em sistemas Linux/Unix, variáveis podem ser definidas em scripts de inicialização do Shell.
Por exemplo, ~/.bashrc pode ser usado para definir variáveis de nível de usuário, enquanto /etc/environment é para variáveis de sistema acessíveis a todos os usuários.
As variáveis também podem ser definidas em linha antes de executar comandos usando o comando export ou diretamente através do comando env no bash:
# In ~/.bashrc
export DB_URL=localhost
export APP_PORT=3000
# Em /etc/environment
DB_HOST=localhost
DB_NAME=mydatabase
As variáveis também podem ser definidas inline antes de executar comandos:
export TOKEN=abcdef
python app.py
Definir variáveis no nível do sistema operacional as torna globalmente disponíveis, o que é bastante útil quando você quer executar o aplicativo sem depender de valores internos.
Você também pode referenciar variáveis definidas em scripts ou argumentos de linha de comando.
python app.py --db-name $DB_NAME --db-host $DB_HOST --batch-size $BATCH_SIZE
2. Definindo Variáveis de Ambiente no Código da Aplicação
Além de variáveis de nível de sistema operacional, variáveis de ambiente podem ser definidas e acessadas diretamente no código da aplicação durante a execução.
O dicionário os.environ em Python contém todas as variáveis de ambiente atualmente definidas. Podemos definir novas simplesmente adicionando pares chave-valor:
As variáveis de ambiente também podem ser definidas e acessadas diretamente dentro do código da aplicação. Em Python, o dicionário os.environ contém todas as variáveis de ambiente definidas:
import os
os.environ["API_KEY"] = "123456"
api_key = os.environ.get("API_KEY")
Então, o dicionário os.environ permite a configuração e recuperação dinâmica de variáveis de ambiente dentro do código Python.
A maioria das linguagens vem acompanhada de suas bibliotecas, oferecendo acesso a variáveis de ambiente durante a execução.
Você também pode usar frameworks como Express, Django e Laravel para ter integrações mais profundas, como carregamento automático de arquivos .env contendo variáveis de ambiente.
3. Criando Arquivos de Configuração Local Para Variáveis de Ambiente
Além das variáveis de nível de sistema, as variáveis de ambiente podem ser carregadas a partir dos arquivos de configuração local de uma aplicação. Isso mantém os detalhes de configuração separados do código, mesmo para desenvolvimento e testes locais.
Algumas abordagens populares:
Arquivos .env
O formato de arquivo .env popularizado pelo Node.js oferece uma maneira conveniente de especificar variáveis de ambiente em um formato de chave-valor:
# .env
DB_URL=localhost
API_KEY=123456
Frameworks web como Django e Laravel carregam automaticamente variáveis definidas em arquivos .env para o ambiente da aplicação. Para outras linguagens como Python, bibliotecas como python-dotenv são responsáveis por importar arquivos .env:
from dotenv import load_dotenv
load_dotenv() # Carrega variáveis .env
print(os.environ['DB_URL']) # localhost
A vantagem de usar arquivos .env é que eles mantêm a configuração limpa e separada sem fazer alterações no código.
Arquivos de Configuração JSON
Para necessidades de configuração mais complexas envolvendo múltiplas variáveis de ambiente, usar arquivos JSON ou YAML ajuda a organizar as variáveis juntas:
// config.json
{
"api_url": "https://api.example.com",
"api_key": "123456",
"port": 3000
}
O código da aplicação pode então carregar rapidamente esses dados JSON como um dicionário para acessar variáveis configuradas:
import json
config = json.load('config.json')
api_url = config['api_url']
api_key = config['api_key']
port = config['port'] # 3000
Isso impede arquivos dotenv desorganizados ao lidar com múltiplas configurações de aplicativos.
Como Você Acessa Variáveis de Ambiente em Diferentes Linguagens de Programação?
No entanto, escolhermos definir as variáveis de ambiente, nossas aplicações precisam de uma forma consistente de procurar valores durante a execução.
Embora existam várias maneiras de definir variáveis de ambiente, o código da aplicação precisa de uma forma padrão de acessá-las em tempo de execução, independentemente da linguagem. Aqui está uma visão geral das técnicas para acessar variáveis de ambiente em linguagens populares:
Python
Python fornece o dicionário os.environ para acessar as variáveis de ambiente definidas:
import os
db = os.environ.get('DB_NAME')
print(db)
Podemos obter uma variável usando os.environ.get(), que retorna None se não definido. Ou acesso direto via os.environ(), que irá gerar KeyError se não estiver presente.
Métodos adicionais como os.getenv() e os.environ.get() permitem especificar valores padrão se não estiverem definidos.
JavaScript (Node.js)
Em código JavaScript Node.js, as variáveis de ambiente estão disponíveis no objeto global process.env:
// Obter variável de ambiente
const db = process.env.DB_NAME;
console.log(db);
Se indefinido, process.env conterá indefinido. Também podemos fornecer padrões como:
const db = process.env.DB_NAME || 'defaultdb';
Ruby
Aplicações Ruby acessam variáveis de ambiente através do hash ENV:
# Acesse a variável
db = ENV['DB_NAME']
puts db
Nós também podemos passar um valor padrão se a chave desejada não existir:
db = ENV.fetch('DB_NAME', 'defaultdb')
PHP
PHP fornece métodos globais getenv(), $_ENV e $_SERVER para acessar variáveis de ambiente:
// Obter variável de ambiente
$db_name = getenv('DB_NAME');
// Ou acessar arrays $_ENV ou $_SERVER
$db_name = $_ENV['DB_NAME'];
Dependendo da fonte da variável, elas podem estar disponíveis em diferentes globais.
Java
Em Java, o método System.getenv() retorna variáveis de ambiente que podem ser acessadas:
String dbName = System.getenv("DB_NAME");
Isso permite acesso a variáveis definidas em um nível de sistema globalmente em Java.
Por enquanto, algumas melhores práticas sobre a higiene das variáveis de ambiente.
Guia de Segurança de Variáveis de Ambiente
Quando se trata de gerenciar variáveis de ambiente de forma segura, devemos ter em mente várias melhores práticas.
Nunca Armazene Informações Sensíveis no Código
Antes de mais nada, nunca armazene informações sensíveis como senhas, chaves de API ou tokens diretamente no seu código.
Pode ser tentador simplesmente codificar uma senha de banco de dados ou uma chave de criptografia diretamente no seu código-fonte para acesso rápido, mas resista a essa vontade!
Se você acidentalmente enviar esse código para um repositório público no GitHub, você estará basicamente transmitindo seus segredos para o mundo inteiro. Imagine se um hacker obtivesse as credenciais do seu banco de dados de produção apenas porque elas estavam em texto simples no seu código-fonte. Pensamento assustador, certo?
Em vez disso, sempre use variáveis de ambiente para armazenar qualquer tipo de configuração sensível. Mantenha seus segredos em um local seguro como um arquivo .env ou uma ferramenta de gerenciamento de segredos e referencie-os em seu código através de variáveis de ambiente. Por exemplo, em vez de fazer algo assim no seu código Python:
db_password = "supers3cr3tpassw0rd"
Você armazenaria essa senha em uma variável de ambiente assim:
# Arquivo .env
DB_PASSWORD=supers3cr3tpassw0rd
E depois acesse-o em seu código como:
import os
db_password = os.environ.get('DB_PASSWORD')
Dessa forma, seus segredos ainda estão seguros mesmo que seu código fonte seja comprometido. As variáveis de ambiente atuam como uma camada de abstração segura.
Utilizar Variáveis Específicas do Ambiente
Outra prática é usar diferentes variáveis de ambiente para cada ambiente de aplicação, como desenvolvimento, staging e produção.
Você não vai querer se conectar acidentalmente ao seu banco de dados de produção enquanto desenvolve localmente só porque esqueceu de atualizar uma variável de configuração! Dê namespace às suas variáveis de ambiente para cada ambiente:
# Desenvolvimento
DEV_API_KEY=abc123
DEV_DB_URL=localhost
# Produção
PROD_API_KEY=xyz789
PROD_DB_URL=proddb.amazonaws.com
Em seguida, referencie as variáveis apropriadas no seu código dependendo do ambiente atual. Muitos frameworks como o Rails fornecem arquivos de configuração específicos para cada ambiente para este propósito.
Mantenha Segredos Fora do Controle de Versão
É crucial também manter seus arquivos .env e de configuração que contêm segredos fora do controle de versão. Adicione .env ao seu .gitignore
para não cometer o erro de enviá-lo acidentalmente para o seu repositório.
Você pode usar git-secrets
para verificar a existência de informações sensíveis antes de cada commit. Para segurança extra, criptografe seu arquivo de segredos antes de armazená-lo. Ferramentas como Ansible Vault e BlackBox podem ajudar com isso.
Segredos Seguros em Servidores de Produção
Ao gerenciar variáveis de ambiente em seus servidores de produção, evite configurá-las usando argumentos de linha de comando, que podem ser inspecionados através da tabela de processos.
Em vez disso, utilize as ferramentas de gerenciamento de ambiente do seu sistema operacional ou da plataforma de orquestração de contêineres. Por exemplo, você pode usar Kubernetes Secrets para armazenar e expor segredos de forma segura para os seus pods de aplicação.
Use Algoritmos de Criptografia Fortes
Use algoritmos de criptografia robustos e modernos ao criptografar seus segredos, seja em trânsito ou em repouso. Evite algoritmos desatualizados como DES ou MD5, que possuem vulnerabilidades conhecidas. Em vez disso, opte por algoritmos padrão do setor como AES-256 para criptografia simétrica e RSA-2048 ou ECDSA para criptografia assimétrica.
Rotacione Segredos Regularmente
Rotacione seus segredos regularmente, especialmente se você suspeitar que eles possam ter sido comprometidos. Trate os segredos como você trataria uma senha — atualize-os a cada poucos meses. Uma ferramenta de gerenciamento de segredos como Hashicorp Vault ou AWS Secrets Manager pode ajudar a automatizar esse processo.
Tenha Cuidado com o Registro e Relatório de Erros
Tenha cuidado com os registros e relatórios de erros. Certifique-se de não registrar quaisquer variáveis de ambiente que contenham valores sensíveis. Se estiver usando uma ferramenta de rastreamento de erros de terceiros, configure-a para limpar dados sensíveis. A última coisa que você quer é que seus segredos apareçam em um rastreamento de pilha em um painel de relatórios de exceções!
Quando Evitar Variáveis de Ambiente?
Existem vários casos em que as variáveis de ambiente devem ser evitadas:
Gerenciando Configuração Complexa
Usar variáveis de ambiente para gerenciar configurações de sistemas de software complexos pode se tornar confuso e propenso a erros. À medida que o número de parâmetros de configuração aumenta, você acaba com nomes de variáveis de ambiente longos que podem colidir involuntariamente. Também não há uma maneira fácil de organizar valores de configuração relacionados juntos.
Em vez de variáveis de ambiente, considere usar arquivos de configuração em um formato como JSON ou YAML. Estes permitem que você:
- Agrupe parâmetros de configuração relacionados em uma estrutura aninhada.
- Evite colisões de nomes encapsulando a configuração em escopos e namespaces.
- Defina tipos de dados personalizados em vez de apenas strings.
- Visualize e modifique configurações rapidamente usando um editor de texto.
Armazenando Informações Sensíveis
Embora as variáveis de ambiente pareçam fáceis de injetar configurações externas como chaves API, senhas de banco de dados, etc., isso pode causar problemas de segurança.
O problema é que as variáveis de ambiente são acessíveis globalmente em um processo. Portanto, se existir uma exploração em parte da sua aplicação, ela poderá comprometer os segredos armazenados nas variáveis de ambiente.
Uma abordagem mais segura é usar um serviço de gerenciamento de segredos que cuida da criptografia e do controle de acesso. Esses serviços permitem o armazenamento de dados sensíveis externamente e fornecem SDKs para a recuperação de valores de aplicativos.
Portanto, considere usar uma solução dedicada de gestão de segredos em vez de variáveis de ambiente para credenciais e chaves privadas. Isso reduz o risco de expor acidentalmente dados sensíveis através de exploits ou registros não intencionais.
Trabalhando com Múltiplos Ambientes
Gerenciar variáveis de ambiente pode se tornar tedioso à medida que as aplicações crescem e são implantadas em vários ambientes (dev, staging, staging, prod). Você pode ter dados de configuração fragmentados espalhados por diversos scripts bash, ferramentas de implantação, etc.
Uma solução de gerenciamento de configuração ajuda a consolidar todas as configurações específicas do ambiente em um local centralizado. Isso pode ser arquivos em um repositório, um servidor de configuração dedicado ou integrado com seus pipelines CI/CD.
Se o objetivo é evitar a duplicação de variáveis de ambiente, uma única fonte de verdade para configurações faz mais sentido.
Compartilhando Configuração Entre Equipes
Já que as variáveis de ambiente são carregadas localmente por processo, compartilhar e sincronizar dados de configuração entre diferentes equipes trabalhando na mesma aplicação ou conjunto de serviços torna-se muito difícil.
Cada equipe pode manter sua cópia dos valores de configuração em diferentes scripts bash, manifestos de implantação, etc. Essa configuração descentralizada leva ao seguinte:
- Deriva de Configuração: Sem uma única fonte de verdade, é fácil para a configuração se tornar inconsistente entre os ambientes à medida que diferentes equipes fazem alterações independentes.
- Falta de visibilidade: Não existe uma maneira centralizada de visualizar, buscar e analisar o estado completo da configuração em todos os serviços. Isso torna extremamente difícil entender como um serviço está configurado.
- Desafios de auditoria: As alterações nas variáveis de ambiente não são rastreadas de forma padrão, tornando difícil auditar quem alterou qual configuração e quando.
- Dificuldades de teste: Sem uma maneira fácil de capturar instantaneamente e compartilhar a configuração, garantir ambientes consistentes para desenvolvimento e teste torna-se extremamente complicado.
Em vez dessa abordagem fragmentada, ter uma solução de configuração centralizada permite que as equipes gerenciem a configuração a partir de uma única plataforma ou repositório.
Construa seus aplicativos com variáveis de ambiente para o longo prazo
À medida que sua aplicação cresce, considere como você pode precisar de maneiras mais avançadas para gerenciar suas configurações.
O que parece simples agora pode se tornar mais complicado mais tarde. Provavelmente, você precisará de melhores maneiras de controlar o acesso, compartilhar configurações de equipe, organizar tudo claramente e atualizar as configurações de forma suave.
Não se coloque em uma situação difícil usando apenas variáveis de ambiente desde o início. Você quer planejar como lidar com as configurações conforme suas necessidades aumentam.
Enquanto as variáveis de ambiente são excelentes para lidar com dados focados no ambiente, como credenciais de login, nomes de bancos de dados, IPs locais, etc, você deseja criar um sistema que siga princípios sólidos como segurança, compartilhabilidade, organização e a capacidade de se adaptar rapidamente às mudanças.
As alternativas que discutimos, como usar um arquivo de configuração dedicado ou serviço, possuem recursos valiosos que se alinham com esses princípios. Isso ajudará você a continuar se movendo rapidamente sem ser retardado.