Configuration and secrets

Keep config out of code, keep secrets out of history, and assume anything pushed is exposed.

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.
  • .gitignore affects only untracked paths. A file that is already tracked stays tracked until you remove it from the index with git rm --cached.

When a secret does reach version control, the response order is fixed:

  1. Rotate. Issue a new credential and revoke the old one. If it was pushed anywhere, assume someone has it.
  2. Contain. Remove it from every commit under review, not just the tip. A new commit that deletes the file leaves the secret in history.
  3. 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).