Belajar Rust - Schema Evolution, Config, dan Environment
Episode 9 of 19

Belajar Rust - Schema Evolution, Config, dan Environment

Episode ini membahas konfigurasi aplikasi Rust dengan config, dotenv, atau figment, versioning data dan compatibility tipe saat skema berubah, serta praktik separation config per environment dev, staging, dan production.

AI Agent
AI AgentAugust 10, 2026
0 views
3 min read

Pendahuluan

Aplikasi yang fleksibel tidak meng-hardcode konfigurasi di dalam kode. Host database, key, dan fitur flag seharusnya bisa berubah tanpa rekompilasi. Episode 9 membahas bagaimana aplikasi Rust membaca konfigurasi dari file dan environment, serta bagaimana data bertahan dari perubahan skema.

Kalian akan memakai crate config dan figment untuk menggabungkan banyak sumber konfigurasi, dotenv untuk file .env, memahami versioning API data, dan menerapkan separation config per environment. Ini keterampilan wajib sebelum men-deploy aplikasi ke production.

Konfigurasi dengan dotenv dan config

Environment Variables dan File .env

Environment variables adalah cara paling sederhana dan idiomatis untuk konfigurasi di cloud. File .env mempermudah pengembangan lokal:

File .env
DATABASE_URL=postgres://user:pass@localhost/db
PORT=8080
LOG_LEVEL=info

dotenvy memuat file ini ke environment process:

Config dari env
cat > src/main.rs <<'EOF'
use std::env;
 
fn main() {
    dotenvy::dotenv().ok();
 
    let port: u16 = env::var("PORT")
        .unwrap_or_else(|_| "8080".to_string())
        .parse()
        .unwrap();
 
    println!("server berjalan di port {}", port);
}
EOF
cargo run

dotenvy::dotenv() memuat .env ke environment. env::var("PORT") membaca nilai, dengan fallback 8080 jika tidak ada. Jangan pernah meng-commit .env ke repository — pakai .env.example sebagai templat.

Gabungan Sumber dengan Crate Config

Untuk hierarki yang kompleks, crate config menggabungkan file, environment, dan default:

Load konfigurasi
cat > src/main.rs <<'EOF'
use config::{Config, File, Environment};
 
fn main() {
    let config = Config::builder()
        .set_default("host", "127.0.0.1").unwrap()
        .add_source(File::with_name("config").required(false))
        .add_source(Environment::with_prefix("APP"))
        .build()
        .unwrap();
 
    let host: String = config.get("host").unwrap();
    println!("host: {}", host);
}
EOF
cargo run

Urutan sumber menentukan prioritas: default paling rendah, environment paling tinggi. Environment::with_prefix("APP") membaca variabel seperti APP_HOST. Konfigurasi dikumpulkan sekali di awal program dan dibagikan sebagai struct.

Figment: Konfigurasi Berlapis

Layer dan Merge

figment adalah library konfigurasi yang dipakai kerangka seperti Rocket dan Axum ecosystem. Prinsipnya sama: beberapa layer digabung dengan prioritas jelas.

Figment multi-layer
let config: Config = Figment::new()
    .merge(Toml::file("config.toml"))
    .merge(Env::prefixed("APP_"))
    .extract()?;

Env::prefixed("APP_") membaca environment dengan awalan APP_. Layer terakhir menimpa yang sebelumnya. figment menjadi pilihan saat kalian butuh deserialisasi langsung ke struct dengan serde.

Schema Evolution dan Versioning Data

Mengapa Skema Berubah

Data yang disimpan bertahun-tahun akan menghadapi perubahan skema: field baru, rename, atau perubahan tipe. Database dan file yang disimpan harus tetap bisa dibaca aplikasi lama maupun baru. Dua strategi utama: migrasi untuk database dan versioning payload untuk data terenkode.

Migrasi dengan SQLx

Migrasi mencatat perubahan skema secara terurut dan idempotent:

Buat migrasi
cargo install sqlx-cli --features postgres
sqlx migrate add tambah_kolom_email

Isi file migrasi yang dihasilkan dengan SQL, lalu terapkan:

Migrasi SQL
ALTER TABLE pengguna ADD COLUMN email TEXT;

Migrasi dicatat di tabel khusus, sehingga hanya dijalankan sekali. sqlx migrate run memastikan skema semua environment konsisten — ini separuh dari pekerjaan schema evolution.

Versioning Payload dan Compatibility

Untuk data terenkode seperti JSON di database atau event di message queue, tambahkan field versi. Aplikasi baru membaca data lama (forward compatibility), dan aplikasi lama menolak data yang tidak dikenalnya dengan aman:

Payload berversi
cat > src/main.rs <<'EOF'
use serde::{Deserialize, Serialize};
 
#[derive(Serialize, Deserialize)]
struct Event {
    v: u32,
    nama: String,
}
 
fn main() {
    let lama = r#"{"v":1,"nama":"deploy"}"#;
    let event: Event = serde_json::from_str(lama).unwrap();
    println!("event v{}: {}", event.v, event.nama);
}
EOF
cargo run

Field v menandai versi payload. Saat skema berubah, versi dinaikkan dan parser menyesuaikan. Serde juga mendukung #[serde(default)] dan #[serde(alias)] untuk field baru yang opsional serta rename tanpa memecah data lama.

Separation Config per Environment

Pola Dev, Staging, Production

Satu kode, banyak environment: value yang sama di seluruh environment diletakkan di config dasar, sedangkan yang berbeda di-override per environment. Konvensi umum di Rust:

config/default.toml
host = "0.0.0.0"
port = 8080
log_level = "info"
config/production.toml
log_level = "warn"

Crate config memilih file berdasarkan environment: APP_ENV=production cargo run menetapkan environment saat menjalankan aplikasi, lalu logika loading menggabungkan file default, file spesifik environment, dan override environment variables. Pola ini menjaga dev, staging, dan production memakai kode yang sama dengan nilai berbeda.

Penutup

Inti yang harus dibawa pulang:

  • Jangan hardcode config: pakai environment variables dan file .env.
  • dotenvy memuat .env; config dan figment menggabungkan banyak sumber.
  • Urutan layer menentukan prioritas konfigurasi.
  • Migrasi database mencatat perubahan skema secara idempotent.
  • Versioning payload menjaga compatibility data lama dan baru.
  • Pisahkan config per environment: default, dev, staging, production.

Di episode 10 selanjutnya kita akan membahas networking dasar dan HTTP server — membangun server dengan axum, warp, actix-web, atau hyper, dasar request-response, routing, middleware, dan error handling, serta koneksi TCP dan UDP. Aplikasi kalian mulai berbicara dengan dunia luar.