Environment Configuration Best Practices
Environment configuration is one of those areas that tends to work—until it doesn’t. Problems usually appear late: a missing variable, the wrong environment loaded, or production silently behaving like development.
The approach here is deliberately strict:
Configuration should be explicit, validated at startup, and impossible to misconfigure silently.
That leads to a small set of rules:
- no default values or fallbacks
- validate everything early
- no environment-based branching in code
- no example placeholders
No Defaults, Fail Fast
Default values hide configuration errors. They allow the application to start in an unintended state. We should also ensure all required variables are checked as early as possible:
// bad
const apiUrl = process.env.API_URL || "http://localhost:3000";
// good
const required = ["API_URL", "DATABASE_URL", "SECRET_KEY"];
const missing = required.filter((v) => !process.env[v]);
if (missing.length > 0) {
throw new Error(`Missing environment variables: ${missing.join(", ")}`);
}
Explicit Configuration Only
Configuration should live in configuration, not in conditionals. This is particularly relevant for deployment scripts (e.g. CDK scripts), were variables are more likely to be baked in.
// bad - this embeds environment knowledge in code.
const apiUrl =
NODE_ENV === "production"
? "https://api.example.com"
: "http://localhost:3000";
// good
const apiUrl = process.env.API_URL;
if (!apiUrl) throw new Error("API_URL is required");
Flags Over Environment Names
Environment names should not control behaviour. The behaviour of the system should be explicit and independently configurable.
// bad
if (process.env.NODE_ENV === "development") {
console.log(data);
}
// good
if (process.env.DEBUG_LOGGING === "true") {
console.log(data);
}
Placeholders, Not Values
Configuration templates (i.e. .env.example) should document what is required. Avoid placeholder values, as these can often trip up our earlier validation checks.
# good
API_URL=
API_KEY=
DATABASE_URL=
DEBUG_LOGGING=
# bad
API_KEY="INSERT_KEY"