The 12 principles every cloud app must follow: portability, reproducibility, and clean separation of config, code, and state.
The 12 principles every cloud app must follow: portability, reproducibility, and clean separation of config, code, and state.
Lesson outline
It was 11:47 PM. Their biggest sale of the year had just started. Then the site went down.
The team scrambled to add servers, but they could not. Why? The app stored session data in memory on a single machine. Config was baked into the binary. Logs were written to local disk. There was no way to spin up a second instance — it would have different state, different config, and no logs anyone could read.
They had built a web app. They had not built a cloud-native app.
Adam Wiggins and the Heroku team saw this pattern across thousands of apps they hosted. In 2011, they wrote down twelve factors — twelve conditions an app must satisfy to be truly portable, scalable, and operable in the cloud.
What is the Twelve-Factor App?
A methodology for building software-as-a-service apps that are portable across environments, deployable to modern cloud platforms, scalable without architecture changes, and maintainable by a team of developers working in parallel.
Factor I–IV: Code, dependencies, config, and backing services
pip install not in requirements.txt. GOOD: requirements.txt, package.json, go.mod, Gemfile — versioned, checked in.const DB_URL = "postgres://prod-db.company.com" in source code. GOOD: process.env.DATABASE_URL. This is also why you never commit .env files.Factor V–VIII: Build, process, port, and concurrency
vim app.py). GOOD: CI/CD pipeline creates immutable Docker images tagged by commit SHA.app.listen(process.env.PORT).Factor IX–XII: Disposability, dev/prod parity, logs, and admin
logger.setFile("/var/log/app.log") — log rotation, disk management = your problem. GOOD: console.log(JSON.stringify({level:"info", msg:...})) → shipped to Datadog by the platform.db.migrate() every time it starts. GOOD: kubectl exec a migration job before rolling out the new version.Of all twelve factors, III (Config) is broken most often, with the most severe consequences.
BAD: Config in code
// app.js — this gets committed to GitHub const config = { database: "postgres://admin:password123@prod-db.company.com:5432/myapp", apiKey: "sk-live-abc123def456", stripeKey: "sk_live_xxxxxxxx" };
GOOD: Config in environment
// app.js — zero secrets in source const config = { database: process.env.DATABASE_URL, apiKey: process.env.API_KEY, stripeKey: process.env.STRIPE_SECRET_KEY }; // .env (never committed — in .gitignore) DATABASE_URL=postgres://admin:password@localhost:5432/myapp_dev
The test: could you open-source your codebase right now without exposing credentials? If no, you are violating Factor III. Fix it before a disgruntled employee or accidental public repo does it for you.
A developer stores the production database password as a constant in `config.js` and commits it to the repo. Which factor does this violate?
Imagine needing to handle a traffic spike. With stateful processes, you cannot just add servers — each server has different in-memory state, and you have no way to route users back to "their" server reliably.
| Scenario | Stateful app | Twelve-Factor app |
|---|---|---|
| Traffic spike — need 10 more servers | Cannot — user sessions are in memory on server 1 | Launch 10 new instances in 30 seconds, load balancer routes anywhere |
| Server crashes mid-request | User loses their cart / session | User retries, another process handles it — no lost state (it is in Redis) |
| Deploy new version | Must maintain "sticky sessions" routing nightmare | Drain old instances, spin up new ones — stateless means no migration needed |
| Disaster recovery | Complex — need to replicate in-memory state | Trivial — start fresh processes, state is in the DB/cache |
The mental model: treat each process like a lambda function. It starts, does work using data from a backing service, returns a result, and exits. No local state. No assumptions about what ran before.
Back to the startup that crashed. Here is what they violated and what they fixed:
Violated factors and their fixes
Result
Next Black Friday: site stayed up. They scaled from 2 to 40 instances in 4 minutes. Cost: $180 in extra compute for 6 hours. Revenue saved: six figures.
Cloud architecture and backend interviews: interviewers use 12-Factor to assess whether you understand cloud-native design. Expect to explain why stateless processes matter for scaling, or how you handle config across environments.
Common questions:
Key takeaways
What does "backing service" mean in 12-Factor terminology?
Any service the app consumes over the network as part of its normal operation — databases, caches, message queues, SMTP servers. They are attached resources swappable via config.
Why must processes be stateless (Factor VI)?
So any process can handle any request, enabling horizontal scaling by adding identical instances without coordination or state migration.
Where should the app write its logs according to Factor XI?
To stdout as an event stream. The execution environment (Kubernetes, Heroku, AWS) captures and routes them to log aggregation systems.
Ready to see how this works in the cloud?
Switch to Career Paths for structured paths (e.g. Developer, DevOps) and provider-specific lessons.
View role-based pathsSign in to track your progress and mark lessons complete.
Questions? Discuss in the community or start a thread below.
Join DiscordSign in to start or join a thread.