DEV Community

Binary Journal
Binary Journal

Posted on

Environment Variables the Safe Way

Stop Hardcoding Secrets

We've all been there: a config file with API_KEY = "sk-123456" sitting in the repo. It works until it doesn't. A teammate pushes it to GitHub, a bot scrapes it, and now your credit card bill is someone else's shopping spree.

Environment variables are the standard fix, but doing it wrong is almost as bad as not doing it at all. Here's how to handle them safely in real projects.

The Basics: What Are Env Vars?

Environment variables are key-value pairs available to your process at runtime. They keep secrets out of code and let you change behavior without touching source files.

export DATABASE_URL="postgres://user:pass@localhost:5432/mydb"
Enter fullscreen mode Exit fullscreen mode

In Node.js, you read them with process.env.DATABASE_URL. In Python, os.environ.get("DATABASE_URL"). Simple enough.

The .env File Trap

Tools like dotenv load variables from a .env file. That's convenient for local dev, but it's a trap if you're not careful.

The rule: never commit .env. Add it to .gitignore immediately.

# .gitignore
.env
Enter fullscreen mode Exit fullscreen mode

But here's the catch: if you use .env, you need a template. Commit .env.example with dummy values so other developers know what to set.

# .env.example
DATABASE_URL=postgres://user:pass@localhost:5432/mydb
API_KEY=changeme
PORT=3000
Enter fullscreen mode Exit fullscreen mode

Now teammates can copy it to .env and fill in real values.

Don't Use One File for Everything

Separate concerns. Local development, staging, and production have different needs. Don't force a single .env to handle all cases.

For local dev, dotenv is fine. For production, use your hosting platform's secret manager (AWS Secrets Manager, Vercel Env Variables, etc.). Never ship a .env file to a server.

Validate Early, Fail Fast

Missing env vars are a common source of bugs. Instead of crashing deep in your code with a cryptic error, validate at startup.

const required = ["DATABASE_URL", "API_KEY", "PORT"];

for (const key of required) {
  if (!process.env[key]) {
    throw new Error(`Missing required env var: ${key}`);
  }
}
Enter fullscreen mode Exit fullscreen mode

Or use a library like envalid in Node or pydantic settings in Python. They give you type coercion and clear error messages.

Defaults with Caution

Defaults are handy for non-secret values like PORT=3000. But never default secrets. An empty string default for an API key is still a secret that's missing.

const port = process.env.PORT || 3000; // fine
const apiKey = process.env.API_KEY; // no default!
Enter fullscreen mode Exit fullscreen mode

Avoid Committing .env Files in CI

If your CI needs env vars, set them in the CI service's settings, not in the repo. GitHub Actions has secrets, GitLab has variables, and they're all encrypted. Use those.

Watch Out for Logging

Don't log env vars. It's tempting to debug with console.log(process.env), but that can leak secrets into logs. If you must log, redact values.

function safeEnv() {
  const copy = { ...process.env };
  for (const key of Object.keys(copy)) {
    if (/KEY|SECRET|PASSWORD|TOKEN/i.test(key)) {
      copy[key] = "[REDACTED]";
    }
  }
  return copy;
}
Enter fullscreen mode Exit fullscreen mode

Use a Library for Complex Config

For larger projects, manage config with a library that supports nested structures and validation. In Node, config or convict are good. In Python, pydantic is excellent.

from pydantic import BaseSettings

class Settings(BaseSettings):
    database_url: str
    api_key: str
    port: int = 3000

    class Config:
        env_file = ".env"

settings = Settings()
Enter fullscreen mode Exit fullscreen mode

Now you get automatic validation and type conversion.

Rotate Secrets Regularly

Even with perfect practices, secrets leak. Rotate them periodically. Use your cloud provider's rotation features or set reminders.

The Golden Rules

  1. Never commit real secrets. Use .env.example and .gitignore.
  2. Load env vars at process start. Don't fetch them lazily in random places.
  3. Validate everything. Fail fast with clear messages.
  4. Use platform secret managers in production.
  5. Redact logs.

Environment variables are a simple tool, but they require discipline. Follow these practices and you'll sleep better knowing your secrets are safe.

Top comments (1)

Collapse
 
swapnoneel123 profile image
Swapnoneel Saha

the startup validation and separate environments are the most useful rules here. i would add a secret scan in pre commit and ci, plus a boot check that reports the variable name and expected type without printing the value. for rotation, support two active keys during a short overlap, then revoke the old one after all instances reload. that reduces failed deploys while keeping the secret out of the repo.