12 factor app methodology

TL;DR

  • 1. One codebase tracked in revision control with many deploys
  • 2. Explicitly declare and isolate dependencies
  • 3. Store sensitive configs in the environment
  • 4. Treat backing services as attached resources
  • 5. Strictly separate build and run stages
  • 6. Execute the app as one or more stateless processes
  • 7. Export services via port binding
  • 8. Scale out via the process model
  • 9. Maximise robustness with fast startup and graceful shutdown
  • 10. Keep development, staging and production as similar as possible
  • 11. Treat logs as event streams
  • 12. Run admin/ management tasks as one-off processes

1. Codebase

One codebase tracked in revision control with many deploys

Like many of today's agencies we have one codebase per project tracked in a revision control systems with many deploys on daily basis.

we use GIT:
https://git-scm.com/

As our code repository of choice and we manage all our repositories through GitHub:
https://github.com/

It is standard for us to use to three environments (local: development | staging: testing | production: live). All three share the same codebase, same operating system, the same back services and the same dependencies.

The codebase will always support these environments with unique configurations.

2. Dependancies

Explicitly declare and isolate dependencies

Modern software relies heavily on the work of others, it's the very nature of open source technology. You must never assume that external libraries exist on the target system. Rather you should declare all dependencies and state the required versions.

By in large we use Composer:
https://getcomposer.org/

To handle all our dependencies for us. But we have also started to use:
Yarn

When we deploy to a server via Capistrano or Travis, we always ensure the latest dependencies have been installed on the server as part of the deployment process.

3. Configuration

Store sensitive config in the environment

You would never want sensitive data such as mysql username and passwords committed to your central code repository! Or you might want different configurations for each environment. It is for this reason we manage configuration items outside of the repository.

Each environment has it's own unique ENV details, that the app running on the server then relies on.

4. Backing services

Treat backing services as attached resources

Isolate backing services such as mysql or memcached and store these details in your configuration, that way if the network connection changes you only need to update one location.

For example, your development environment talks to a MySQL server on your local machine. On production it might run on a completely different server, you should only need to update your configuration with the differences for the web application to continue to work as expected.

Again these differences can be stored in the environment.

5. Build, release, run

Strictly separate build and run stages

Build should be different to the release, and the release should be different to the run.

1. The build stage is fully controlled by the developer. It's here that we create new versions, releases, tags and fix bugs.

codebase. + dependencies + assets

2. The release stages prepares the build for execution in the target environment (production | staging).

build + config

3. The run stage then executes the application and shouldn't need any intervention.

execute, no code changes

Automation of this stage will make this simpler for the whole team. GitHub can be used to tag your latest build, whilst Capistrano:
https://capistranorb.com/

And tools like Phinx:
https://phinx.org/

can be used to deploy your codebase to the environment (production | staging) with absolutely no interuption to the service. You further have the ability to rollback deployments in the case of problems.

6. Processes

Execute the app as one or more stateless processes

Stateless apps should degrade gracefully. If one part of the stack fails, then the whole app should not become a failure.

7 . Port binding

Export services via port binding

You application should be accessible via url, just as your backing services also (MySQL | Memcache):

        127.0.0.1:8000 - application
        127.0.0.1:3306 - mysql
        127.0.0.1:8025 - mailcatcher
        

By doing so you should be able to run your application though PHP's built in server:
https://secure.php.net/manual/en/features.commandline.webserver.php

This would be useful for local development or for integrating your app into Travis, where as for production or staging environments you would need a more complicated webserver on top.

8. Concurrency

Scale out via the process model

Each process required to run your application should be able to scale, restart or clone itself when required.

9. Disposability

Maximise robustness with fast startup and graceful shutdown

When you deploy new code to staging or production you want the new version to start straight away and deal with the incoming traffic. Similarly after a crash or new deployment your app should have everything it needs to run. Reloading the code should only take a few seconds max... Capistrano/ Travis handles the change of code effortlessly!

10. Dev/ prod parity

Keep development, staging and production as similar as possible

Your development environment should resemble production as close as possible. This means working on the same codebase, operating system, the same back services and the same dependencies. This reduces the amount of bugs and downtime, allowing your organisation to have a much more rapid development cycle.

This is why we all work on a vagrant box, which is provisioned by re-usable Puppet roles/ profiles. This configuration contains all the code to set up a perfect local development environment, and we can then define how our production and staging environment will work at the same time.

It prevents that "It's working on my machine" problem.

11 Logs

Treat logs as event streams

Logging is important for debugging and checking up on the general health of the application. But do you really want your application to be responsible for storage and maintenance of these logs? We make use of external services that help with the archival and analysis.

Examples include:

12 Admin Processes

Run admin/ management tasks as one-off processes

So your application has been running on production for some time, now you need to handle a database migration or fetch data for anaylsis. These `commands` are handled by Smyfony console and ensure that the same commands can be used on any of the environments, even when we are building complex sites on Travis.