What is a Twelve-Factor application?
If this is the first time you’ve heard the phrase, read the entire writeup here. If you’re kicking around moving into a micro-services architecture or thinking about moving into a containerized environment, read about all of the factors until you have them memorized. It will save you time and heartache.
In addition to the benefits called out in the article, a Twelve-Factor application can make things easier in a number of ways:
Automation of your deployment involves installing dependencies, set the environment (configure the app), and run the app! There should be no writing of files or scary
cp statements in your deployment automation. As a developer, if you’re writing twelve-factor applications, it will make it easier for you to deploy your application, and it will make it easier for any operations folks you’re working with as well.
How often have you seen tokens, keys, or passwords committed to repos? I’m sure you’ve seen it a lot. There are bots that crawl GitHub and BitBucket looking for secrets for nefarious purposes. This is a real thing. Sometimes committed secrets are due to people not knowing any better, sometimes it’s just a mistake. By using your environment for configuration, you don’t have to worry about these kinds of mistakes. If people are putting secrets into the code once you’ve moved to environment configuration, it’s a training issue.
The idea of configuring in your environment means that you are not committing sensitive information. Here’s an example of something you wouldn’t want to commit to configure a Rails app:
default: &default adapter: postgresql host: postgres.example.com user: appuser password: n3v3rG0nn4G1v3Y0uUp pool: 5 timeout: 5000
If we commit the above, we’re just asking for trouble. Using environment configuration, we can completely remove the database configuration from this file.
default: &default adapter: <%= ENV['DATABASE_ADAPTER'] > host: <%= ENV['DATABASE_HOST'] > user: <%= ENV['DATABASE_USER'] > password: <%= ENV['DATABASE_PASSWORD'] > pool: <%= ENV['DATABASE_POOL'] > timeout: <%= ENV['DATABASE_TIMEOUT'] >
This may look like a headache in terms of configuration initially, but once you’ve setup how your environment is laid down, your application is now much more portable. If you are building a twelve-factor application, this configuration could be all you need for your database setup. If your development team does a deployment for every branch for testing or QA, having a configuration like this means whatever is controlling your deployment will handle the configuration, and you don’t need to have a configuration per branch or environment.
Flexibility in Configuration
I’ve highlighted some of the security benefits in configuration, and now we’ll highlight some of the flexibility available. When someone wants to contribute to your project, you can make it easier for them to get started by offering defaults. This can also be useful for testing. Here’s an example:
default: &default adapter: <%= ENV['DATABASE_ADAPTER'] || 'postgres'> host: <%= ENV['DATABASE_HOST'] || 'localhost' > user: <%= ENV['DATABASE_USER'] || 'dev' > password: <%= ENV['DATABASE_PASSWORD'] || 'devpass'> pool: <%= ENV['DATABASE_POOL'] || '5'> timeout: <%= ENV['DATABASE_TIMEOUT'] '5000' >
With sensible defaults, contributors have an easy way to get started without having to put in a lot of environment configuration. This can help with things like local testing.
Simplify your Application
By following the concepts of a twelve-factor application, you completely separate the logic of your deployment from your applications. This is excellent. Your application will not need to make decisions based on what environment it is in, the environment will manage what the application should talk to, and how it should be configured. This will make your application more portable.
Dev/Prod Parity is a Thing
Pretty much every development manifesto, principle, methodology and practice talks about releasing frequently. If you make a change, make sure your pipeline is setup so you can get that change tested, validated, and pushed to production as soon as possible. The further apart your development and production environments get, the more likely you are to have issues with a release. If you get nervous deploying, focus on what you need to do to become comfortable releasing. Whether you release once a day or 50 times per day, you should feel comfortable with your process.
Scrum practitioners will emphasize the importance of keeping dev and prod in sync. There are some very smart people have done studies and found the longer it takes to find issues or bugs, the more expensive they are to fix.
Other Uses for Twelve-Factor
If you’re a Mac or Linux user who spends a lot of time at the command prompt, things like managing your configuration via the environment can be very useful. I use the environment to manage a lot of my configuration. Fire up a new shell and see how many things are set in your environment by default. Here’s mine:
➜ ~ env | wc -l 58
On a pretty vanilla Ubuntu image I fired up, that count is 18 lines.
If you’re doing development work locally, the dependency declaration and isolation is huge. Don’t use the system installation of any language. If you mess something up at the system level, recovery can be very painful.
All of the most popular languages come with tools for managing development environments. Here are the tools I use to manage environments across a few languages:
If you’re not already developing your applications using at least some of the factors of a twelve-factor application, you should start now! Adopting these principles will simplify your application, make it easier to deploy and make your application more portable across environments and providers.