The 12-Factor App Was Written in 2012
Heroku's original 12-factor methodology is still widely referenced. Some factors are timeless. Others need updating for container-first, serverless, and cloud-native development.
Here's an honest look at each factor in 2025.
The Timeless Factors
I. Codebase
One codebase tracked in version control, many deploys.
Still true. One repo, multiple environments (staging, production) via branches or tags.
git flow: main → production, develop → staging
# Or trunk-based: main → production directly
II. Dependencies
Explicitly declare and isolate dependencies.
// package.json: explicit versions
{
"dependencies": {
"express": "4.18.2",
"prisma": "5.7.0"
}
}
Docker extends this: FROM node:20-alpine pins the runtime too.
III. Config
Store config in the environment.
// Never:
const apiKey = 'sk-prod-abc123';
// Always:
const apiKey = process.env.STRIPE_SECRET_KEY;
Now validated with Zod at startup — smarter than raw process.env.
IV. Backing Services
Treat backing services as attached resources.
// Database, Redis, S3 — all via URL
const db = new PrismaClient({ datasources: { db: { url: process.env.DATABASE_URL } } });
const redis = createClient({ url: process.env.REDIS_URL });
const s3 = new S3Client({ region: process.env.AWS_REGION });
Swap prod PostgreSQL for local by changing one env var. Still essential.
X. Dev/Prod Parity
Keep development, staging, and production as similar as possible.
Docker Compose makes this achievable:
# docker-compose.yml
services:
app:
build: .
environment:
DATABASE_URL: postgresql://postgres:password@db:5432/myapp
db:
image: postgres:16 # same version as production
redis:
image: redis:7 # same version as production
Factors That Need Updating
V. Build, Release, Run
Strictly separate build and run stages.
Docker multi-stage builds automate this:
# Stage 1: BUILD
FROM node:20 AS builder
RUN npm run build
# Stage 2: RELEASE + RUN (immutable image)
FROM node:20-alpine
COPY --from=builder /app/dist ./dist
CMD ["node", "dist/server.js"]
The image tag IS the release. myapp:v1.2.3 is immutable.
VIII. Concurrency
Scale out via the process model.
Originally: run multiple Unix processes. Now: run multiple containers/replicas.
# Kubernetes
replicas: 5
# or auto-scale based on CPU/memory
Serverless takes this further—scale to zero, then to thousands.
IX. Disposability
Fast startup, graceful shutdown.
// Graceful shutdown is more important than ever
process.on('SIGTERM', async () => {
await server.close(); // stop accepting new connections
await prisma.$disconnect(); // close DB connections
await redis.quit(); // close Redis
process.exit(0);
});
Kubernetes sends SIGTERM before killing containers. Handle it.
Factors That Need New Thinking
XI. Logs
Treat logs as event streams.
Original: write to stdout, let the platform handle it.
Now: structured logs + distributed tracing:
import pino from 'pino';
const logger = pino({ level: 'info' });
// Instead of: console.log('Payment processed for user', userId)
logger.info({ userId, orderId, amount }, 'Payment processed');
// Logs are queryable JSON, correlatable with trace IDs
XII. Admin Processes
Run admin/management tasks as one-off processes.
Migrations as one-off jobs before deployment:
# Kubernetes Job for migrations
apiVersion: batch/v1
kind: Job
metadata:
name: migrate
spec:
template:
spec:
containers:
- command: ["npx", "prisma", "migrate", "deploy"]
What 12-Factor Misses in 2025
- Observability: Logs alone aren't enough—you need metrics, traces, and structured errors
- Security: Secrets management (Vault, AWS Secrets Manager) beyond env vars
- GitOps: Infrastructure as code, deployment via PR review
- Service mesh: mTLS, circuit breakers, traffic shaping between services
The 12-factor app is a foundation, not a ceiling. Use it as a checklist, then layer modern practices on top.
12-factor patterns baked into the architecture: Whoff Agents AI SaaS Starter Kit.
Top comments (0)