Configuration and Environment Management

The same application build usually runs in several places — a developer's laptop, CI, preview environments, staging, and production — and should behave differently in each only because its configuration differs: which database it talks to, which payment account it uses, how verbose its logs are, how long it waits before timing out. Getting configuration right means one tested artifact can be promoted from environment to environment with confidence. Getting it wrong produces some of the most common and embarrassing outages: a typo in a timeout, a staging API key in production, a missing variable that only fails on the first real payment.

Configuration management answers a handful of questions deliberately: what counts as configuration, where each value lives, how the application reads and validates it, who can change it, and how a change is rolled out and rolled back.

TL;DR

Quick Example

A single configuration module in TypeScript that parses environment variables with Zod, applies defaults, converts types, and stops the process on invalid input:

The rest of the codebase imports config and never touches process.env directly, so every setting has one definition, one type, and one place to document it.

Core Concepts

What Counts as Configuration

The Twelve-Factor App defines configuration as everything likely to vary between deploys:

Internal wiring that is the same everywhere — routes, dependency injection, business rules — is code, not configuration. Making it configurable adds options nobody tests.

Where Configuration Lives

Layering and Precedence

Most applications combine sources: defaults in code, an environment-specific file, environment variables, and occasionally flags. Document the precedence — typically later sources override earlier ones — and log the effective configuration at startup with secrets redacted, so "what value is this instance actually using?" has a quick answer.

Static Versus Dynamic Configuration

Static configuration is read once at startup; changing it means restarting or redeploying, which keeps behavior predictable and ties every change to a release. Dynamic configuration changes while the process runs, which is faster but needs validation, gradual rollout, and a way to revert. Feature flags are dynamic configuration with targeting rules; use them for releasing features and operational kill switches, not for values such as database URLs.

Secrets Are Different

Secrets need stricter access control, audit logs, rotation, and protection from accidental exposure. Store them in a secrets manager, inject them at deploy time or fetch them at startup with short-lived credentials, and never commit them to repositories or bake them into images. See Secrets Management.

Environments

Parity

Differences between environments should be limited to configuration values, scale, and data. Same database engine and version, same container images, same configuration mechanism. Infrastructure as code makes this achievable; changes made by hand in one environment are how "it worked in staging" happens.

Ask for Behavior, Not the Environment Name

Explicit settings let a preview environment use real email sandboxes, or production run a temporary diagnostic mode, without new code paths.

Kubernetes Example

Ordinary settings in a ConfigMap and credentials referenced from a Secret:

Kubernetes Secrets are only base64-encoded by default; enable encryption at rest and restrict access, or sync them from an external secrets manager.

Changing Configuration Safely

Google's SRE book estimates that roughly 70% of outages come from changes to live systems, and configuration changes are changes. Treat them with the same discipline as code:

Best Practices

Fail Fast at Startup

A missing database URL should stop the process with a clear message during deployment — where the rollout halts — not cause the first user request to fail an hour later. See Backend Service Lifecycle and Graceful Shutdown.

Default the Harmless, Require the Important

Provide defaults for tuning values such as log level and timeouts. Require explicit values for anything environment-specific or dangerous — database URLs, credentials, external endpoints — so production never silently runs with a development default.

Put Units in Names

REQUEST_TIMEOUT_MS, MAX_UPLOAD_BYTES, and CACHE_TTL_SECONDS prevent the classic mistake of a value meant as seconds being read as milliseconds.

Document Every Setting

Generate documentation, or a .env.example, from the schema: name, type, default, whether it is secret, and what it does. Delete settings nobody uses; every option is something that can be misconfigured.

Common Mistakes

Secrets Committed to the Repository

String Flags That Are Always True

Environment Variables Read Everywhere

Hand-Edited Production Settings

FAQ

Should I use environment variables or configuration files?

Often both. Environment variables suit values that differ per deployment and are supported by every platform; files suit structured defaults and settings that benefit from comments. Whatever the sources, load them through one module with a documented precedence.

Is it safe to keep secrets in environment variables?

It is common and acceptable with care, but environment variables can leak through crash reports, debug endpoints, child processes, and logs that dump the environment. For sensitive credentials, prefer secrets mounted as files or fetched from a secrets manager, and make sure error reporting scrubs them.

What is the difference between configuration and feature flags?

Configuration describes the environment the application runs in and is usually fixed per deployment. Feature flags control product behavior at runtime, often per user or tenant, to release features gradually or switch them off quickly. Flags need their own lifecycle, including removal once a rollout is complete.

How do we keep staging like production?

Build both from the same infrastructure code and container images, use the same database engine and versions, and limit differences to configuration values, scale, and data. Review differences regularly; each one is a place where staging can pass while production fails.

Should configuration be hot-reloadable?

Only where fast changes are worth the complexity — operational switches, rate limits, flags. Hot reload needs validation before applying, atomic switching to the new values, and visibility of which version is active. For most settings, a quick redeploy is simpler and safer.

Related Topics

References