The Twelve-Factor App Methodology Explained
The twelve-factor app is a set of principles for building portable, scalable cloud software. Each factor explained, and why they still hold up today.
The twelve-factor app is a methodology for building software-as-a-service applications that are portable across environments, scale cleanly, and can be deployed with minimal operational ceremony. It was distilled from patterns observed running many applications on cloud platforms, and despite being older than most of the tools we now use to enforce it, it still describes the baseline assumptions that containers, Kubernetes, and CI/CD pipelines are built around.
The twelve factors
1. Codebase. One codebase tracked in version control, many deploys. If you have genuinely separate codebases for the same app, you have a distributed system, not one app with multiple deploys — treat each as its own twelve-factor app instead.
2. Dependencies. Explicitly declare and isolate dependencies — never rely on system-wide packages being present. A twelve-factor app never assumes anything exists in its environment that isn’t declared in a manifest (package.json, requirements.txt, and so on).
3. Config. Store configuration in environment variables, not in code. Config that varies between environments — database URLs, API keys, feature toggles — should never be checked into the codebase. This is the factor most directly responsible for making the same build artifact deployable to staging and production without modification.
4. Backing services. Treat backing services — databases, queues, caches — as attached resources, referenced by a URL or credentials in config. Swapping a local Postgres instance for a managed one should require a config change, not a code change.
5. Build, release, run. Strictly separate the build stage (turn code into an executable bundle), the release stage (combine that bundle with config), and the run stage (execute it). Releases should be immutable and identifiable — this is the philosophy behind immutable infrastructure and why rollbacks work by deploying a previous release rather than reverting code and rebuilding.
6. Processes. Execute the app as one or more stateless processes. Anything that needs to persist belongs in a backing service, not in local memory or on local disk — a request handled by one process instance should be handleable by any other. This is what makes horizontal scaling trivial: spin up more identical, stateless processes.
7. Port binding. The app is self-contained and exports services via port binding rather than relying on an external web server being injected at runtime. A twelve-factor web app includes its own HTTP server library rather than expecting to be deployed into Apache or a similar container.
8. Concurrency. Scale out via the process model — run more instances of a process type rather than making a single process handle more work through threads alone. This maps naturally onto how Kubernetes deployments scale: more replicas, not fatter processes.
9. Disposability. Processes should start fast and shut down gracefully on SIGTERM, finishing in-flight requests and releasing resources cleanly. Fast startup enables fast scaling and fast deploys; graceful shutdown is what makes rolling deploys and blue-green or canary releases safe rather than lossy.
10. Dev/prod parity. Keep development, staging, and production as similar as possible — same backing services, same versions, minimal time gap between writing code and deploying it. The classic anti-pattern this factor targets is developing against SQLite locally and running Postgres in production; subtle behavioral differences between the two turn into production bugs that never showed up locally.
11. Logs. Treat logs as event streams — write them unbuffered to stdout, and let the execution environment handle routing, storage, and rotation. The app itself should never manage log files. This is exactly what log aggregation tooling is built to consume.
12. Admin processes. Run one-off admin tasks — database migrations, a one-time data-repair script — as one-off processes in an environment identical to the app’s regular long-running processes, using the same codebase and config, rather than SSHing into a server and running something ad hoc.
Why this still matters
The twelve-factor app predates widespread container adoption, but reads today almost like a spec for what a container image and a Kubernetes deployment assume about your app: stateless processes, config via environment variables, logs to stdout, fast startup, graceful shutdown on SIGTERM. An app that violates several of these factors is usually the app that’s painful to containerize or run under an autoscaler — not because the tooling is deficient, but because the app was never built with the process model those tools assume.
It’s also a good reference point for platform engineering teams building an internal developer platform: a platform that enforces twelve-factor conventions by default (injected config, managed backing services, standardized log shipping) removes an entire category of decisions from individual teams.
Where the methodology shows its age
A few factors need light reinterpretation for how teams actually build software now. “One codebase” predates the monorepo-vs-polyrepo debate that’s since become mostly a matter of taste rather than correctness. And “backing services as attached resources” is now so default-assumed — thanks to managed databases and Infrastructure as Code — that it barely reads as a distinct principle anymore. The core discipline underneath all twelve — separate config from code, keep processes stateless and disposable, make releases immutable — hasn’t dated at all.
The takeaway
The twelve-factor app methodology is a checklist for building cloud software that’s portable, horizontally scalable, and safe to deploy repeatedly: externalize config, treat backing services as swappable attachments, keep processes stateless and fast to start and stop, and stream logs rather than managing files. It predates most of the infrastructure we now use to enforce these ideas automatically, which is exactly why it’s worth reading directly — it explains the why behind conventions that container platforms and CI/CD pipelines otherwise just hand you as defaults.
Tagged
Keep reading
The Lycoris Team · · 4 min read Distributed Tracing Explained: Following Requests Across Services
Distributed tracing follows a single request as it crosses service boundaries, using spans and trace IDs to reconstruct the full call path and find where time goes.
Chisato · · 5 min read Logs vs Metrics vs Traces: The Three Pillars
Logs, metrics, and traces each answer a different question about a running system — what each captures, and how they work together.
Chisato · · 4 min read Monorepo vs Polyrepo: Which Should You Choose
A monorepo holds all projects in one repository; a polyrepo splits them apart. Trade-offs in tooling, ownership, and CI/CD for each approach.