DEV Community

Lucas M Dev
Lucas M Dev

Posted on

Stop Hardcoding Secrets: Environment Variables Best Practices in 2026

I've reviewed hundreds of GitHub repos. The #1 security mistake? Hardcoded secrets. Here's how to handle environment variables properly in 2026.

The Problem

// DO NOT DO THIS
const db = mysql.connect({
  host: 'production-db.example.com',
  password: 'super_secret_password_123'
});
Enter fullscreen mode Exit fullscreen mode

This code gets committed, pushed, and now your database password is public forever (yes, even if you delete the commit — git history remembers).

The Solution: .env Files + Environment Variables

Step 1: Create a .env file

DATABASE_URL=postgres://user:pass@host:5432/mydb
API_KEY=sk_live_abc123
JWT_SECRET=your-256-bit-secret
NODE_ENV=development
Enter fullscreen mode Exit fullscreen mode

Step 2: Load it in your code

Node.js:

npm install dotenv
Enter fullscreen mode Exit fullscreen mode
require('dotenv').config();

const db = mysql.connect({
  host: process.env.DATABASE_HOST,
  password: process.env.DATABASE_PASSWORD
});
Enter fullscreen mode Exit fullscreen mode

Python:

pip install python-dotenv
Enter fullscreen mode Exit fullscreen mode
from dotenv import load_dotenv
import os

load_dotenv()

db_password = os.getenv('DATABASE_PASSWORD')
Enter fullscreen mode Exit fullscreen mode

Step 3: NEVER commit .env

Add to .gitignore:

.env
.env.local
.env.production
Enter fullscreen mode Exit fullscreen mode

Best Practices Checklist

1. Use .env.example (committed) + .env (gitignored)

# .env.example (committed — shows structure, no real values)
DATABASE_URL=postgres://user:password@localhost:5432/mydb
API_KEY=your-api-key-here
JWT_SECRET=generate-a-random-string
Enter fullscreen mode Exit fullscreen mode

2. Validate env vars at startup

const required = ['DATABASE_URL', 'API_KEY', 'JWT_SECRET'];

for (const key of required) {
  if (!process.env[key]) {
    console.error(`Missing required env var: ${key}`);
    process.exit(1);
  }
}
Enter fullscreen mode Exit fullscreen mode

3. Use different .env files per environment

.env.development
.env.staging
.env.production
Enter fullscreen mode Exit fullscreen mode

4. Never log environment variables

// NEVER do this
console.log('Config:', process.env);

// If you must debug, redact:
console.log('DB connected to:', process.env.DATABASE_HOST);
Enter fullscreen mode Exit fullscreen mode

5. Rotate secrets regularly

Set a calendar reminder. Rotate API keys and passwords at least every 90 days.

Production: Use a Secrets Manager

For production, .env files aren't enough. Use:

Service Free Tier Best For
Doppler 5 projects Small teams
AWS Secrets Manager 30-day trial AWS deployments
Infisical Unlimited (open source) Self-hosted
Railway/Vercel env Built-in PaaS deployments

Quick Audit: Is Your Repo Safe?

Run this in your repo:

# Check if .env is in .gitignore
grep -q ".env" .gitignore && echo "OK: .env is gitignored" || echo "WARNING: .env not in .gitignore"

# Check for hardcoded secrets (basic scan)
grep -rn "password\s*=\s*['\"]" --include="*.js" --include="*.py" --include="*.ts" .
grep -rn "api.key\s*=\s*['\"]" --include="*.js" --include="*.py" --include="*.ts" .
Enter fullscreen mode Exit fullscreen mode

TL;DR

  1. Secrets go in .env, never in code
  2. .env goes in .gitignore, always
  3. .env.example gets committed (with placeholder values)
  4. Validate env vars at startup
  5. Use a secrets manager in production

More developer guides at lucasmdevdev.github.io

Top comments (1)

Collapse
 
theoephraim profile image
Theo Ephraim

check out varlock.dev - it's a huge upgrade from default dotenv loading.