The twelve-factor app

I do not normally do "look-it-I-found" posts, but sometimes you encounter some article, or even a whole community, that makes you go "This is exactly what I feel!"

The Twelve-Factor App is such an article. It collects in one compact article several relevant points about writing software-as-a-service, that I have been trying to make. In particular, the article articulates the relationship between such things as dependencies, releases and deploys, which I have long felt to be marginalized in the greater software development discussion.

Before you think "But I don't do SaaS stuff," be advised that all of these recommendations are good practices even for more traditional software development. In fact, I think it excessive modesty on the part of the author to limit himself to this scope.

I agree with and would emphasize each and every rule in The Twelve-Factor app, bar one. I have one real piece of beef with the article, namely its view of configuration.

The world is not so simple

Says section III:

The twelve-factor app stores config in environment variables (often shortened to env vars or env). 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.

There are several problems with using environment variables as the main configuration vehicle:

  • environment variables can not easily be changed after startup,
  • many applications have far too much configuration for environment variables to be useful, and
  • environment variables are too visible.

Changes after startup: The article envisions that running a process should perform a task once and then return/exit. Thus, configuration changes can occur between restart. However, there are numerous legitimate use cases where you want to change configuration of a running process inside the scope of its task. For example, you may have a web trawling process and you want to change its logging verbosity or what back-off delay it uses.

Configuration volume: Certain applications have a large volume of configuration. In particular, commercial software typically pass several slightly different use cases over its lifetime as it passes from one financing customer to the next. This means it will accumulate configuration options to select between those use cases or simply need to be configurable by customer or similar. This will quickly grow unmanageable if set through environment variables. One may argue that these are not optimal circumstances for software development, but the alternative is typically bankruptcy.

Environment variables are too visible: It is difficult to manage security of environment variables. Some ways they may leak:

  • ps e
  • Set in shell before startup
  • .bash_history or .zhistory

By contrast, a configuration file can be referred to by name without revealing its content, which makes it much preferable; most configuration files will end up containing credentials sooner or later.

Making room for configuration management

The brilliance of The Twelve-Factor App is that it carves out a distinct niche for Configuration Management, essentially by excluding it from the software development process altogether. Also, it pushes much of process management to systems administrators, by empowering them.

However, those systems administrators may have to manage numerous different installations with different configuration sets. For example, an organization may run one instance per customer. At this point, the configuration will have to be versioned and managed in its own right, whereby the environment variables will have to be written to and loaded from file anyway.

Alternative recommendation

While environment variables may be useful to name the configuration file or configuration backing service, using them as the main configuration strategy will lead to grief in most non-trivial cases. It may work in the early stages of the life cycle, but sooner or later, chances are you will out-grow that strategy.

Rather, my recommendation would be to use either of two strategies, depending on the use case:

  • plain configuration file, using whatever standard tools are available in your environment, or
  • configuration backing service, when configuration becomes complex; in particular, when different processes need to behave differently depending on e.g. connecting user.

This critique notwithstanding, my hat is off to Adam Wiggins of Heroku fame.