Configuration is everything that varies between deploys: endpoints, credentials, and feature flags. Twelve-Factor's rule is to read it from the environment, so the same build runs everywhere and a credential never has to live in the code.
Local development still needs values. The usual pattern:
- A tracked template (
.env.example) names every variable with empty or dummy values. - An untracked local file (
.env.local) holds real values, protected by a narrow ignore rule (/.env.local). A broad rule like.env*also hides the template. .gitignoreaffects only untracked paths. A file that is already tracked stays tracked until you remove it from the index withgit rm --cached.
When a secret does reach version control, the response order is fixed:
- Rotate. Issue a new credential and revoke the old one. If it was pushed anywhere, assume someone has it.
- Contain. Remove it from every commit under review, not just the tip. A new commit that deletes the file leaves the secret in history.
- Prevent. Add the ignore rule, a pre-commit or CI secret scanner, and a template.
CI systems draw the same line. Workflows triggered by untrusted code, such as a fork's pull request, must not receive deploy secrets. Split build-and-test (no secrets, read-only token) from deploy (trusted branch, protected environment).