Sakit yang Diam-diam Saat Deploy Aplikasi

Sakit yang Diam-diam Saat Deploy Aplikasi

M. Zakyuddin Munziri

M. Zakyuddin Munziri

@zakiego

Ditulis asli dalam English.

Saya terus melihat cerita deployment yang sama berulang di berbagai tim dan stack. Awalnya terlihat kecil. Tidak ada yang gagal saat deploy. Health check hijau. CI senang. Server live.

Kemudian, sesuatu rusak di production dan investigasi menjadi perburuan yang lambat dan buruk. Akar masalahnya sering kali sama. Environment variable yang hilang atau salah. Aplikasi berjalan selama berjam-jam atau berhari-hari sebelum nilai yang hilang akhirnya menjadi penting. Saat itu kegagalannya terlihat misterius. Saat itu orang-orang sudah stres.

Pola ini begitu umum sehingga harus diperlakukan sebagai cacat desain, bukan kecerobohan developer. Environment variable bukan metadata opsional. Mereka adalah konfigurasi kritis untuk runtime. Perlakukan mereka seperti itu.


Cerita yang familiar

Satu developer meminta bantuan developer lain untuk men-ship aplikasi. CI lolos. Deploy berhasil. Saat dites, semuanya terlihat baik-baik saja. Kemudian sebuah fitur berperilaku tidak terduga. Atau sebuah endpoint diam-diam mengembalikan payload yang salah.

Kami menggali log. Tidak ada yang jelas. Tidak ada stack trace yang menunjuk langsung ke masalah. Jam berlalu. Akhirnya seseorang menemukannya: environment variable yang wajib tidak pernah sampai ke production. Di lokal itu berhasil karena seseorang menambahkannya di mesin mereka dan lupa memperbarui config pusat.

Ini bukan edge case. Ini adalah salah satu mode kegagalan paling melelahkan di aplikasi modern. Murah untuk dihindari, dan mahal untuk diperbaiki saat sedang krisis.


Masalah sebenarnya

Ini bukan tentang menyalahkan orang. Ini tentang sistem yang membiarkan kesalahan konfigurasi laten hidup terlalu lama.

Jika kendaraan tidak memiliki rem, Anda tidak akan membiarkannya mulai bergerak. Anda tidak akan membiarkannya berjalan sampai rem dipasang. Namun banyak aplikasi mulai dan berjalan bahkan ketika konfigurasi esensial hilang. Kegagalan hanya menjadi terlihat ketika jalur kode tertentu dipicu.

Kita perlu memperlakukan konfigurasi sebagai first class citizen. Gagal lebih awal. Gagal dengan jelas. Itu adalah engineering yang bertanggung jawab.


Seharusnya bagaimana ia berperilaku

Minimal, dua aturan harus ditegakkan di setiap service:

  1. Aplikasi menolak untuk start jika environment variable yang wajib hilang. Jangan menunggu runtime untuk mengungkapkan masalahnya. Crash saat startup dan buat masalahnya langsung dan jelas.

  2. Error harus eksplisit dan mudah dibaca manusia. Jika API_KEY hilang, error runtime harus mengatakan itu persis. Tanpa tebak-tebakan. Tanpa nekromansi di log.

Dua aturan ini menghapus sebagian besar beban emosional dalam respons insiden. Mereka menghentikan perburuan lambat dan mengarahkan usaha ke tempat yang penting: memperbaiki kontrak konfigurasi antara deploy dan runtime.


Memvalidasi environment variable

Perlakukan environment variable sebagai input. Validasi mereka. Di dunia JavaScript ini mudah dan risikonya rendah.

Saya biasanya menyarankan validasi skema dengan Zod atau menggunakan abstraksi kecil seperti @t3-oss/env-nextjs. Polanya sederhana:

  • definisikan kontrak
  • validasi saat startup atau build time
  • gagal dengan keras (fail loud) jika ada yang salah

Itu memberi Anda type safety dan menghapus jebakan "works on my machine".

Contoh dengan Zod

env.ts

import { z } from "zod";

const envSchema = z.object({
  NODE_ENV: z.enum(["development", "test", "production"]),
  DATABASE_URL: z.string().url(),
  API_KEY: z.string().min(1),
  PORT: z.coerce.number().default(3000),
});

export const ENV = envSchema.parse(process.env);

Jika nilai yang wajib hilang atau salah format, proses akan crash saat startup dengan error yang jelas. Itulah perilaku yang Anda inginkan.

