17.2.2.1 Environment Config Practice
A focused guide to Environment Config Practice, connecting core concepts with practical Docker and container operations.
Environment config practice covers the day-to-day organizational conventions for managing environment files themselves, naming, templating, version control treatment, and naming consistency across variables, which is a more operational, workflow-focused concern than the architectural principle of externalizing configuration in the first place.
Naming convention for environment files
A consistent naming pattern across environment files makes it immediately clear which file applies to which context, without needing to open and inspect each one to determine its purpose:
.env.development
.env.staging
.env.production
.env.test
Adopting one consistent pattern across an entire project or organization, rather than letting each project invent its own ad hoc naming, reduces the cognitive overhead of moving between different projects or onboarding someone new to a given codebase.
Maintaining a template or example file
A committed .env.example file, listing every variable an application expects along with a placeholder or description rather than any real value, serves as living, version-controlled documentation of exactly what configuration a service requires:
# .env.example
DATABASE_URL=postgres://user:password@localhost:5432/app
LOG_LEVEL=info
JWT_SECRET=replace-with-a-real-secret
FEATURE_NEW_CHECKOUT=false
This file should be kept genuinely in sync with the application's actual configuration needs, updated in the same change that introduces a new required variable, rather than treated as a one-time setup artifact that gradually drifts out of accuracy as the application evolves.
Never committing actual environment files with real values
Files containing genuine configuration values, particularly any environment beyond pure local development, should never be committed to version control, while the template file containing only placeholders is the appropriate thing to commit and share:
.env
.env.local
.env.*.local
!.env.example
A .gitignore pattern excluding actual environment files while explicitly allowing the example file through, as shown here, enforces this distinction structurally, rather than relying on every contributor remembering not to commit a real configuration file manually each time.
Consistent variable naming conventions
A consistent naming scheme across environment variables, whether a project-specific prefix, a logical grouping convention, or simply consistent casing, makes a growing configuration surface considerably easier to scan and reason about as the number of variables grows over time:
APP_DATABASE_URL
APP_LOG_LEVEL
APP_FEATURE_NEW_CHECKOUT
dbUrl
LOG_level
feature_NewCheckout
The first set follows one consistent, predictable convention throughout; the second mixes casing styles and naming patterns inconsistently, which makes it considerably harder to predict a given variable's exact name without checking, and increases the chance of a typo-driven configuration mismatch.
Grouping related variables for clarity
For an application with a substantial number of configuration values, grouping related variables together within the environment file itself, with comments delineating each group, improves readability considerably compared to one long, undifferentiated list:
# Database
DATABASE_URL=postgres://db:5432/app
DATABASE_POOL_SIZE=20
# Feature flags
FEATURE_NEW_CHECKOUT=false
FEATURE_DARK_MODE=true
# Observability
LOG_LEVEL=info
METRICS_ENABLED=true
This kind of lightweight organizational structure costs little to maintain but meaningfully improves the experience of anyone reviewing or modifying the configuration later.
Avoiding environment file sprawl
As a project grows, it is easy to accumulate an increasing number of environment files, separate ones for every conceivable variation, a specific feature branch, a specific tester's setup, a specific debugging scenario, which eventually becomes its own maintenance burden rather than a helpful organizational tool:
.env.development
.env.staging
.env.production
.env.alice-debug
.env.feature-x-testing
.env.temp
Periodically reviewing and removing environment files that no longer correspond to an actively maintained, genuinely distinct context keeps this from accumulating into unmanageable sprawl over time.
Documenting which variables are required versus optional
Distinguishing, directly within the template file or accompanying documentation, which variables are strictly required for the application to start versus which are optional with a sensible default, helps anyone setting up a new environment understand exactly what they genuinely need to supply:
# Required
DATABASE_URL=
# Optional (defaults to "info" if not set)
LOG_LEVEL=
This distinction, paired with the startup-time validation logic that actually enforces required values, keeps the documentation and the application's own enforced behavior consistent with each other.
Common mistakes
- Using an inconsistent or ad hoc naming pattern for environment files across different projects, increasing cognitive overhead when moving between them.
- Letting a committed example or template file drift out of sync with the application's actual, current configuration requirements.
- Accidentally committing an actual environment file containing real values rather than only the placeholder-based template file.
- Mixing naming conventions and casing styles across environment variables inconsistently, increasing the chance of a typo-driven configuration mismatch.
- Allowing environment files to accumulate indefinitely for every individual, temporary need rather than periodically reviewing and removing ones no longer actively maintained.
Environment config practice is the operational discipline that keeps the broader principle of config externalization actually usable day to day, consistent file naming, an accurately maintained template file, structural prevention of accidentally committing real values, and consistent variable naming, all of which reduce the friction and error rate of working with an application's externalized configuration over its entire lifetime.