dwyl/env2

Confusing: not actually 12 factor app config approach

Closed this issue · 11 comments

env2 looks like a nice way to manage config, it's just not the 12fa way to manage config. As it currently stands, env2 doesn't actually allow you to take in config from the environment! :)

Unless I'm missing something, 12fa explicitly calls out the pattern in env2 as an anti-pattern, and provides good reasons to avoid it:

Another approach to config is the use of config files which are not checked into revision control, such as config/database.yml in Rails. This is a huge improvement over using constants which are checked into the code repo, but still has weaknesses: it’s easy to mistakenly check in a config file to the repo; there is a tendency for config files to be scattered about in different places and different formats, making it hard to see and manage all the config in one place. Further, these formats tend to be language- or framework-specific.

Env vars are easy to change between deploys without changing any code; unlike config files, there is little chance of them being checked into the code repo accidentally; and unlike custom config files, or other config mechanisms such as Java System Properties, they are a language- and OS-agnostic standard.

@timruffles how do you solve this problem? (we really want to know the right way of solving this!)

Also, we specifically do not over-write any environment variables if they were set in the environment
see:

env2/lib/env.js

Lines 32 to 34 in b431b97

if(!process.env[k]) { // allow enviroment to take precedence over env.json
process.env[k] = env[k]; // only set if not set by environment
}

And wrapping the code in try/catch means that requiring env2 will fail (relatively) silently if there is no config.env or env.json so actual environment variables take precedence.

Last time I took the 12fa approach I stored all config in ansible (~ whichever deploy/automation tool you use), and then built the environment for each app programatically when the infrastructure was created. I ended up with a service.sh file run by a process manager (runit, systemd, pm2 etc) which set the environment and the booted the app:

#!/bin/bash

export DB=protocol://someone:pass@somehost.com/database
export FOO=bar

exec node my-app.js

Inside the app I normally loaded in the config via getenv with defaults for simple development[1], the defaults being disabled in production via env.disableFallbacks().

[1]: e.g var REDIS_URL = getenv("REDIS_URL", "tcp://localhost:6379");

Also, like I said, the env2 'config in repo and in .gitignore' is a great way to do config: it works, it's simple, it's used by 1,000s of teams every day. I think it's actually better for smaller projects/teams that don't deploy to 10-100s of servers, as 12fa config requires extra faff and solves problems they don't have. The 12fa way is designed for large cloud deployments, immutable infrastructure etc - it's not the 'right way' for any and all projects.

I <3 env2: I just think you should take the 12fa reference out of the readme :)

Right, but I don't see how this is approach is an "anti-pattern" ...
What env2 allows is:

  • a simple(er) way to load any environment variables you have from a file while in development
  • reference the environment variable using the standard node.js way: process.env.VAR_NAME
  • no need to invoke a getter method to access environment variables ( ... we were trying to avoid env.getVar('MY_VAR') everywhere )
  • if the variable has been set in the environment (either by exporting it or in a bash script, etc.) variable does not get over-written. (i.e. complies with 12fa)
  • Teams of developers can share the .env or env.json file in a private way (e.g: Google Docs or Private Repo) and exclude them from GitHub in the .gitignore file.

Our original problem was that iTerm _does not remember_ environment variables which we export from one session to the next so we kept having to export them by pasting a list of export commands.

It's a great pattern for lots of (most?) people, as I said above: it's an anti-pattern for people with the problems 12fa solve, for the reasons in the 12fa site.

I just find it odd to take an approach that is not 12fa, and then quote 12fa. It confused me and it'll confuse other people, and perhaps then they won't take the 12fa approach when it's useful :)

But we can still use ansible or salt or jenkins or codeship to deploy our app while conforming to 12fa ... env2 works both locally (while developing) and anywhere we control the steps in the deployment process but _degrades gracefully_ when invoked in production.

Sorry if I'm being a bit thick here... but I want to understand what is wrong with this approach...

As said a few times, I <3 env2, I <3 the approach which I use often and will keep using, maybe even via env2.

I just can't see the point of quoting a very specific approach to config when env2 does something completely different. It's your repo tho!

Would it be betterer if the Readme did not open with mentioning 12fa then?

Sure, or even only mention it under "other approaches that might work for you" :)