Environment variables
General rules
- Only use environment variables when it makes sense. If values are unlikely to change across environments, consider using constants instead.
- Every environment variable should be configurable during local development by setting a value for that environment variable. Code should not have to be modified to test different environment variable values.
- If possible, use some type of centralized settings object to store environment variable values. This will make validation, testing and usage management easier. As an example, environment variables should only be referred to in the settings file of a Django web service, and then the settings object is used throughout the code. Note that this may not be possible for environment variables controlling functionality in dependencies.
- Values for environment variables should only be set if they need to be. If a default value is provided, and this default value should be used, don’t also explicitly set the value.
Categories
Environment variables are categorized into the following:
- Required: These environment variables are required for the core functionality of the code to run successfully. What is considered “core functionality” is discretionary and varies case by case. If the environment variable being unset would lead to an unhandled exception or the service fails to start up, then the environment variable is considered required.
- Conditional: These environment variables are only required for some non-core functionality to run successfully. Different functionality may require different subsets of these environment variables to be set. This may also include environment variables that are only set in local environments.
Default values
Some environment variables have default values which support the typical functionality of the code. These default values should be configured according to their scope:
- Global: These default values apply to all environments. This includes production, so ensure the value is appropriate. If possible, this value should be configured in the source code and applied in the absence of a value being explicitly set e.g. providing a default value for a call to
os.getenv. - Local only: These default values apply to every instance of local development only. If possible, this value should be configured in a configuration file, but in such a way that it is only set for local development e.g. setting a default value in a
docker-compose.yamlfile that is only used for local development, a.env.examplefile, etc. If the code does not provide a mechanism for setting shared local default values, just include the value in the documentation.
In general, every environment may have different values for its environment variables, so when using default values be careful that incorrect values are not being unintentionally used. Default values should be chosen to optimize performance and behavior for the majority of environments or instances within their scope. For globally scoped environment variables in particular, consider whether they would be better implemented as constants instead.
Using environment variables
Python
Environment variables should only be accessed in code within a single module that is executed on startup. In Django services, this would be the Django settings file. In other use cases this may be a pydantic-settings class, or just within the entrypoint script.
When not using a higher-level environment variable management tool like pydantic-settings, any environment variables should be accessed using the EnvironmentVariableParser class (def) from service utils. This will ensure that all environment variables are consistently typed and parsed, and that environment variable errors are user friendly and consistent. For most use cases, this would be using either the environment_variable or required_environment_variable functions (def).
Documentation
Each category of environment variables that a repository uses should be documented in an Environment variables section of the README.
There should also be a list of which environment variables are required to be set for local development listed under a Local development heading. Typically, these would be any required environment variables that don’t have a default value.
Environment variables should be listed in alphabetical order within each category.
An example is as follows:
## Environment variables ### Required | Variable | Description | Has default value | | --- | --- | --- | | `OIDC_CLIENT_ID` | OIDC Client ID. Available from OIDC provider. | Global | | `OIDC_CLIENT_SECRET` | OIDC Client secret. Available from OIDC provider. | - | | `OIDC_TOKEN_URL` | Token URL for OIDC provider. | Local only | ### Conditional | Variable | Description | Has default value | | --- | --- | --- | | `SENTRY_DSN` | DSN required for Sentry integration. Available from Sentry. | - | ### Local development The following environment variables are required to be set for local development: - `OIDC_CLIENT_SECRET`