Enforces the twelve-factor methodology for building scalable, maintainable, and portable software-as-a-service applications deployable to any cloud platform.
## 12-Factor App Rules
### I. Codebase
- One codebase, tracked in version control — never multiple repos for one app
- Multiple deploys (staging, production) share the same codebase; environment-specific differences come from config
- If multiple apps share code, extract the shared code to a library (package/module)
### II. Dependencies
- Declare all dependencies explicitly in a manifest file (`package.json`, `pyproject.toml`, `go.mod`, `Cargo.toml`)
- Never rely on globally installed tools — isolate dependencies per project
- Use lock files (`package-lock.json`, `poetry.lock`, `go.sum`) to pin exact versions
- System tools (curl, imagemagick) must also be declared and vendored or containerized
### III. Config
- All configuration that varies between environments belongs in environment variables — not in code
- Config includes: database URLs, API keys, service credentials, feature flags, port numbers
- Never commit `.env` files with real secrets — commit `.env.example` with placeholder values
- Do not group config into named environments (`development`, `staging`) in code — use individual env vars
- Access config via `process.env`, `os.environ`, or a config service — never hardcode values
### IV. Backing Services
- Treat databases, caches, queues, mail servers as attached resources accessed via URL/credentials
- Local and remote backing services are interchangeable — swap a local PostgreSQL for an RDS instance without code changes
- Backing service URLs and credentials come from config (Factor III), never hardcoded
- Gracefully handle backing service unavailability — implement retries with exponential backoff
### V. Build, Release, Run
- Strictly separate the build stage (compile, bundle), release stage (combine with config), and run stage
- Releases are immutable — a deployed release cannot be modified; create a new release for changes
- Every release must have a unique ID (timestamp or incremented number)
- The run stage should be simple enough to execute from a shell script (no build tools at runtime)
### VI. Processes
- The app runs as one or more stateless processes
- Processes must not store sticky session data in memory — use a backing store (Redis, database)
- File system is ephemeral — do not persist state to the local file system between requests
- Asset compilation and packaging happen at build time, not at runtime
- Use a shared cache service (Redis) for sessions, not in-memory process state
### VII. Port Binding
- The app exports HTTP by binding to a port — it does not rely on an external web server injection
- The app is completely self-contained; the web server library is a declared dependency
- One process per port; a router/load balancer in front handles routing to the correct process
### VIII. Concurrency
- Scale out via the process model — add more worker processes, not threads within a process
- Different workloads run as different process types: `web`, `worker`, `clock`, `scheduler`
- Processes are stateless (Factor VI), making horizontal scaling trivial and safe
### IX. Disposability
- Processes start fast (under 5 seconds) and shut down gracefully on `SIGTERM`
- Web processes stop accepting new requests and finish in-flight requests before shutting down
- Worker processes return the current job to the queue on shutdown — they are idempotent
- Sudden process deaths must not corrupt data — use transactions and atomic operations
### X. Dev/Prod Parity
- Keep development, staging, and production as similar as possible
- Use the same backing services locally as in production — use Docker Compose for local dev
- Deploy frequently — small gaps between code and production reduce divergence
- Do not use SQLite locally with PostgreSQL in production — the differences cause bugs
### XI. Logs
- Treat logs as event streams — each process writes to stdout, unbuffered
- Do not manage log files in the app — let the execution environment capture and route stdout
- Use structured logging (JSON) for machine-parseable output
- Log levels: ERROR for actionable alerts, WARN for non-critical issues, INFO for lifecycle events, DEBUG for development
### XII. Admin Processes
- Run admin tasks (migrations, console, data fixes) as one-off processes against a release
- Admin code ships with app code so it runs against the same environment
- Use the same language and dependencies as the app — no separate admin scripts
- Migrations run automatically at deployment before the app starts (never manually on prod)
### Anti-patterns
- Sticky sessions in load balancers — violates stateless processes (Factor VI)
- Baking config values into Docker images — violates separate config (Factor III)
- Using the local file system as a message queue between processes — violates stateless processes
- Different dependency versions in dev and prod — violates dev/prod parity (Factor X)