The 12 Factor App

First time I heard about 12 Factor App around 6 years ago. I even didn’t know that some of the rules that I use in software development have its own naming. When I went through all aspects, I thought that this topic is really worth to know. To be able to build scalable, maintainable and resilient applications, every developer should be aware of this methodology introduced by Heroku engineers.

What Is the 12-Factor App?

The 12-Factor App methodology is a set of guidelines providing a set of best practices for designing cloud-native software-as-a-service (SaaS) applications. These principles address challenges related to code management, configuration, deployment, scalability, and operational efficiency.

1. Codebase

“One codebase tracked in version control, many deploys”

A 12-factor app has a single codebase per application, stored in version control (e.g., Git). Deploying different versions (e.g., production, staging, development) doesn’t mean duplicating repositories but rather using the same codebase with different configurations.

Example: A Git repository for an e-commerce application has different deployment branches for staging and production, but the source code remains the same.

2. Dependencies

“Explicitly declare and isolate dependencies”

Applications should never rely on system-wide dependencies. Instead, they should use dependency managers such as npm (Node.js), pip (Python), or Maven (Java) to declare dependencies explicitly.

Example: A Node.js application defines its dependencies in package.json, ensuring every developer and deployment environment installs the correct versions.

3. Config

“Store config in the environment”

Configuration settings (e.g., database credentials, API keys) should not be hardcoded in the source code but stored in environment variables.

Example: Instead of storing AWS credentials in config.js, they are accessed via process.env.AWS_ACCESS_KEY_ID in a Node.js application.

4. Backing Services

“Treat backing services as attached resources”

External services such as databases, message queues, and caching systems should be treated as interchangeable components that can be swapped without changing the core application code.

Example: A PostgreSQL database can be replaced with MySQL by updating the database URL in environment variables, without modifying the application’s code.

5. Build, Release, Run

“Strictly separate build and run stages”

The build process compiles code, the release stage combines the build with configuration, and the run stage executes the application. This separation ensures reliability and repeatability.

Example: A CI/CD pipeline in GitHub Actions builds a Docker image, pushes it to a container registry, and deploys it to Kubernetes with the appropriate environment-specific configuration.

6. Processes

“Execute the app as one or more stateless processes”

A 12-factor app should avoid storing session state locally. Instead, it should externalize state management to databases, caches, or other services.

Example: A RESTful API for user authentication stores session data in Redis instead of keeping it in memory.

7. Port Binding

“Export services via port binding”

Applications should not assume a specific execution environment. Instead, they should expose HTTP or other services via port binding.

Example: A Python Flask application listens on PORT from environment variables, allowing it to run in different environments without modifications.

8. Concurrency

“Scale out via the process model”

Instead of relying on a single monolithic process, an application should run multiple lightweight processes that can scale horizontally.

Example: A microservices-based e-commerce system runs separate services for payments, orders, and inventory, each scaling independently based on demand.

9. Disposability

“Maximize robustness with fast startup and graceful shutdown”

Applications should start quickly and shut down gracefully to handle sudden crashes or redeployments efficiently.

Example: A Kubernetes-deployed service ensures safe shutdown by handling SIGTERM signals and closing database connections properly.

10. Dev/Prod Parity

“Keep development, staging, and production as similar as possible”

Differences between environments can lead to unexpected failures. Keeping them consistent prevents surprises in production.

Example: A developer working on a Django app uses Docker to ensure the local development environment mirrors the production PostgreSQL setup.

11. Logs

“Treat logs as event streams”

Logs should be handled as event streams, not stored within the application itself, making debugging and monitoring easier.

Example: A Node.js application writes logs to stdout, and a centralized logging service like Elasticsearch, Loki, or Datadog collects and analyzes them.

12. Admin Processes

“Run admin/management tasks as one-off processes”

Tasks like database migrations, cron jobs, or data cleanup should be run as separate one-off processes, not embedded in the main application.

Example: Running python manage.py migrate in a Django app as a Kubernetes job before starting the main web service.

Why the 12-Factor App Matters

Following these principles helps in achieving:

  • Scalability – Applications can be easily scaled horizontally.
  • Portability – Easier deployment across different environments.
  • Maintainability – Clean separation of concerns leads to better code organization.
  • Operational efficiency – Faster development, debugging, and deployment.

Conclusion

Adopting the 12-Factor App methodology doesn’t mean completely rebuilding your existing applications. Instead, it serves as a guide to incrementally improving architecture, ensuring your applications remain flexible, resilient, and cloud-ready.