- Declarative formats for setup automation
- Clean contract with the underlying operating system for portability between execution environments
- Deployment on modern cloud platforms
- Minimal divergence between development and production for continuous deployment
- Scalability without significant changes to tooling, architecture, or development practices
The contributors to this document have developed and deployed hundreds of apps, and indirectly witnessed the development, operation, and scaling of hundreds of thousands of apps via their work on the Heroku platform. This document pays particular attention to:
- The dynamics of the organic growth of an app over time
- The dynamics of collaboration between developers working on the app's codebase
- Avoiding the cost of software erosion
Its motivation is to raise awareness of some systemic problems in modern application development and provide a shared vocabulary for discussing those problems. It offers a set of broad conceptual solutions to those problems with accompanying terminology. It is inspired by Martin Fowler's books Patterns of Enterprise Application Architecture and Refactoring.
- Developers building apps which run as a service
- Ops engineers who deploy or manage such applications
A twelve-factor app is always tracked in a version control system. A copy of the revision tracking database is known as a code repository (Code repo/repo). A codebase is any set of repos who share a root commit.
There is always a one-to-one correlation between the codebase and the app. If there are multiple codebases it's not an app, it's a distributed system. Each component is an app and can individually comply with twelve-factor. Multiple apps should not share the same code and instead should factor shared code into libraries included through the dependency manager.
A deploy is a running instance of the app. There is only one codebase per app, but there will be many deploys of the app. This is typically a production site, and one or more staging sites. Every developer has a copy of the app running in their local development environment which also qualify as deploys.
The codebase is the same across all deploys, although different versions may be active in each deploy. A developer may have some commits not yet deployed to staging while staging has some commits not yet deployed to production. Since they all share the same codebase they are identifiable as different deploys of the same app.
Libraries installed through a packaging system can be installed either:
- System-wide (site packages)
- Scoped into the directory containing the app (vendoring/bundling)
A twelve-factor app never relies on implicit existence of system-wide packages
It declares all dependencies via a dependency declaration manifest and uses a dependency isolation tool during execution. This ensures that no implicit dependencies "leak in" from the surrounding system. It is applied uniformly to both production and development.
This simplifies setup for new developers who can now check out the app's codebase onto their development machine. They just need the language runtime and dependency manager. Everything needed to run the app's code is setup with a deterministic build command.
Twelve-factor apps do not rely on the implicit existence of any system tools such as shelling out to
curl. There is no guarantee these tools will exist on all systems where the app may run. The tools should be vendored into the app.
An app's config is everything that is likely to vary between deploys (staging, production, developer environments) including:
- Resource handles to the database or other backing services
- Credentials to external services such as Amazon S3 or Twitter
- Per-deploy values such as the canonical hostname for the deploy
Apps should not store config as constants in the code. Twelve-factor apps require strict separation of config from code. Config varies substantially across deploys while code does not. All your config has been correctly factored out of the code if your app could be open sourced without compromising any credentials.
Twelve-factor apps store config in environment variables (env vars/env). These 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. Unlike custom config files they are language and OS agnostic.
Sometimes apps batch config into named groups (environments) for specific deploys (
production). New environment names become necessary as more deploys of the app are created (
qa). Developers may add their own special environments causing a combinatorial explosion of config.
Env vars are granular controls in twelve-factor apps, each fully orthogonal to other env vars. They are never grouped together as environments. They are independently managed for each deploy so they can scale up smoothly.
A backing service is any service the app consumes over the network as part of its normal operation such as:
- Datastores (MySQL or CouchDB)
- Messaging/queueing systems (RabbitMQ or Beanstalkd)
- SMTP services for outbound email (Postfix)
- Caching systems (Memcached)
Databases are traditionally managed by the same systems administrators deploying the app's runtime. The app may also have services provided and managed by third parties such as:
- SMTP services (Postmark)
- Metrics-gathering services (New Relic or Loggly)
- Binary asset services (Amazon S3)
- API-accessible consumer services (Twitter, Google Maps, or Last.fm).
The code for a twelve-factor app makes no distinction between local and third party services.
Both are attached resources accessed via a URL or other locator/credentials stored in the config. A deploy of the twelve-factor app should be able to swap out a local MySQL database with one managed by a third party (Amazon RDS) without changing the app's code. Similarly, a local SMTP server could be swapped with a third-party SMTP service (Postmark) without code changes. Only the resources handled in the config need to change.
Each distinct backing service is a resource. A MySQL database is a resource. Two MySQL databases sharded at the application layer qualify as two distinct resources. These databases are treated as attached resources, indicating their loose coupling to their attached deploy.
Resources can be attached to and detached from deploys at will. If the app's database has hardware issues, the app's administrator might spin up a new database server from a recent backup. The current production database could be detached and the new database attached. This could be done without any code changes.