Environment Variables Done Right: Stop Hardcoding Secrets
Your .env file is in git. Your database password is in plain text. Your JWT secret is the same in dev and prod.
The Basics
import { z } from "zod";
const envSchema = z.object({
DATABASE_URL: z.string().url(),
JWT_SECRET: z.string().min(32),
PORT: z.coerce.number().default(3000),
NODE_ENV: z.enum(["development", "production", "test"]),
});
export const env = envSchema.parse(process.env);
App crashes at startup if any env var is missing or wrong type. No more runtime surprises.
Secret Management in Production
Never store secrets in .env files in production. Options:
.gitignore Rules
.env
.env.local
.env.production
Commit .env.example with placeholder values. Never commit actual secrets.
Secret Rotation
Rotate secrets without downtime: support TWO valid secrets simultaneously during rotation. Accept both old and new JWT secrets during a transition window.
Part of my Production Backend Patterns series. Follow for more practical backend engineering.
More...
Your .env file is in git. Your database password is in plain text. Your JWT secret is the same in dev and prod.
The Basics
import { z } from "zod";
const envSchema = z.object({
DATABASE_URL: z.string().url(),
JWT_SECRET: z.string().min(32),
PORT: z.coerce.number().default(3000),
NODE_ENV: z.enum(["development", "production", "test"]),
});
export const env = envSchema.parse(process.env);
App crashes at startup if any env var is missing or wrong type. No more runtime surprises.
Secret Management in Production
Never store secrets in .env files in production. Options:
- Cloud provider secrets: AWS Secrets Manager, GCP Secret Manager, Azure Key Vault
- HashiCorp Vault: Self-hosted, dynamic secrets, auto-rotation
- Kubernetes secrets: Base64 encoded (not encrypted), use sealed-secrets or external-secrets operator
.gitignore Rules
.env
.env.local
.env.production
Commit .env.example with placeholder values. Never commit actual secrets.
Secret Rotation
Rotate secrets without downtime: support TWO valid secrets simultaneously during rotation. Accept both old and new JWT secrets during a transition window.
Part of my Production Backend Patterns series. Follow for more practical backend engineering.
More...