Key idea
Configuration is anything that changes between the places your app runs: your laptop, staging, production. Keep it outside the code and give it to the app when it starts. The code stays the same everywhere; only the configuration changes.
Code or configuration?
Code is how your app behaves: its routes, its queries, the way it formats a price. It's the same in every environment, and changing it means a new version.
Configuration is the part that differs by environment:
- Addresses of what the app talks to: the database URL, another service's URL, a mail server.
- Credentials: passwords, API keys, tokens.
- Feature flags:
NEW_CHECKOUT=onin staging while it's off in production. - Per-environment settings: log level, the public URL, which payment account to charge.
Some values look like settings but are really code. The number of items on a page, or the route names, are the same everywhere, so they can stay in the code.
The rule
The twelve-factor app, a widely used set of guidelines for web apps, puts it in one line: store config in the environment. In practice that means environment variables, which the next lesson shows how to read and set.
A quick test: could you publish your code today without leaking anything, and deploy it to a new environment without editing a file? If either answer is no, some configuration is still in the code.
What goes wrong when it's in the code
Say the database URL is written into db.js:
const db = connect("postgres://app:[email protected]:5432/shop");
Three problems follow:
- Every environment needs its own copy of the code. Staging and production drift apart, and a fix made in one gets lost in the other.
- The password is in your repository, and in its history, for anyone who can read it. Module 6 shows how that leaks.
- Changing it means a new build. Moving the database becomes a code change, a review and a release.
Read the value instead, and the same file works everywhere:
const db = connect(process.env.DATABASE_URL);
What about config files?
Files like config.json or settings.yaml are fine for values that are the same everywhere, and for structure (which settings exist and their defaults). Per-environment values and credentials still come from the environment. A common pattern is a config file with defaults that environment variables override.
Keep one config file per app, not one per environment (config.prod.json, config.staging.json). Every new environment then needs a code change, and the files full of production values end up in the repository.
Group it, name it, check it
Read every setting in one place when the app starts, not scattered through the code. Give each a clear, upper-case name (DATABASE_URL, LOG_LEVEL). If a required value is missing, stop with a message that names it, rather than failing later in a confusing way.
Check yourself