Twelve-Factor App #
Twelve-Factor App adalah metodologi yang mendefinisikan dua belas prinsip untuk membangun aplikasi yang siap berjalan di cloud. Ditulis oleh tim Heroku berdasarkan pengalaman deploy jutaan aplikasi, metodologi ini menjadi standar de facto untuk cloud-native application development. Setiap faktor menjawab masalah spesifik yang muncul ketika aplikasi perlu di-scale, di-deploy di berbagai environment, atau dikelola oleh tim yang berbeda. Memahami keduabelas faktor ini bukan sekadar mengikuti aturan — tapi memahami mengapa setiap prinsip ada.
I. Codebase — Satu Repo, Banyak Deploy #
Prinsip:
Satu aplikasi = satu codebase di version control
Satu codebase bisa di-deploy ke banyak environment (dev, staging, prod)
// ANTI-PATTERN: Codebase berbeda per environment
/repo-development ← kode untuk dev
/repo-production ← kode untuk prod (dengan "perbaikan" yang
tidak di-merge ke dev)
// BENAR: Satu codebase, konfigurasi berbeda per environment
/repo ← satu codebase untuk semua environment
Deploy dev: git checkout main → deploy dengan config dev
Deploy prod: git checkout main → deploy dengan config prod
Mengapa penting:
Codebase yang berbeda per environment adalah sumber divergensi
"Works on dev but not on prod" sering berakar dari sini
II. Dependencies — Deklarasikan dan Isolasi Semua Dependensi #
Prinsip:
Semua dependensi dideklarasikan secara eksplisit
Tidak ada asumsi tentang software yang sudah terinstall di sistem
// ANTI-PATTERN: Dependensi implisit
# Script deployment yang berasumsi curl sudah ada di server
curl https://api.example.com/data
// BENAR: Deklarasi eksplisit
# requirements.txt (Python)
requests==2.31.0
flask==3.0.0
# package.json (Node.js)
{
"dependencies": {
"express": "4.18.0",
"axios": "1.6.0"
}
}
# Dockerfile yang isolasi environment sepenuhnya
FROM python:3.11-slim
COPY requirements.txt .
RUN pip install -r requirements.txt
Mengapa penting:
Deployment yang reproducible — environment yang sama persis
di dev, staging, dan prod
III. Config — Simpan Konfigurasi di Environment #
Prinsip:
Konfigurasi yang berbeda antar environment (URL database, API key,
port) disimpan di environment variables, bukan di kode
// ANTI-PATTERN: Konfigurasi hardcoded atau di file yang di-commit
DATABASE_URL = "postgresql://prod-db.company.com/appdb"
API_KEY = "sk_live_abcdef123456" # JANGAN commit ini ke repo!
# Atau: config file dengan nilai berbeda per environment
# yang di-commit ke repo (config-dev.json, config-prod.json)
// BENAR: Environment variables
import os
DATABASE_URL = os.environ["DATABASE_URL"]
API_KEY = os.environ["API_KEY"]
# Di environment dev: set via .env file (TIDAK di-commit)
# Di environment prod: set via secrets manager atau platform config
Mengapa penting:
Credentials tidak pernah masuk ke repository
Sama persis kode, berbeda konfigurasi = deployment yang predictable
IV. Backing Services — Perlakukan sebagai Resource yang Terpasang #
Prinsip:
Database, cache, message queue, email service diperlakukan
sebagai resource eksternal yang bisa diganti tanpa ubah kode
// ANTI-PATTERN: Hardcode lokasi backing service
db = connect("postgresql://192.168.1.10:5432/app")
# Jika database pindah host, harus ubah kode
// BENAR: Akses via URL dari environment variable
db = connect(os.environ["DATABASE_URL"])
# Ganti dari local DB ke cloud DB = cukup ubah DATABASE_URL
# Ganti provider database = cukup ubah DATABASE_URL
# Tidak ada perubahan kode
# Berlaku untuk semua backing service:
REDIS_URL = os.environ["REDIS_URL"]
SMTP_URL = os.environ["SMTP_URL"]
QUEUE_URL = os.environ["QUEUE_URL"]
Mengapa penting:
Portabilitas — bisa ganti backing service tanpa redeploy kode
Testing — mudah ganti database prod dengan database test
V. Build, Release, Run — Pisahkan Tiga Tahap #
Prinsip:
Tiga tahap yang harus terpisah dan tidak bisa berjalan mundur:
Build stage:
→ Ambil kode dari repo
→ Compile, install dependencies
→ Hasilkan build artifact (executable, container image)
→ Tidak ada konfigurasi environment-specific di sini
Release stage:
→ Gabungkan build artifact + konfigurasi environment
→ Hasilkan release yang siap dijalankan
→ Setiap release punya ID unik dan bisa di-rollback
Run stage:
→ Jalankan release di environment target
→ Tidak ada modifikasi kode atau konfigurasi di sini
Alur yang benar:
git push → CI build image → staging release → prod release
↑
tidak bisa "patch langsung di prod"
Mengapa penting:
Rollback yang reliable — kembali ke release sebelumnya
Audit trail — setiap deployment terdokumentasi
VI. Processes — Jalankan sebagai Proses Stateless #
Prinsip:
Aplikasi berjalan sebagai satu atau lebih proses yang stateless
Tidak ada state yang tersimpan di memori antara request
(Ini adalah faktor yang sudah dibahas mendalam di artikel
Stateless vs Stateful — lihat artikel tersebut untuk detail)
Ringkasan:
✗ Jangan simpan session di memori proses
✗ Jangan simpan file di filesystem lokal
✓ Simpan state di backing service (Redis, database)
✓ Simpan file di object storage
VII. Port Binding — Export Layanan via Port #
Prinsip:
Aplikasi mandiri (self-contained) dan expose layanannya
melalui port yang di-bind — tidak bergantung pada
web server eksternal yang di-inject oleh environment
// ANTI-PATTERN: Bergantung pada web server yang di-install di server
# Aplikasi PHP yang bergantung pada Apache yang sudah ada di server
# Aplikasi Java yang butuh Tomcat yang di-setup oleh ops
// BENAR: Aplikasi menjalankan HTTP server sendiri
# Python Flask
from flask import Flask
app = Flask(__name__)
if __name__ == "__main__":
port = int(os.environ.get("PORT", 5000))
app.run(host="0.0.0.0", port=port)
# Node.js Express
const port = process.env.PORT || 3000;
app.listen(port);
Mengapa penting:
Portabilitas — bisa berjalan di mana saja yang punya runtime
Container-friendly — satu container = satu aplikasi + HTTP server
Composability — satu layanan bisa jadi backing service bagi layanan lain
VIII–XII. Lima Faktor Terakhir #
Lima faktor berikutnya membangun di atas fondasi yang sudah ada.
VIII. Concurrency — Scale via Process Model #
Prinsip:
Scale dengan menambah lebih banyak proses (horizontal),
bukan dengan membuat satu proses lebih besar (vertical thread)
Web process: menangani HTTP request
Worker process: memproses background jobs
Clock process: menjalankan scheduled tasks
Scale web: tambah web process instances
Scale worker: tambah worker process instances
Keduanya bisa scale independen sesuai kebutuhan
IX. Disposability — Maximize Robustness dengan Fast Startup dan Graceful Shutdown #
Prinsip:
Proses bisa distart dan distop kapan saja tanpa efek buruk
Fast startup:
✓ Proses siap menerima request dalam hitungan detik
✓ Penting untuk auto-scaling yang responsif
Graceful shutdown:
✓ Saat menerima sinyal SIGTERM, selesaikan request yang sedang berjalan
✓ Kembalikan unfinished jobs ke queue
✓ Jangan drop request secara tiba-tiba
X. Dev/Prod Parity — Minimalkan Perbedaan Dev dan Prod #
Prinsip:
Jauhkan gap antara development dan production sekecil mungkin
Gap yang harus diminimalkan:
Time gap: deploy sesering mungkin, bukan sekali sebulan
People gap: developer yang membuat fitur juga yang deploy
Tools gap: dev dan prod gunakan backing service yang sama
(Docker Compose di dev = setup yang sama dengan prod)
// ANTI-PATTERN: "Di dev pakai SQLite, di prod pakai PostgreSQL"
Behavior berbeda bisa menyebabkan bug yang hanya muncul di prod
// BENAR: Gunakan database yang sama di dev dan prod
Docker Compose untuk dev: PostgreSQL container
Production: managed PostgreSQL
Same engine, same version
XI. Logs — Perlakukan sebagai Event Stream #
Prinsip:
Aplikasi tidak mengelola log file — ia hanya menulis ke stdout/stderr
Environment yang menangani routing log ke mana pun diperlukan
// ANTI-PATTERN: Aplikasi menulis ke file log sendiri
logging.basicConfig(filename="/var/log/app.log")
// BENAR: Tulis ke stdout, environment yang routing
logging.basicConfig(stream=sys.stdout, level=logging.INFO)
Platform (Kubernetes, cloud run, dll) akan capture stdout dan
kirim ke log aggregation service (CloudWatch, Stackdriver, dll)
XII. Admin Processes — Jalankan Admin Task sebagai One-Off Process #
Prinsip:
Database migration, script maintenance, console REPL —
dijalankan sebagai proses sementara di environment yang sama,
bukan sebagai fitur yang di-bake ke dalam aplikasi utama
✓ Database migration: jalankan sebagai job terpisah saat deploy
✓ Data cleanup: jalankan sebagai one-off job dengan kode yang
sama dengan aplikasi, di environment yang sama
✓ Debug console: jalankan container sementara yang connect ke
database prod (dengan akses yang terbatas)
Ringkasan #
- Twelve-Factor App adalah blueprint cloud-native application — dua belas prinsip yang menjawab masalah nyata deployment, scaling, dan maintenance di cloud.
- Config di environment variables, bukan di kode — credentials tidak pernah masuk ke repository, konfigurasi per environment dikelola di luar kode.
- Backing services sebagai attached resources — database, cache, queue bisa diganti tanpa ubah kode asalkan URL-nya dari environment variable.
- Pisahkan build, release, dan run — tidak ada “patch langsung di prod”; setiap perubahan harus melalui proses yang terdokumentasi dan bisa di-rollback.
- Proses stateless + disposability = cloud-native elasticity — startup cepat, shutdown graceful, tidak ada state di memori proses.
- Dev/prod parity mencegah “works on my machine” — gunakan backing service yang sama di semua environment untuk menghilangkan perbedaan yang menyembunyikan bug.
← Sebelumnya: Event-Driven Architecture Berikutnya: Virtual Network (VPC/VNet) →