Separate config and code Link to heading
When starting out on a fresh project we always prioritise speed over correctness/best practice. This is no more evident when it comes to configuration in code. Often in our eagerness to bring our idea to life we store our configuration as close to our code as possible. Things like database credentials and API tokens are hard coded as constants in our source code to reduce friction.
The Twelve-Factor App highlights this as a violation
of the twelve-factor app, which requires “strict separation of config from code”.
There are several approaches to implement the separation of config and code.
For example, in Django, configuration is included in the
One approach would be to have a development settings file checked into the repo
and have a production module for when the application is deployed.
Whilst much better than storing database passwords in constant variables it is
You could imagine with this solution how the production file could be accidentally
committed to a repo and exposed to the outside world.
A tidier solution would be to have a settings module that reads in environment specific
values from the environment.
os.environ can be used to implement this solution.
There are other more sophisticated solutions, such as python-env.
For Django, we would have one
settings.py file which reads secret values from
There are several ways to add variables to your environment.
Configuring Apps using Environment Variables Link to heading
An initial solution would be to add the secret environment variables to
This would set the environment variables globally and doesn’t scale very well,
it becomes more difficult if you worked on two or more projects that had the
same variables to be set.
A better solution is to have a collection of scripts to set and unset project environment variables.
# set.sh export DATABASE_NAME=database_name export DATABASE_PASSWORD=my_secret_password export DATABASE_USER=user # unset.sh unset DATABASE_NAME unset DATABASE_PASSWORD unset DATABASE_USER
When starting work on a project you would run the
Once finished we’d run the
unset.sh script before beginning work on another codebase.
This is better than having global environment variables but requires the developer to remember to run the scripts before and after working on a codebase.
If only there was a way to do this for us automatically?
Using direnv for Development Link to heading
This is where direnv comes in. Once configured and activated it automatically sets and un-sets environment variables when the developer changes into the project directory.
direnv works with both macOS and Linux.
It comes with a bash installer that simplifies the installation process.
curl -sfL https://direnv.net/install.sh | bash
direnv then needs to be configured to hook into your shell.
The instructions for this can be found here.
direnv simply create a
.envrc file in the root of project.
.envrc will contain all the environment variables you wish to export into the
export DATABASE_NAME=database_name export DATABASE_PASSWORD=my_secret_password export DATABASE_USER=user
As a security precaution
direnv won’t automatically activate these environment
On adding the environment variables to the
.envrc file the developer must run
direnv allow to signal to
direnv that it is okay to export the environment
variables in the file.
This command only needs to be run if the file changes.
Now every time you change into the directory (or child directory) the environment variables will be automatically exported. Similarly, on leaving the directory the environment variables will be unset.
Since the application settings are being read from environment variables no code has to be changed to accommodate deployment to production.
direnv has more settings to improve your workflow.
For example it has the
edit sub-command that will open the
in your editor choice and automatically run
direnv allow when the file is saved.
To take advantage of this sub-command ensure that the
EDITOR environment variable
export EDITOR=vim direnv edit
In this example
.envrc will be opened with
vim, upon saving and exiting
direnv allow is run.
direnv also comes with a set of functions that make command tasks easier to complete.
PATH_add is a function that can be used in
.envrc files to easily prepend paths
to the users
Imagine your project contains a
bin directory with a set of helper scripts
that you want to call only when working on your current project.
.envrc file we could call the
PATH_add function to add
PATH only when in the project directory.
# .envrc PATH_add ./bin
direnv contains lots of other useful functions in its standard library.