12 Factor App
- 1 External
- 2 Internal
- 3 1. Codebase
- 4 2. Dependencies
- 5 3. Configuration
- 6 4. Backing Services
- 7 5. Build/Release/Run
- 8 6. Processes
- 9 7. Export Services via Port Binding
- 10 8. Scaling Out via the Process Model
- 11 9. Process Disposability
- 12 10. Development/Production Parity
- 13 11. Logs
- 14 12. Administrative Processes
- Crash-only software: More than meets the eye http://lwn.net/Articles/191059/
Multiple applications must not share code. Factor the shared code in libraries that can be included through the dependency manager. The codebase must be tracked in a revision control system.
An application must never rely on implicit existence of system-wide dependencies. It must declare its dependencies via a dependency declaration manifest, in both development and production. Twelve-factor apps also do not rely on the implicit existence of any system tools.
Configuration is everything that varies between deployments. The configuration information should be strictly separated from code. Configuration constants must not be stored in code. What about the default values that work in most of the cases and serve as fallback? Prefer configuration in environment variables. Environment variables are granular controls, each fully orthogonal to other environment variables. They are never grouped together as “environments”, but instead are independently managed for each deploy. This is a model that scales up as the application naturally expands into more deploys over its lifetime.
4. Backing Services
External resources (database, queueing systems, caches, SMTP servers, etc) are identified via an URL or other locator/credentials specified in configuration. Two equivalent resources should be swappable at any time without any change in code. Treat backing services as attached resources.
Strictly separate the build, release and run phases. Build Phase: is the transformation that converts code from repository into an executable bundle known as build. The dependencies are fetched at build stage. Release Phase: takes the build and combines it with the deployment's current config. The resulting release contains both the build and the config and it is ready for immediate execution in the execution environment. Really? Some of the config should stay in the runtime environment, not in the release. Releases are an append-only ledger and a release cannot be mutated once is created. Any new change must create a new release. Every release should always have a unique release ID.
Application processes are stateless https://en.wikipedia.org/wiki/Shared_nothing_architecture. Any data that needs to persist must be stored in a stateful backing service (database). The memory space or filesystem of the process can be used as a brief, single-transaction cache.
Design so different request can be served by different, independent processes: never assumes that anything cached in memory or on disk will be available on a future request or job; with many processes of each type running, chances are high that a future request will be served by a different process.
Sticky sessions are a violation of twelve-factor and should never be used or relied upon. Session state is a good candidate for Memcached or Redis.
7. Export Services via Port Binding
The application is completely self-contained, by exporting HTTP as a service by binding to a port. It declares its dependency on a web server library (Jetty) and the HTTP processing happens within the application code - in the user space. This can apply to other protocol (Redis, etc.)
8. Scaling Out via the Process Model
Processes should be first class citizens. Process management should rely on the operating system's process manager ( Upstart or Foreman) to manage output streams, respond to crashed processes and handle user-initiated restarts and shutdowns.
9. Process Disposability
Processes should be disposable, meaning they can be started and stopped fast (seconds). They should shut down gracefully when they receive SIGTERM from the process manager. For a web process, graceful shutdown means they refuse to accept new connections, wait until the current requests are served (HTTP request must be short) and exit. For long polling, the client should seamlessly attempt to reconnect when the connection is lost. From a worker, graceful shutdown is to return the current job to the queue. Processes should be robust against sudden death.
10. Development/Production Parity
Use the same backing services between development and production. Keep development, staging, production similar.
Application should see logs as aggregated, time-ordered event streams. They have not fixed beginning or end, but flow continuously as long as the app is operating.
Routing and storage of logs must not be the concern of the application. The application should not attempt to write or manage log files. Instead, the process should write its event stream, unbuffered, to stdout. The event stream will be captured by the application's execution environment, which will route it appropriately, process it and possibly archive it.
12. Administrative Processes
Run administrative processes as one-off processes.