What you'll learn
- Quick answer
- Why the Methodology Exists
- Factors I-IV: Codebase, Dependencies, Config, Backing Services
- Factors V-VIII: Build/Release/Run, Processes, Port Binding, Concurrency
- Factors IX-XII: Disposability, Dev/Prod Parity, Logs, Admin Processes
- How This Maps Onto Docker and Kubernetes
- Where the Methodology Shows Its Age
- FAQ
Quick Answer
The Twelve-Factor App is a set of 12 practices for building software-as-a-service applications that deploy cleanly across environments, scale predictably, and minimize the gap between development and production. It was drafted by engineers at Heroku around 2011 from patterns they saw across many apps running on their platform, and it's still the implicit design philosophy behind most containerized, cloud-native applications today.
Why the Methodology Exists
The Twelve-Factor App methodology was written up by engineers at Heroku, a platform-as-a-service company, and first presented publicly by Adam Wiggins around 2011. It wasn't derived from theory. Heroku's whole business was running thousands of other companies' applications on shared infrastructure, and patterns kept showing up across which apps deployed cleanly and scaled predictably versus which ones constantly broke in ways specific to Heroku's environment. The twelve factors are essentially a writeup of what the well-behaved apps had in common.
The stated goals, in the methodology's own framing, are apps that use declarative formats for setup so a new developer can get running with minimal manual steps; have a clean, explicit contract with the underlying operating system so they're portable across execution environments; are suited to deployment on modern cloud platforms without needing servers or system administration to be managed by hand; minimize the gap between development and production so continuous deployment is actually feasible; and can scale up without major changes to tooling, architecture, or how the team develops. Twelve concrete factors fall out of that goal.
Factors I-IV: Codebase, Dependencies, Config, Backing Services
I. Codebase — one codebase tracked in version control, many deploys. There's exactly one Git repository per application, and every environment, dev, staging, production, is a different deploy of that same codebase, not a fork or a separately-maintained copy. If two environments have diverged into genuinely different code, they're not deploys of one app anymore.
II. Dependencies — explicitly declare and isolate dependencies. Every library the app needs is listed in a manifest, such as requirements.txt, package.json, or pom.xml, with pinned versions, and the app never relies on a system-wide package being present. This is why committing a lockfile matters: "works on my machine" is usually an undeclared dependency the developer's machine happened to already have installed.
III. Config — store config in the environment. Anything that varies between deploys, database credentials, API keys, hostnames, belongs in environment variables, never hardcoded or checked into a config file in the repository. The test the methodology proposes: could you open-source this codebase right now without exposing any credential? If config is in the code, no.
IV. Backing services — treat backing services as attached resources. A database, a message queue, an SMTP server, a cache: the app should be able to swap one instance for another, say a local Postgres for a managed cloud Postgres, by changing a connection string in config, with zero code changes. If swapping your Redis instance for a different one requires touching application code, that service isn't properly "attached."
Factors V-VIII: Build/Release/Run, Processes, Port Binding, Concurrency
V. Build, release, run — strictly separate these three stages. Build takes code and produces an executable bundle by compiling and fetching dependencies. Release combines that build with the current config for a specific environment, producing an immutable, uniquely identified release. Run executes that release in the target environment. Crucially, releases are immutable: if something's wrong, you don't patch a running release, you create a new one and deploy it, keeping the previous release available for instant rollback.
VI. Processes — execute the app as one or more stateless processes. Nothing your app needs to remember between requests should live in that process's memory or local disk; session state, uploaded files, cached data all belong in an external backing service like Redis, a database, or object storage, because the process itself is expected to be killed and restarted at any time, and a new process instance shouldn't need whatever the old one remembered.
VII. Port binding — export services via port binding. A twelve-factor web app doesn't rely on an external web server like Apache being injected into it at runtime; it's completely self-contained and binds to a port itself, a Node app calling app.listen(3000), for instance, becoming a backing service for some other app if needed, rather than depending on one.
VIII. Concurrency — scale out via the process model. Rather than trying to make a single process handle more load by adding threads or more RAM, scaling up, a twelve-factor app scales by running more copies of its process types, scaling out: more web processes, more background worker processes, each one simple and disposable, which is only possible because factor VI already made them stateless.
Factors IX-XII: Disposability, Dev/Prod Parity, Logs, Admin Processes
IX. Disposability — maximize robustness with fast startup and graceful shutdown. Processes should start in seconds, not minutes, so autoscaling and crash recovery are actually fast, and they should handle a SIGTERM by finishing in-flight requests and exiting cleanly rather than dying mid-request or ignoring the signal entirely. A process that can be killed and restarted at any moment without anyone noticing is the whole point of everything factors VI through VIII set up.
X. Dev/prod parity — keep development, staging, and production as similar as possible. Historically, developers used a lightweight local substitute, SQLite in dev, Postgres in prod, for speed; this factor argues against that specifically, because subtle behavioral differences between backing services cause bugs that only show up in production. The methodology also pushes for small time and personnel gaps between writing code and deploying it, not just environment similarity.
XI. Logs — treat logs as event streams. An app shouldn't manage its own log files or decide where they're routed; it just writes its event stream, unbuffered, to stdout, and lets the execution environment, a container runtime or a log aggregator, capture, route, and store that stream. This is why "just write to a file and rotate it" is explicitly the wrong pattern here: file management is an infrastructure concern, not an application concern.
XII. Admin processes — run admin and management tasks as one-off processes. Database migrations, a one-time data-fix script, a console or REPL session: these should run in the exact same environment, using the exact same codebase and config as the regular long-running processes, rather than a special script maintained separately that can silently drift out of sync with the app it's supposedly operating on.
How This Maps Onto Docker and Kubernetes
The methodology predates Docker (2013) and Kubernetes (2014) by a couple of years, but reads today almost like a specification for what those tools ended up enforcing structurally. A Docker image is close to a factor V "build" artifact: an immutable, versioned bundle. A container's expected lifecycle, start fast, respond to SIGTERM, be killed and rescheduled without ceremony, is factor IX, disposability, made mandatory rather than optional. Kubernetes ConfigMaps and Secrets injected as environment variables are a direct, literal implementation of factor III. Kubernetes scaling a Deployment's replica count is factor VIII's "scale out via the process model," executed by the orchestrator instead of manually.
This isn't a coincidence. The same operational pressures that produced the twelve factors at Heroku, many different apps run by many different teams on shared infrastructure that needs to treat them uniformly, are exactly the pressures container orchestration was built to solve at a larger scale. Understanding the twelve factors is a genuinely useful shortcut to understanding why Kubernetes and Docker are shaped the way they are, instead of just memorizing their APIs.
Where the Methodology Shows Its Age
It's fair to be honest that a methodology drafted around 2011 doesn't cover everything a 2026 cloud-native team deals with. It says very little about observability beyond logs, with no explicit factor for structured metrics or distributed tracing, both of which are now considered essential in any non-trivial production system. It predates the mainstream adoption of service meshes, and has nothing to say about service-to-service authentication, circuit breaking, or the specific challenges of coordinating dozens of independently-deployed services talking to each other, which is the normal reality of microservices today rather than the single-app-per-repo framing the original methodology assumes throughout.
Various people have proposed extensions over the years. A commonly cited unofficial "fifteen-factor app" adds API-first design, telemetry, and authentication and authorization as explicit additional factors, reflecting exactly the gaps above. None of these extensions contradict the original twelve; they're additions for problems that either didn't exist yet or weren't yet common when the original methodology was written. The original twelve factors themselves have aged remarkably well specifically because they're about a single application's internal discipline, statelessness, explicit config, clean environment contracts, which remains just as true for one microservice in a mesh of fifty as it was for a single monolith on Heroku in 2011.
