Environments & Secrets
- environments
- environment variables
- secrets
- .env
- 12-factor
- .gitignore
Environments & Secrets - Config Without Catastrophe ๐
A true story (it happens constantly). A developer accidentally committed their AWS key to a public GitHub repo. Bots scrape GitHub for keys every minute - within 9 minutes theirs was found and used to spin up servers mining crypto. The bill: tens of thousands of dollars. This lesson is how you never become that story.
๐ง The mental model: rehearsal โ dress rehearsal โ opening night
Your code runs in several environments - same script, very different stakes:
| Environment | Like... | Talks to |
|---|---|---|
| development | rehearsing in your bedroom | fake / local data |
| staging | dress rehearsal on the real stage | a copy of production |
| production | opening night, live audience | the real database & users |
The golden rule: the exact same build runs everywhere. Only the configuration changes.
Config lives in the environment, not the code (the "12-Factor" rule)
How does the same build know which database to use? Environment variables - values set outside your code, per environment:
import os
DATABASE_URL = os.environ["DATABASE_URL"] # set per-environment, never hardcoded
ENV = os.environ.get("ENV", "development") # .get() with a safe default
Locally you keep them in a .env file; in production your platform (Vercel, AWSโฆ) injects them.
The secret rules (memorise these)
- Never put keys, passwords, or tokens in source code or Git.
- Add
.envto.gitignoreso it is never committed.- If a secret leaks, rotate it immediately - deleting the commit is NOT enough, because Git keeps history.
# .gitignore
.env
*.key
The #1 production outage you'll cause
A required secret that is present but empty (""). The app boots, then explodes three hours into the night run. Validate config at startup and fail loudly - an empty string is "missing".
๐ง Try it for real
echo 'API_KEY=test123' > .env # create local config
echo '.env' >> .gitignore # make sure it's never committed
python -c "import os; print(os.environ.get('API_KEY', 'NOT SET'))"
Your Task
Read a config dict, detect the environment, and validate that all required secrets are present - treating an empty value as missing (the classic outage).