Episode ini mengajarkan manajemen konfigurasi yang benar: variabel lingkungan dengan os.Getenv, godotenv untuk development, Viper untuk kebutuhan kompleks, struct config terpusat, server HTTP custom dengan ReadTimeout dan WriteTimeout, serta graceful shutdown.

Kredensial database, secret JWT, dan port server tidak boleh di-hardcode. Konfigurasi yang benar membuat aplikasi kalian bisa berpindah dari development ke production tanpa mengubah satu baris kode pun. Episode ini mengajarkan fondasi 12-factor app pada level praktis.
Episode 10 ini membahas variabel lingkungan dengan os.Getenv, godotenv untuk development, Viper untuk konfigurasi kompleks, struct config terpusat, server HTTP custom dengan ReadTimeout dan WriteTimeout, serta graceful shutdown.
os.Getenv adalah cara paling dasar dan sebenarnya cukup untuk sebagian besar aplikasi:
port := os.Getenv("PORT")
if port == "" {
port = "8080"
}Selalu berikan default. os.Getenv mengembalikan string kosong saat variabel tidak ada, jadi pola if port == "" menjaga aplikasi tetap berjalan meski konfigurasi belum lengkap.
Jangan sebarkan pemanggilan os.Getenv ke seluruh kode. Kumpulkan semua konfigurasi dalam satu struct yang diisi sekali saat startup:
type Config struct {
Port string
DatabaseURL string
JWTSecret string
Environment string
}
func Load() Config {
return Config{
Port: getEnv("PORT", "8080"),
DatabaseURL: getEnv("DATABASE_URL", ""),
JWTSecret: getEnv("JWT_SECRET", ""),
Environment: getEnv("ENVIRONMENT", "development"),
}
}Struct Config ini kemudian dioper ke seluruh komponen lewat dependency injection — pola yang sudah kalian bangun di episode 8.
Di development, menulis variabel di terminal tiap kali sangat tidak nyaman. godotenv memuat file .env ke dalam environment saat proses dimulai:
go get github.com/joho/godotenvif err := godotenv.Load(); err != nil {
slog.Warn("file .env tidak ditemukan, pakai environment")
}
cfg := Load()Jangan pernah commit file .env ke repository — pastikan masuk .gitignore. Untuk secret, selalu gunakan environment dari sistem atau secret manager.
Saat konfigurasi bertambah — file YAML, key dari banyak sumber, dan override otomatis — Viper menjadi pilihan:
go get github.com/spf13/viperviper.SetConfigName("config")
viper.SetConfigType("yaml")
viper.AddConfigPath(".")
viper.AutomaticEnv()
if err := viper.ReadInConfig(); err != nil {
slog.Warn("konfigurasi file tidak dibaca", "err", err)
}
port := viper.GetString("server.port")Dengan viper.AutomaticEnv(), variabel lingkungan secara otomatis menjadi override terhadap nilai file. Ini kombinasi yang umum dipakai di production.
e.Start memakai pengaturan default yang kurang aman untuk production. Bangun http.Server sendiri agar waktu tunggu terkontrol:
s := &http.Server{
Addr: ":" + cfg.Port,
ReadTimeout: 30 * time.Second,
WriteTimeout: 30 * time.Second,
IdleTimeout: 120 * time.Second,
}
e.HideBanner = true
e.HidePort = true
if err := e.StartServer(s); err != nil {
e.Logger.Fatal(err)
}ReadTimeout melindungi dari koneksi lambat, WriteTimeout mencegah handler yang menggantung, dan IdleTimeout menutup koneksi yang tidak aktif. e.StartServer(s) menjalankan server custom ini bersama router Echo.
Server production harus berhenti dengan rapi: menyelesaikan request yang sedang berjalan sebelum mati, bukan langsung memutus koneksi. Gunakan signal.NotifyContext dan e.Shutdown:
ctx, stop := signal.NotifyContext(context.Background(),
os.Interrupt, syscall.SIGTERM)
defer stop()
go func() {
if err := e.StartServer(s); err != nil &&
err != http.ErrServerClosed {
e.Logger.Fatal(err)
}
}()
<-ctx.Done()
shutdownCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
if err := e.Shutdown(shutdownCtx); err != nil {
e.Logger.Fatal(err)
}
slog.Info("server berhenti dengan rapi")Saat Ctrl+C atau SIGTERM diterima, e.Shutdown memberi waktu 10 detik bagi request yang sedang berjalan untuk selesai sebelum proses keluar. Ini adalah pola yang wajib di semua aplikasi production.
Episode 10 membuat konfigurasi aplikasi kalian dewasa: variabel lingkungan dibaca sekali lewat struct Config, godotenv memudahkan development, Viper menangani kebutuhan kompleks, server HTTP custom dengan timeout melindungi resource, dan graceful shutdown menghentikan server tanpa memutus request aktif.
Inti yang harus dibawa pulang:
Config.os.Getenv butuh default; jangan pernah hardcode secret..env untuk development; jangan commit filenya.http.Server custom menetapkan ReadTimeout dan WriteTimeout.signal.NotifyContext dan e.Shutdown menghasilkan graceful shutdown.Di episode 11 selanjutnya kita akan membahas error handling & logging — HTTPError dan custom error handler, format RFC 9457 Problem Details untuk respons API yang konsisten, logging terstruktur dengan slog via RequestLogger, dan integrasi dengan log aggregator.