What follows are my own notes and summations after reading what makes a 12 factor application. Reading this has been a great glimpse into why we do certain things.
I’m currently working with a friend (Ginger Nomad) on building a web app. He is the expert so I follow his lead even though it seems needlessly complex at times.
After reading this, I get it. Why obfuscate the config.js file and the .env files with these additional node modules? Because it’s smart. This document helped me get off the, “Why are we making this so complex”-train.
That’s the why. Here are my notes.
A deployment is a running instance of an application. This of a deployment like a play. Trey and Matt wrote the Book of Mormon, but the Eugene O’Neill Theatre puts an instance of the play on when they perform it.
Each application should have a single codebase. If multiple apps make up a software project, that isn’t an app. It’s a distributed system made up of apps. Each app that makes up a distributed system should be a 12 factor app on it’s own.
A 12 factor app doesn’t depend on system-wide packages. When building a 12 factor app, I should be able to send a new developer the project and they should be able to run it with a working version of node and a simple npm install XXX command.
12 factor apps should contain everything that is likely to vary between deploys in a single place. This is often the .env file for us NodeJS developers. This file will include things like:
- Database Connection Keys – URI, Username, Password, etc.
- External service credentials – API keys for example
- Canonical values like hostname – We use path – npm
When we build NodeJS apps, we use the env – npm package to put all the config information in one place.
They warn against storing config variables as constants across the whole application. I’m unsure if this is for security reasons, or if it’s because they just want all the config variables in one place. When deploying multiple instances of an application, it makes sense to have all that data in one place for easy (or algorithmic) editing.
IV. Backing Services
There should be no distinction between local and third party services. To me, this means the app should be able to swap a local MongoDB with a database hosted on mLab, for example, without changing the codebase.
Other backing services could include Amazon S3, Google Maps, Twitter, and Postmark to name a few.
Why? Imagine you’re running an instance of your application and the server gets hit by a bus. That would cause the server to stop working and the database to explode into an inferno. How would you clean it up?
Well, because your backing services are all connected the same way, you would just adjust the config file and create a new instance of the database from backup. You could be back up and running in 10 minutes.
V. Build, Release, and Run
12 factor application development calls for separated build, release and run stages.
- Build Stage – Where you transform a code repo into an executable bundle. This is where the server fetches dependencies and compiles the assets.
- Release Stage – Combined build procedure with deploy’s config resulting in a release which can actually be run by humans.
- Run Stage – Here you run the app by running it’s processes against a selected release. Otherwise put, you use it. Click away brave soldier!
The document calls for executing the app as one or more stateless processes. These processes should be stateless and share-nothing. All persistent data should be stored in a backing service like a database.
To be honest, I don’t entirely get this. I relate it to the way Redux adjusts state in single bursts, but I’m unsure if that is a worthwhile comparison (let me know in the comments below if you know more about this).
VII. Port Binding
12 factor apps should be self-contained and should not rely on runtime injections of a web-server into the execution environment.
I don’t know what self-contained means exactly. It’s possible that NodeJS was built to be self-contained so I’ve never experienced this problem. When building a Node application, one of the first steps is to set the port for the server to listen from.
Processes are first-class citizens.
In 12 factor apps, the processes share nothing and are horizontally portionable so adding concurrency is simple and reliable.
To me this is like… returning books at the library. The application is the library and it can put a box out to collect returned books. If the library has an especially busy day of book collection, they can just put out an additional box. That way the two boxes are accepting books at the same time, but they books aren’t getting mixed up or returned out of order….
Does that make sense? If you have something to add, please do so in the comments below.
Robustness in applications is maximized with graceful shutdowns and fast starts. Therefore, 12 factor apps should be able to be stopped and started at a moments notice.
SIGTERM is the signal from a process manager which should be able to shut down your application.
Idempotent – A property of an operation that means the operation can be applied multiple times without changing the result beyond the initial application.
In the article they mentioned a database technology called CouchDB. I’d like to return to it one day. I love the idea of idempotent operations.
X. Development and Production Parity
Development, staging and production should have similar execution environments. Also, they time distance between a developer making a change and the production side of the app seeing that change should be short.
No more working on code for six months and launching it once. Continuous development is an important characteristic for 12 factor apps.
12 factor apps should treat logs as event streams. Therefore a 12 factor app should not be concerned with routing or storing it’s output stream.
XII. Administrative Processes
Administration processes should be one-off processes.
Administration code should also ship with the application code and run against a release using the same codebase and config process.
12 Factor App Checklist
- Does your application require a single code base?
- Are all required external dependencies loaded into the app? Can a new node developer run ‘npm install’ and get it working?
- Are all your config variables and settings in a single file?
- If you pushed your codebase to an open source repository like GitHub, would your config information be automatically hidden?
- Does your codebase interact with backing services without editing the codebase?
- Are your build, release and runs stages strictly separated?
- Are your application’s processes stateless? Do they share nothing with other processes? Is your backing service stored in persistent backing services?
- Does your app NOT require injections of a webserver?
- Have you bound processes to a specific port?
- Does your app NOT daemonize or write PID files?
- Can your app be started and stopped at a moments notice without losing important data?
- Are the development and production environments similar?
- Does your app not care if it’s storing logging output streams?
- Does your admin code ship with the application? Is the admin code connected to the release number, the codebase and the config file?
If you answered yes to tall the above questions, it seems you have a 12-factor app. Please let me know what it is in the comments below as I’d like to see what you’re working on.