Configuration Strategy
Design configuration that is safe, auditable, and easy to change. Most configuration problems (hardcoded secrets, production config in dev, feature flags with no off-switch) come from not designing the system upfront.
Step 0: Inventory What Already Exists
Before introducing a new environment variable, secret, feature flag, or
constant, establish what the project already defines. Inspect the current state
directly — grep the tree; don't infer it from the requirements or memory.
Cover both:
- Artifacts — existing key names and the values themselves. Search for the
literal value, not just the name: the same timeout or limit is often already
hardcoded in two places, and adding a third is the common failure.
- Written decisions — config conventions, naming schemes,
.env.example,
and prior audit findings that already govern where things live.
Exit condition — one line, then keep going in the same response:
"<existing key> already carries <value>; it does / does not serve this
because <reason>."
This step is not a gate on delivering. Greenfield — nothing configured yet,
or nothing you can reach — closes it in one line: say so and go straight to
Step 1. When you can't inspect, name what you would check and continue under
stated assumptions. Never answer with inventory and questions alone; produce the
configuration design in the same reply.
This constrains the input, not the choice. A new key is a fine outcome; an
unexamined one isn't — and a duplicated value will drift.
Step 1: Identify All Configuration Needed
List every piece of configuration the feature or service requires:
- Service endpoints: URLs for external APIs, internal services, databases
- Credentials and secrets: API keys, database passwords, signing keys, certificates
- Behavior toggles: Feature flags, A/B test parameters, rate limits, timeouts
- Static config: Timeouts, retry counts, pagination defaults, log levels
- Environment-specific values: Different database URLs for dev/staging/prod
Don't leave any implicit. "We'll hard-code the staging URL for now" is the start of a production incident.
Step 2: Classify Each Configuration Item
For each item, determine:
Sensitivity:
- Secret: Must never appear in logs, code, or non-encrypted storage (API keys, passwords, tokens)
- Sensitive: Not a secret but not public (internal service URLs, customer IDs)
- Non-sensitive: Safe to log, safe in version control (log levels, timeouts, feature flag names)
Mutability:
- Static: Set at deploy time, doesn't change without a redeploy (database schema version, service name)
- Runtime-mutable: Can change without redeploy (feature flags, rate limits, A/B test parameters)
Scope:
- Global: Same value in all environments (timeout constants, algorithm parameters)
- Environment-specific: Different per env (database URLs, API endpoints, log levels)
- Per-tenant/per-user: Different per customer (feature entitlements, custom limits)
Step 3: Design the Configuration Hierarchy
Based on classification, assign each item to the right storage:
| Storage |
For |
Examples |
| Environment variables |
Environment-specific non-secrets |
DATABASE_URL, SERVICE_ENV, LOG_LEVEL |
| Secrets manager |
All secrets |
Database passwords, API keys, signing keys |
| Feature flag service |
Runtime-mutable toggles |
Feature flags, A/B variants, rollout percentages |
| Config file in repo |
Non-sensitive static config |
Timeout constants, retry policies, allowed values |
| Database (config table) |
Per-tenant/per-user config |
Customer-specific rate limits, feature entitlements |
Hierarchy principle: More specific overrides less specific. Per-tenant overrides environment which overrides global default.
See references/config-patterns.md for feature flag design patterns and secrets management tool guidance.
Step 4: Plan Safe Config Rollout
Configuration changes can cause outages just like code changes. Plan rollout for each type:
Secrets rotation:
- Generate new secret while old one remains valid
- Deploy application with new secret
- Verify application uses new secret correctly
- Revoke old secret (after 24-hour overlap window)
Feature flag rollout:
- Start at 0% — verify flag infrastructure works
- Enable for internal users — catch obvious breakage
- Enable for small percentage (5-10%) — monitor metrics
- Ramp up gradually — verify metrics hold at each step
- Full rollout — verify, then clean up the flag
Environment variable changes:
- New variables: set in target environment before deploying code that reads them
- Changed values: consider backward compatibility with old code during rolling deploys
- Removed variables: remove from code before removing from environment config
Step 5: Audit Existing Code for Anti-patterns
Review the codebase for common configuration mistakes:
Hardcoded values to find and fix:
- IP addresses, hostnames, port numbers (search:
localhost, 127.0.0.1, regex for IP patterns)
- Credentials (search:
password, secret, api_key, token in string literals)
- Environment-specific constants (search for strings that differ between environments)
- Magic numbers that should be configurable (search for hardcoded timeouts, limits, batch sizes)
Config anti-patterns:
- Feature flags that have been "on" everywhere for 6+ months (dead code risk — remove the flag)
- Config values read but never validated (add startup validation that fails fast on missing config)
- Different config loading paths for test vs production (leads to "works in tests but not prod")
- Secrets passed via command-line arguments (visible in process lists)
Principles Applied
- DRY: Single source of truth for each config value. If the same value appears in multiple places, it will drift.
- KISS: Flat config beats deeply nested config. Environment variables beat custom config DSLs.
- Least privilege: Services should only have access to the secrets they need. Database credentials shouldn't be shared across services.
- YAGNI: Don't create config variables for things that will never change. Hard-code what's truly constant.
- Fail fast: Validate all required config on startup. A crash at startup is better than a mysterious failure 10 minutes in.
Cross-Skill References
feature-planning — decide which behaviors will be feature-flagged during planning, before implementation
security-audit — review secrets handling and access control as part of security review
deployment-checklist — verify all config is set before deploying
rollback-strategy — feature flags are the simplest rollback mechanism; design them accordingly
1---2name: configuration-strategy3description: Design environment configuration, secrets management, and feature-flag hierarchy for a service or feature. Triggers: config strategy, environment variables, .env, secrets management, feature flag, config hierarchy, config precedence, twelve-factor config, environment-specific settings.4---56# Configuration Strategy78Design configuration that is safe, auditable, and easy to change. Most configuration problems (hardcoded secrets, production config in dev, feature flags with no off-switch) come from not designing the system upfront.910## Step 0: Inventory What Already Exists1112Before introducing a new environment variable, secret, feature flag, or13constant, establish what the project already defines. Inspect the current state14**directly** — grep the tree; don't infer it from the requirements or memory.1516Cover both:1718- **Artifacts** — existing key names *and* the values themselves. Search for the19 literal value, not just the name: the same timeout or limit is often already20 hardcoded in two places, and adding a third is the common failure.21- **Written decisions** — config conventions, naming schemes, `.env.example`,22 and prior audit findings that already govern where things live.2324**Exit condition** — one line, then keep going in the same response:25*"`<existing key>` already carries `<value>`; it does / does not serve this26because `<reason>`."*2728**This step is not a gate on delivering.** Greenfield — nothing configured yet,29or nothing you can reach — closes it in one line: say so and go straight to30Step 1. When you can't inspect, name what you would check and continue under31stated assumptions. Never answer with inventory and questions alone; produce the32configuration design in the same reply.3334This constrains the input, not the choice. A new key is a fine outcome; an35unexamined one isn't — and a duplicated value will drift.3637## Step 1: Identify All Configuration Needed3839List every piece of configuration the feature or service requires:4041- **Service endpoints**: URLs for external APIs, internal services, databases42- **Credentials and secrets**: API keys, database passwords, signing keys, certificates43- **Behavior toggles**: Feature flags, A/B test parameters, rate limits, timeouts44- **Static config**: Timeouts, retry counts, pagination defaults, log levels45- **Environment-specific values**: Different database URLs for dev/staging/prod4647Don't leave any implicit. "We'll hard-code the staging URL for now" is the start of a production incident.4849## Step 2: Classify Each Configuration Item5051For each item, determine:5253**Sensitivity:**54- **Secret**: Must never appear in logs, code, or non-encrypted storage (API keys, passwords, tokens)55- **Sensitive**: Not a secret but not public (internal service URLs, customer IDs)56- **Non-sensitive**: Safe to log, safe in version control (log levels, timeouts, feature flag names)5758**Mutability:**59- **Static**: Set at deploy time, doesn't change without a redeploy (database schema version, service name)60- **Runtime-mutable**: Can change without redeploy (feature flags, rate limits, A/B test parameters)6162**Scope:**63- **Global**: Same value in all environments (timeout constants, algorithm parameters)64- **Environment-specific**: Different per env (database URLs, API endpoints, log levels)65- **Per-tenant/per-user**: Different per customer (feature entitlements, custom limits)6667## Step 3: Design the Configuration Hierarchy6869Based on classification, assign each item to the right storage:7071| Storage | For | Examples |72|---------|-----|---------|73| Environment variables | Environment-specific non-secrets | DATABASE_URL, SERVICE_ENV, LOG_LEVEL |74| Secrets manager | All secrets | Database passwords, API keys, signing keys |75| Feature flag service | Runtime-mutable toggles | Feature flags, A/B variants, rollout percentages |76| Config file in repo | Non-sensitive static config | Timeout constants, retry policies, allowed values |77| Database (config table) | Per-tenant/per-user config | Customer-specific rate limits, feature entitlements |7879**Hierarchy principle**: More specific overrides less specific. Per-tenant overrides environment which overrides global default.8081See [references/config-patterns.md](references/config-patterns.md) for feature flag design patterns and secrets management tool guidance.8283## Step 4: Plan Safe Config Rollout8485Configuration changes can cause outages just like code changes. Plan rollout for each type:8687**Secrets rotation:**881. Generate new secret while old one remains valid892. Deploy application with new secret903. Verify application uses new secret correctly914. Revoke old secret (after 24-hour overlap window)9293**Feature flag rollout:**941. Start at 0% — verify flag infrastructure works952. Enable for internal users — catch obvious breakage963. Enable for small percentage (5-10%) — monitor metrics974. Ramp up gradually — verify metrics hold at each step985. Full rollout — verify, then clean up the flag99100**Environment variable changes:**101- New variables: set in target environment *before* deploying code that reads them102- Changed values: consider backward compatibility with old code during rolling deploys103- Removed variables: remove from code *before* removing from environment config104105## Step 5: Audit Existing Code for Anti-patterns106107Review the codebase for common configuration mistakes:108109**Hardcoded values to find and fix:**110- IP addresses, hostnames, port numbers (search: `localhost`, `127.0.0.1`, regex for IP patterns)111- Credentials (search: `password`, `secret`, `api_key`, `token` in string literals)112- Environment-specific constants (search for strings that differ between environments)113- Magic numbers that should be configurable (search for hardcoded timeouts, limits, batch sizes)114115**Config anti-patterns:**116- Feature flags that have been "on" everywhere for 6+ months (dead code risk — remove the flag)117- Config values read but never validated (add startup validation that fails fast on missing config)118- Different config loading paths for test vs production (leads to "works in tests but not prod")119- Secrets passed via command-line arguments (visible in process lists)120121## Principles Applied122123- **DRY**: Single source of truth for each config value. If the same value appears in multiple places, it will drift.124- **KISS**: Flat config beats deeply nested config. Environment variables beat custom config DSLs.125- **Least privilege**: Services should only have access to the secrets they need. Database credentials shouldn't be shared across services.126- **YAGNI**: Don't create config variables for things that will never change. Hard-code what's truly constant.127- **Fail fast**: Validate all required config on startup. A crash at startup is better than a mysterious failure 10 minutes in.128129## Cross-Skill References130131- `feature-planning` — decide which behaviors will be feature-flagged during planning, before implementation132- `security-audit` — review secrets handling and access control as part of security review133- `deployment-checklist` — verify all config is set before deploying134- `rollback-strategy` — feature flags are the simplest rollback mechanism; design them accordingly