Twelve-factor in practice: which factors still hold
Twelve-factor is a description of how one hosting platform expected applications to behave, written in 2011, and it remains the best short summary of the discipline anyone has produced. It is also treated as scripture by people who have not read it. Four of the factors do nearly all the work, three have aged badly enough that following them literally now produces worse systems, and the rest are either obvious or situational.
What is the twelve-factor app?
A list of twelve properties that make an application easy to deploy repeatedly onto a managed platform: one codebase, declared dependencies, configuration in the environment, backing services treated as attached resources, separate build and run stages, stateless processes, port binding, concurrency through processes, disposability, development and production parity, logs as event streams, and admin tasks as one-off processes.
The context matters for reading it. It was written by people running a platform that took your code, built it, ran many copies of it and could destroy any copy at any time. Every factor is an instruction for surviving that, which is why it maps so cleanly onto container orchestration a decade later.
What it is not is an architecture. It says nothing about how to divide a system, model a domain or handle consistency. Teams that treat a twelve-factor checklist as a design review are checking packaging while the design goes unexamined.
Which factors carry the most weight?
Four: configuration, backing services, stateless processes and disposability. Those are the ones that decide whether an application can be deployed twice, scaled sideways and killed safely, which is the entire practical benefit. The remaining eight range from useful to no longer meaningful.
A quick way to rank them for your own codebase is to ask which one, if violated, would stop you running a second instance today. That is almost always where the actual work is.
| Factor | What it actually asks for | The violation you will find |
|---|---|---|
| III. Config | One build artefact that runs in every environment unchanged | A settings file per environment, baked into the image at build time |
| IV. Backing services | Any attached resource swappable by changing a URL | Hardcoded hostnames and a code path that only exists in production |
| VI. Processes | Nothing important held in process memory or local disk | In-memory sessions, and an upload directory nobody remembers writing |
| IX. Disposability | Fast start, clean SIGTERM handling, work safe to interrupt | A boot sequence that warms a cache for two minutes |
| X. Dev/prod parity | The same kinds of backing service everywhere | SQLite locally and Postgres in production, so migrations only fail late |
| XI. Logs | Write to stdout and let the platform route it | The application managing its own log files and rotation inside a container |
Where has twelve-factor aged badly?
Three places. Factor VIII, concurrency through the process model, was written before async runtimes were normal and before container orchestrators handled scaling. Scaling out by forking processes is now the platform's job, and inside a single instance the right concurrency model depends entirely on the runtime rather than on a general rule.
Factor XI, logs as event streams, is still correct about the direction of travel and now underspecifies the job. Writing unstructured lines to stdout is a smaller win than emitting structured events carrying a trace identifier, because the thing that actually shortens an incident is correlating one user's request across services. Treat the factor as the floor rather than the target.
Factor XII, admin processes, assumed you would attach to a running instance and execute a one-off script. Under an orchestrator that is an anti-pattern: the correct form is a separate job, using the same image, with its own resource limits and its own audit record. The intent survives, the mechanism does not.
Which factor breaks the most pipelines?
Configuration, by a wide margin, and the failure is subtle because the codebase looks compliant. The requirement is not that values arrive in environment variables. It is that a single build artefact is promoted unchanged from testing to staging to production, with only its environment differing. An image built per environment fails the factor even if every value it contains came from an environment variable at build time.
The reason this matters is that the artefact you tested is then not the artefact you shipped. Every environment-specific build reintroduces the class of bug where staging passes and production fails for reasons nobody can reproduce, which is precisely the class the factor exists to remove.
The test is one command. Take the image tag currently running in production, run it against staging configuration, and see whether it works. If it needs rebuilding, your pipeline has an environment-specific artefact and the configuration factor is not satisfied, whatever the code review said.
Is development and production parity actually achievable?
Not fully, and pretending otherwise wastes months. Your laptop will not have the production data volume, the network latency between availability zones, the identity provider or the traffic pattern. Chasing exact parity leads to development environments so heavy that nobody runs them.
What is achievable, and what the factor is really about, is parity of kind. The same database engine at the same major version, the same message broker, the same object storage interface. Substituting SQLite for Postgres or an in-memory queue for the real broker moves an entire category of bugs from your laptop, where they are cheap, to production, where they are not.
The practical version is a container compose file that starts the real backing services locally, seeded with a small but representative dataset. Anything harder than one command to start will decay, and a development environment that has decayed is worse than none because people trust it.
How do you assess a codebase against it without checklist theatre?
Run four checks rather than twelve. Can the current production image start against staging configuration without a rebuild? Can two instances run at once and share nothing but the backing services? Does the process exit cleanly within its grace period when sent SIGTERM under load? And does it start fast enough that an autoscaler adding an instance helps before the spike has passed?
Those four cover the factors that matter and are all observable, which is the point. A checklist answered from memory records the intentions of the codebase. A test answered by running the thing records its behaviour, and they are frequently different documents.
If all four pass, the remaining factors are housekeeping and can be dealt with as they surface. If any fails, that failure is the next piece of work, and it is worth more than any amount of architectural discussion about what to split next.
Common questions
- What is the twelve-factor app methodology?
- A 2011 list of twelve properties that make an application easy to deploy repeatedly on a managed platform: one codebase, declared dependencies, configuration in the environment, backing services as attached resources, separate build and run, stateless processes, port binding, concurrency via processes, disposability, dev and production parity, logs as event streams, and admin tasks as one-off processes.
- Is the twelve-factor app still relevant?
- Mostly. Configuration, backing services, stateless processes and disposability still decide whether an application can scale sideways and be killed safely. The concurrency factor has been superseded by container orchestration, the logging factor now underspecifies what is needed because structured events with trace context matter more than plain lines, and the admin-process factor describes a mechanism orchestrators handle differently.
- Does twelve-factor mean putting configuration in environment variables?
- Not exactly. The requirement is that one build artefact runs unchanged in every environment, with only the environment differing. Environment variables are one way to achieve that and mounted files are another. Building a separate image per environment fails the factor even when every value inside it arrived through an environment variable during the build.
- What is dev/prod parity and how close does it need to be?
- Parity of kind rather than parity of scale. The same database engine at the same major version, the same message broker, the same storage interface, so that a whole category of bugs surfaces on a laptop instead of in production. Matching data volume, network topology and traffic patterns is neither achievable nor necessary, and chasing it produces development environments too heavy to use.
- Is twelve-factor an architecture?
- No. It describes packaging and runtime behaviour, and says nothing about how to divide a system, model a domain or handle consistency. A perfectly twelve-factor application can still have a badly drawn service boundary or a data model that makes every feature expensive. Treating the checklist as a design review checks the wrapper and leaves the design unexamined.