The 12-Factor App Methodology
Standardizing our approach ensures that our applications are portable, scalable, and easy to maintain. We adhere to the 12-Factor App methodology to keep our development and production cycles smooth.
-
Codebase
Section titled “Codebase”One app, one repo.
We keep things simple. A single repository tracks the codebase, ensuring that the exact same code deployed to Staging is the one that lands in Production.
-
Dependencies
Section titled “Dependencies”Explicitly declared and isolated.
We avoid “it works on my machine” surprises. All dependencies must be locked (e.g.,
package.json,pom.xml). -
Configs
Section titled “Configs”Store config in the environment.
Keep secrets secret and configuration flexible. Store config in environment variables, never in the code or hardcoded files. This allows us to switch settings between environments without rebuilding the image.
-
Backing Services
Section titled “Backing Services”Treat backing services as attached resources.
Databases, caches, and queues should be treated like pluggable resources. Your app should be loosely coupled enough that switching from PostgreSQL to MySQL is mostly a configuration change.
-
Build, Release, Run
Section titled “Build, Release, Run”Strictly separate build and run stages.
- Build: We use CloudBuild to create a lean Docker image (binary + essentials).
- Release: ArgoCD combines the image with the environment config.
- Run: Helm Charts execute the app in the cluster.
-
Processes
Section titled “Processes”Execute the app as one or more stateless processes.
Any data that needs to survive a restart (like user sessions) must live in a shared store like Redis, not in the pod’s memory. This allows Kubernetes to scale our replicas up and down freely without data loss.
-
Port Binding
Section titled “Port Binding”Export services via port binding.
The application must be self-contained and listen on a specific port (configurable via env vars). This ensures Kubernetes Services and Ingress controllers can reliably route traffic to your pods.
-
Concurrency
Section titled “Concurrency”Scale out via the process model.
Design your code assuming multiple copies will run simultaneously. Ensure your logic (especially Pub/Sub listeners) handles locking or idempotency so that multiple instances don’t process the same message at the same time.
-
Disposability
Section titled “Disposability”Maximize robustness with fast startup and graceful shutdown.
- Shutdown: Handle the
SIGTERMsignal to finish active requests before the pod closes. - Startup: Keep boot times short. Long startup routines hinder the autoscaler during traffic spikes.
- Shutdown: Handle the
-
Dev/Prod Parity
Section titled “Dev/Prod Parity”Keep development, staging, and production similar.
By using the same Docker images and just swapping environment variables, we ensure that if it passes in Staging, it will work in Production.
-
Treat logs as event streams.
- Format: Use JSON formatting so aggregation tools can parse them.
- Content: Log meaningful info. Avoid cluttering Production logs with high-frequency “I’m healthy” checks.
-
Admin Processes
Section titled “Admin Processes”Run admin/management tasks as one-off processes.
Need to run a database migration? Run it as a separate Kubernetes Job. It should use the same image and config as the app but execute independently.