Tools: Latest: Stop Hardcoding Secrets: Environment Variables Best Practices in 2026

Tools: Latest: Stop Hardcoding Secrets: Environment Variables Best Practices in 2026

The Problem

The Solution: .env Files + Environment Variables

Step 1: Create a .env file

Step 2: Load it in your code

Step 3: NEVER commit .env

Best Practices Checklist

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

2. Validate env vars at startup

3. Use different .env files per environment

4. Never log environment variables

5. Rotate secrets regularly

Production: Use a Secrets Manager

Quick Audit: Is Your Repo Safe? I've reviewed hundreds of GitHub repos. The #1 security mistake? Hardcoded secrets. Here's how to handle environment variables properly in 2026. This code gets committed, pushed, and now your database password is public forever (yes, even if you delete the commit — git history remembers). Set a calendar reminder. Rotate API keys and passwords at least every 90 days. For production, .env files aren't enough. Use: Run this in your repo: More developer guides at lucasmdevdev.github.io Templates let you quickly answer FAQs or store snippets for re-use. check out varlock.dev - it's a huge upgrade from default dotenv loading. Hide child comments as well For further actions, you may consider blocking this person and/or reporting abuse

Code Block

Copy

// DO NOT DO THIS const db = mysql.connect({ host: 'production-db.example.com', password: 'super_secret_password_123' }); // DO NOT DO THIS const db = mysql.connect({ host: 'production-db.example.com', password: 'super_secret_password_123' }); // DO NOT DO THIS const db = mysql.connect({ host: 'production-db.example.com', password: 'super_secret_password_123' }); DATABASE_URL=postgres://user:pass@host:5432/mydb API_KEY=sk_live_abc123 JWT_SECRET=your-256-bit-secret NODE_ENV=development DATABASE_URL=postgres://user:pass@host:5432/mydb API_KEY=sk_live_abc123 JWT_SECRET=your-256-bit-secret NODE_ENV=development DATABASE_URL=postgres://user:pass@host:5432/mydb API_KEY=sk_live_abc123 JWT_SECRET=your-256-bit-secret NODE_ENV=development npm install dotenv npm install dotenv npm install dotenv require('dotenv').config(); const db = mysql.connect({ host: process.env.DATABASE_HOST, password: process.env.DATABASE_PASSWORD }); require('dotenv').config(); const db = mysql.connect({ host: process.env.DATABASE_HOST, password: process.env.DATABASE_PASSWORD }); require('dotenv').config(); const db = mysql.connect({ host: process.env.DATABASE_HOST, password: process.env.DATABASE_PASSWORD }); pip install python-dotenv pip install python-dotenv pip install python-dotenv from dotenv import load_dotenv import os load_dotenv() db_password = os.getenv('DATABASE_PASSWORD') from dotenv import load_dotenv import os load_dotenv() db_password = os.getenv('DATABASE_PASSWORD') from dotenv import load_dotenv import os load_dotenv() db_password = os.getenv('DATABASE_PASSWORD') .env .env.local .env.production .env .env.local .env.production .env .env.local .env.production # .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 # .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 # .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 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); } } 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); } } 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); } } .env.development .env.staging .env.production .env.development .env.staging .env.production .env.development .env.staging .env.production // NEVER do this console.log('Config:', process.env); // If you must debug, redact: console.log('DB connected to:', process.env.DATABASE_HOST); // NEVER do this console.log('Config:', process.env); // If you must debug, redact: console.log('DB connected to:', process.env.DATABASE_HOST); // NEVER do this console.log('Config:', process.env); // If you must debug, redact: console.log('DB connected to:', process.env.DATABASE_HOST); # 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" . # 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" . # 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" . - Secrets go in .env, never in code - .env goes in .gitignore, always - .env.example gets committed (with placeholder values) - Validate env vars at startup - Use a secrets manager in production - Email [email protected] - Location BC, Canada - Work Cofounder of DMNO - https://dmno.dev - Joined Mar 15, 2020