Published on

Separating Config from Code

Authors

When working on a codebase intended to be run in multiple environments you will ultimately be confronted with the decision of where to put your configuration?

The obvious options are:

  1. Have some code somewhere that checks the environment and uses certain configurations
  2. Use some sort of config file to store the configuration. Have a file per environment and bundle the correct environment at build time.

The Dangers with Mixing Configuration Setup In Code

Just to clarify what this means you have some service or class that has business logic in it. Among this business logic, there is code similar to:

if(env == "test"):
    url = test_url

The approach is incredibly dangerous for a few reasons:

  1. If you need a particular configuration in multiple places you end up copy-pasting this across places
    • Duplicating code more than twice is generally considered the rule of thumb that one uses to abstract this out. But with a configuration like this, it is really risky duplicating even once. For example a URL changes, oops you forgot to change it in both/all places (you basically have to do shotgun surgery every time you make a config change).
    • This can potentially lead to a situation where part of your code thinks you are in a test environment while the other part thinks you are in prod. This is obviously incredibly dangerous as test data is often way out of sync and completely unrealistic when compared to production data
  2. Your code could end up behaving completely differently in different environments due to a bug in your config set up in the code
    • Even with unit tests you may miss one or more configuration branches
    • Code behaving differently in production versus other environments is obviously something we strive to avoid as developers.
  3. Each additional environment you add needs additional if-else code to support it.

A Better Solution - Separate Configuration from Code

Nowadays this is really easy to do using straightforward property files or things like .env files.

I personally lean towards .env files which are fairly straightforward and supported in many languages. I also like them because:

  • Out of the box you can easily have a different .env per environment. For example: .env.development and .env.production
  • .gitignore the .env file or/and the .env.local allowing devs to have different configurations on their machine vs everyone else without affecting anyone.
  • Most .env frameworks allow you to specify environment variables as command-line arguments which takes precedence over the values in the file. This is perfect for sensitive config where these can be passed as command-line variables when the app is started by the operations team (no need to commit sensitive production configurations to version control)
  • There is only one standard place you need to look when trying to determine the configuration for something

One really cool approach is to pull in all env variables in one file. When you need to use these configs in the code you simply import this one module and get the variables you need from it. This helps further contain your config variables to 2 places. For example, in Python we define this settings.py file:

import os
from dotenv import load_dotenv
load_dotenv()

SECRET_KEY = os.getenv("EMAIL")
DATABASE_PASSWORD = os.getenv("DATABASE_PASSWORD")

And we use it in another file as follows:

from path.to.your.settings.file import settings

#...

key = settings.SECRET_KEY