.env Files & Environment Variables
debt(d5/e3/b3/t7)
Closest to 'specialist tool catches it' (d5). The detection_hints.tools list includes trufflehog, gitleaks, and semgrep — all specialist security scanning tools. These won't catch the problem by default in a normal dev workflow; a developer must deliberately run or configure these tools to detect committed secrets or missing .env.example files.
Closest to 'simple parameterised fix' (e3). The quick_fix is: add .env to .gitignore, create .env.example with dummy values, and install vlucas/phpdotenv. This is a small, well-understood pattern fix involving a few files (gitignore, example file, composer.json), but not a deep refactor. If credentials were already committed, git history must be purged, which adds complexity — so e3 rather than e1.
Closest to 'localised tax' (b3). The choice applies to both web and cli contexts and touches configuration management broadly, but it's a well-understood, low-ceremony pattern. Once set up correctly (.gitignore, .env.example, dotenv library), it imposes minimal ongoing burden. It doesn't shape architectural decisions for the rest of the codebase.
Closest to 'serious trap' (t7). The misconception field states explicitly: developers believe committing a dev .env is harmless, but it establishes a pattern that leads to production credentials being committed accidentally. This contradicts the developer's intuition ('dev credentials don't matter') and the failure mode is silent until a production credential leaks — a serious, non-obvious trap that many competent developers fall into.
Also Known As
TL;DR
Explanation
The Twelve-Factor App methodology mandates configuration in the environment — database credentials, API keys, and environment-specific settings should never be hardcoded or committed to version control. In development, vlucas/phpdotenv loads a .env file into $_ENV and getenv(). In production, variables are set at the infrastructure level (Docker, Kubernetes secrets, AWS Parameter Store). Always add .env to .gitignore; commit a .env.example with placeholder values. Access variables via $_ENV['KEY'] or getenv('KEY') — prefer $_ENV as it's not affected by php.ini's variables_order.
Watch Out
Common Misconception
Why It Matters
Common Mistakes
- Committing the .env file to version control — the whole point is to keep secrets out of the repo.
- Not providing a .env.example with all required keys — new developers don't know what to configure.
- Loading .env in production when environment variables are already set by the server — causes double-loading conflicts.
- Using $_ENV instead of getenv() or a dotenv library — $_ENV is not populated in all PHP configurations.
Code Examples
// .env committed to git — credentials exposed:
# .gitignore is missing .env entry
DB_PASSWORD=supersecret123
API_KEY=sk-live-abc123
# Anyone with repo access has these credentials
# .env — local secrets, NEVER committed to git
APP_ENV=production
DB_HOST=db.internal
DB_PASS=super_secret_password
STRIPE_SECRET=sk_live_...
# .env.example — template committed to git, no real secrets
APP_ENV=local
DB_HOST=localhost
DB_PASS=
STRIPE_SECRET=
# PHP — vlucas/phpdotenv
$dotenv = Dotenv\Dotenv::createImmutable(__DIR__);
$dotenv->load();
$dotenv->required(['DB_HOST', 'DB_PASS', 'APP_KEY']); // fail fast if missing
$dbPass = $_ENV['DB_PASS'];
# In production: set env vars at server/container level — don't deploy .env files