Configuration
On this page
Hardcoding database URLs, API keys, and feature flags in your source code is a disaster waiting to happen. Different environments (dev, staging, prod) need different configs. Secrets should never appear in git. And you need to change config without redeploying.
The 12-Factor App Rule
One of the most important principles: store config in environment variables. Your code should read configuration from the environment, not from hardcoded values.
// Bad
const DB_URL = "postgres://admin:password123@prod-db.example.com/myapp"
// Good
const DB_URL = process.env.DATABASE_URL
This way the same code runs everywhere, dev, staging, prod, with different env vars.
What Counts as Config?

- Database connection strings
- API keys and secrets
- Port numbers
- Feature flags
- External service URLs
- Cache TTLs
- Rate limit thresholds
- Log levels
If it changes between environments, it’s config.
Secrets Management

Never put secrets in:
- Source code
- Git history
- Docker images
- Log files
Instead use:
- Environment variables (basic, works everywhere)
- Secret managers (AWS Secrets Manager, HashiCorp Vault, Doppler)
- Encrypted config files (decrypted at runtime)
Secret managers are best because they:
- Rotate secrets automatically
- Audit who accessed what
- Inject secrets at deploy time without human involvement
Config Hierarchy
Most apps layer config from multiple sources:
defaults < config file < environment variables < CLI flags
Each layer overrides the previous. So a default port of 3000 can be overridden by a config file, which can be overridden by an env var.
Validation at Startup
Don’t wait until the first database query to discover your DB_URL is missing. Validate ALL required config at startup:
App starts
1. Load config from env vars
2. Validate: DB_URL? Present. API_KEY? Present. PORT? Valid number.
3. Missing something? CRASH immediately with a clear error.
4. All good? Continue booting.
Fail fast. A clear “missing DATABASE_URL” error at startup is infinitely better than a cryptic error 2 hours later when someone hits the database endpoint.
Wrapping Up
- Never hardcode config or secrets
- Use environment variables as the primary mechanism
- Validate all config at app startup
- Use secret managers for production
- Layer config: defaults < file < env < flags (each overrides previous)
- Different environments = different config, same code
Day 15 of 95 | Backend Engineering Series