Reproducible Development Environments: Hentikan Era 'Works on My Machine'
Seminggu lalu seorang teman mengirim pesan panik: "Kok di laptop gue error, padahal di server jalan?" Klasik. Dia habiskan tiga jam debug, padahal akar masalahnya cuma beda versi Node.js—16.x di laptop, 18.x di server. Tiga jam. Untuk sesuatu yang harusnya tidak pernah terjadi.
Reproducible development environments bukan soal disiplin tim atau dokumentasi yang bagus. Ini soal infrastruktur. Kalau environment-mu tidak bisa direplikasi secara deterministik—sama persis, di mesin mana pun, kapan pun—maka setiap onboarding developer baru, setiap ganti laptop, setiap CI pipeline adalah roulette.
Artikel ini bukan teori. Ini setup yang saya pakai sendiri, dengan tool spesifik, konfigurasi nyata, dan trade-off yang jujur.
Kenapa Environment Tidak Reproducible Itu Mahal
Biaya "works on my machine" sering tidak terlihat sampai menumpuk. Beberapa skenario yang saya alami sendiri:
- Onboarding baru: Developer junior butuh 2 hari setup environment. Separuhnya dihabiskan tanya-tanya di Slack.
- Ganti OS: Migrasi dari macOS Intel ke Apple Silicon—tiba-tiba ada dependency yang compiled untuk x86.
- CI/CD drift: Pipeline hijau, tapi production error karena library system di runner-nya beda.
- "Upgrade" yang tidak sengaja: Seseorang jalankan
brew upgradedan tiba-tiba Python 3.10 jadi 3.12.
Semua ini punya solusi yang sama: environment harus didefinisikan sebagai kode, bukan sebagai ritual instalasi manual.
Tiga Layer yang Perlu Dikontrol
Sebelum pilih tool, pahami dulu apa yang perlu dikontrol. Ada tiga layer:
1. Runtime & Language Version
Ini yang paling sering jadi sumber masalah. Node 16 vs 18, Python 3.10 vs 3.12, Go 1.21 vs 1.22—semua bisa breaking.
Tool pilihan saya: mise (versi 2024.x)
mise adalah pengganti asdf yang lebih cepat dan lebih waras. Konfigurasinya lewat file .mise.toml di root project:
[tools]
node = "20.11.0"
python = "3.11.8"
go = "1.22.1"
[env]
NODE_ENV = "development"
DATABASE_URL = "postgresql://localhost:5432/myapp_dev"
File ini masuk ke git. Siapa pun yang clone repo dan punya mise installed, jalankan mise install—selesai. Tidak ada ambiguitas.
Kenapa bukan asdf? mise ditulis ulang di Rust, jauh lebih cepat, dan syntax konfigurasinya lebih bersih. Saya migrasi dari asdf di awal 2024 dan tidak menyesal.
2. System Dependencies & Services
Runtime sudah aman, tapi bagaimana dengan PostgreSQL, Redis, atau library sistem seperti libpq? Di sinilah Docker masuk—bukan untuk seluruh development environment, tapi khusus untuk services.
Pendekatan yang saya rekomendasikan: Docker Compose untuk services, bukan untuk app
# docker-compose.yml
services:
postgres:
image: postgres:16.2-alpine
environment:
POSTGRES_DB: myapp_dev
POSTGRES_USER: dev
POSTGRES_PASSWORD: devpassword
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
redis:
image: redis:7.2-alpine
ports:
- "6379:6379"
volumes:
postgres_data:
App-nya tetap jalan di host, bukan di container. Kenapa? Karena hot reload lebih cepat, debugging lebih mudah, dan file watcher tidak butuh konfigurasi tambahan. Container untuk services, host untuk development—ini trade-off yang masuk akal untuk solo engineer.
3. Project Dependencies & Lockfiles
Ini yang paling sering diabaikan: commit lockfile-mu.
package-lock.jsonatauyarn.lockuntuk Nodepoetry.lockuntuk Pythongo.sumuntuk GoCargo.lockuntuk Rust
Jangan pernah .gitignore lockfile. Itu dokumen kontrak versi dependency-mu. Tanpa itu, npm install hari ini bisa menghasilkan hasil berbeda dari npm install bulan lalu.
Devcontainer: Ketika Docker Memang Diperlukan
Ada skenario di mana kamu butuh isolasi penuh—misalnya kalau kamu kerja di beberapa project dengan konflik system library yang serius, atau kalau tim-mu campuran Windows/macOS/Linux.
Di sinilah Dev Containers (spec dari Microsoft, bukan hanya VS Code) berguna. File konfigurasinya ada di .devcontainer/devcontainer.json:
{
"name": "myapp-dev",
"image": "mcr.microsoft.com/devcontainers/base:ubuntu-22.04",
"features": {
"ghcr.io/devcontainers/features/node:1": {
"version": "20"
},
"ghcr.io/devcontainers/features/python:1": {
"version": "3.11"
}
},
"postCreateCommand": "npm install && pip install -r requirements.txt",
"forwardPorts": [3000, 8000],
"mounts": [
"source=${localWorkspaceFolder},target=/workspace,type=bind"
]
}
Spec ini bisa dipakai di VS Code, JetBrains (via plugin), atau bahkan CLI dengan devcontainer CLI tool. Bukan vendor lock-in ke satu editor.
Tapi jujur: kalau kamu solo engineer dengan setup macOS/Linux yang konsisten, mise + Docker Compose untuk services sudah cukup. Dev Containers menambah overhead yang tidak selalu worth it.
Nix: Solusi Paling Deterministik, Tapi Ada Harganya
Tidak bisa nulis artikel tentang reproducible development environments tanpa menyebut Nix. Ini tool yang paling serius soal reproducibility—secara literal, hash dari setiap dependency di-track.
Dengan nix develop dan file flake.nix, kamu bisa define environment yang benar-benar identik di macOS, Linux, bahkan di CI:
{
description = "myapp dev environment";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.11";
flake-utils.url = "github:numtide/flake-utils";
};
outputs = { self, nixpkgs, flake-utils }:
flake-utils.lib.eachDefaultSystem (system:
let
pkgs = nixpkgs.legacyPackages.${system};
in {
devShells.default = pkgs.mkShell {
buildInputs = with pkgs; [
nodejs_20
python311
postgresql_16
redis
];
};
}
);
}
Jalankan nix develop—dapat shell dengan semua tool di atas, versi exact, tanpa polusi global.
Trade-off yang harus kamu tahu:
- Learning curve Nix sangat curam. Bahasanya sendiri (Nix expression language) butuh waktu untuk terbiasa.
- Error message-nya sering tidak membantu.
- Waktu setup awal bisa lama karena download banyak.
Rekomendasiku: coba Nix kalau kamu sudah nyaman dengan mise + Docker Compose dan masih punya masalah reproducibility. Jangan mulai dari Nix kalau baru mau mulai.
Automasi Setup dengan Makefile atau Justfile
Tool sudah dipilih, konfigurasi sudah ada di git. Tapi masih ada satu masalah: urutan setup. Developer baru harus tahu harus jalankan apa dulu.
Solusi paling simpel: Makefile atau justfile sebagai entry point.
# Makefile
.PHONY: setup dev test clean
setup: ## Setup environment dari nol
mise install
docker compose up -d
npm install
cp .env.example .env
@echo "Setup selesai. Jalankan 'make dev' untuk mulai."
dev: ## Jalankan development server
docker compose up -d
npm run dev
test: ## Jalankan test suite
docker compose up -d
npm test
clean: ## Hentikan semua services
docker compose down
Dengan ini, onboarding jadi:
git clone <repo>make setupmake dev
Tiga langkah. Bukan dua halaman README.
Kalau lebih suka syntax yang lebih bersih, just adalah alternatif yang bagus—syntax-nya lebih ekspresif dan error handling-nya lebih baik dari Make.
CI/CD: Pastikan Pipeline Pakai Environment yang Sama
Semua setup di atas percuma kalau CI-mu pakai environment berbeda. Ini yang sering dilupakan.
Contoh GitHub Actions yang konsisten dengan setup lokal:
# .github/workflows/test.yml
name: Test
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- name: Install mise
uses: jdx/mise-action@v2
# Otomatis baca .mise.toml dan install tools
- name: Start services
run: docker compose up -d
- name: Install dependencies
run: npm ci # bukan npm install
- name: Run tests
run: npm test
Perhatikan npm ci bukan npm install. npm ci strict terhadap lockfile—kalau ada perbedaan antara package.json dan package-lock.json, dia error. Itulah yang kamu mau di CI.
Juga: pin versi ubuntu-22.04, bukan ubuntu-latest. ubuntu-latest bisa berubah kapan saja dan merusak reproducibility yang sudah kamu bangun.
Perbandingan Tool: Pilih yang Sesuai Kebutuhan
| Tool | Use Case | Learning Curve | Reproducibility |
|---|---|---|---|
| mise | Runtime version management | Rendah | Tinggi |
| Docker Compose | Service isolation | Rendah-Sedang | Tinggi |
| Dev Containers | Full env isolation | Sedang | Sangat Tinggi |
| Nix Flakes | Maximum reproducibility | Sangat Tinggi | Maksimal |
Untuk sebagian besar solo engineer: mise + Docker Compose + lockfiles sudah 90% dari jalan. Dev Containers kalau butuh portabilitas lintas OS. Nix kalau kamu mau all-in.
Kalau kamu tertarik dengan pendekatan self-hosted yang lebih jauh, this guide on self-hosted e-commerce platform comparison 2024 bisa memberikan perspektif tentang infrastruktur yang bisa jadi bagian dari setup development environment-mu.
Dan kalau CI/CD self-hosted juga menarik, ada juga artikel tentang Woodpecker CI untuk pipeline lokal yang pakai Docker Compose sebagai dasarnya.
Kesimpulan: Apa yang Harus Kamu Lakukan Besok
Reproducible development environments bukan proyek besar yang butuh waktu berbulan-bulan. Mulai kecil, tapi mulai sekarang.
Besok pagi, ambil satu project yang paling sering bikin masalah setup. Lakukan ini:
- Install mise, tambahkan
.mise.tomldengan versi exact dari semua runtime yang dipakai. - Buat
docker-compose.ymluntuk semua external services (database, cache, queue). - Commit lockfile—kalau belum ada, generate sekarang.
- Tulis
Makefiledengan targetsetup,dev, dantest.
Total waktu: 1-2 jam. Manfaatnya: tidak ada lagi debug "works on my machine" yang makan waktu berjam-jam.
Kalau sudah nyaman dengan empat langkah itu, baru pertimbangkan Dev Containers atau Nix. Tapi untuk sekarang—mulai dari yang simpel dan konsisten.