Kenapa saya mengekspor ENV sebagai uppercase

Saya mengekspor nilai yang divalidasi sebagai konstanta uppercase:

export const ENV = ...

Huruf besar menandakan konfigurasi global level deployment. Ketika Anda melihat ENV.DATABASE_URL, Anda tahu ini bukan sekadar variabel di memori. Ini adalah bagian dari kontrak aplikasi dengan deployment dan CI. Konvensi kecil itu mengurangi beban mental di codebase besar.


Level selanjutnya: Next.js dan helper env t3

Untuk Next.js, @t3-oss/env-nextjs bagus karena memisahkan config server dan client serta mendukung validasi build time.

src/env.ts

import { createEnv } from "@t3-oss/env-nextjs";
import { z } from "zod";

export const ENV = createEnv({
  server: {
    NODE_ENV: z.enum(["development", "test", "production"]),
    DATABASE_URL: z.string().url(),
    OPEN_AI_API_KEY: z.string().min(1),
  },
  client: {
    NEXT_PUBLIC_PUBLISHABLE_KEY: z.string().min(1),
  },
  runtimeEnv: {
    DATABASE_URL: process.env.DATABASE_URL,
    OPEN_AI_API_KEY: process.env.OPEN_AI_API_KEY,
    NEXT_PUBLIC_PUBLISHABLE_KEY: process.env.NEXT_PUBLIC_PUBLISHABLE_KEY,
  },
});

Mengimpor file env di next.config.ts membuat kesalahan konfigurasi gagal lebih awal, sebelum deployment, yang bahkan lebih baik.

import "./src/env";

const nextConfig = {};

export default nextConfig;

.env.example adalah kontrak

Commit .env.example. File ini bukan sekadar dokumentasi. Ini adalah kontrak antara developer, CI, dan infrastruktur production. Ini menunjukkan key apa yang wajib dan bentuk apa yang seharusnya mereka miliki.

NODE_ENV=production
DATABASE_URL=
OPEN_AI_API_KEY=
NEXT_PUBLIC_PUBLISHABLE_KEY=
PORT=3000

Jika variabel hilang dari file ini, itu adalah sinyal bahwa sesuatu bukan bagian dari kontrak.


Hasilnya (The payoff)

Environment variable yang tidak diperiksa menyebabkan kerapuhan yang tidak terlihat.

Ketika Anda memvalidasi lebih awal:

  • DevOps membuang lebih sedikit waktu pada petunjuk palsu
  • Engineer kurang tidur berkurang karena bug production misterius
  • Deployment menjadi kurang membuat stres
  • “Works on my machine” berhenti menjadi hasil yang dapat diterima

Memvalidasi env bukan pilihan. Ini adalah bagian dari sabuk pengaman yang Anda pakai sebelum menyalakan mobil.


Checklist praktis

Anda tidak butuh sistem kompleks untuk melakukan ini dengan benar. Mulai dengan langkah kecil:

  1. Tambahkan skema dan validasi saat startup.
  2. Ekspor config yang divalidasi sebagai konstanta global seperti ENV.
  3. Commit .env.example dan perlakukan sebagai kontrak kanonik.
  4. Jika memungkinkan, impor validasi env ke dalam langkah build Anda untuk gagal cepat selama CI.

Langkah-langkah ini berbiaya rendah dan ROI tinggi.


Pemikiran terakhir

Jika aplikasi Anda masih bergantung pada nilai process.env yang tidak diperiksa, Anda tidak bertanya apakah production akan rusak. Anda bertanya kapan itu akan rusak.

Gagal lebih awal. Gagal dengan jelas. Buat konfigurasi mustahil untuk diabaikan.

Artikel Lainnya

Saya Berhenti Menggali Log

Saya Berhenti Menggali Log

Debugging berubah ketika saya berhenti membaca log secara manual dan mulai menggunakan agen AI untuk mengkorelasikan error di seluruh data observabilitas - root cause lebih cepat, jalan buntu lebih sedikit.

Kecepatan Bukanlah Bagian Tersulit dalam CI CD

Kecepatan Bukanlah Bagian Tersulit dalam CI CD

Pipeline cepat tidak menghilangkan rasa takut saat shipping. Kepercayaan diri datang dari rollback yang aman, feature flags, dan sistem yang berperilaku dapat diprediksi saat terjadi kesalahan.