Cara Meninjau Kode Hasil AI Sebelum Masuk Production

Kode hasil AI terlihat selesai, padahal belum tentu benar. Protokol tinjauan praktis untuk niat perubahan, kebenaran, keamanan, pengujian, keterpeliharaan, dan kesiapan produksi, sebelum di-merge.

Sebagian besar kode yang dihasilkan AI tidak terlihat salah. Justru itu yang membuat proses review-nya lebih sulit daripada kode yang ditulis manusia.

Engineer manusia meninggalkan jejak: fungsi yang ditulis buru-buru, nama variabel yang tidak konsisten, komentar yang jujur mengaku ragu ("ini agak hacky, tapi jalan"). Model tidak meninggalkan jejak apa pun. Hasilnya rata dan percaya diri: penamaan rapi, struktur masuk akal, komentar yang menjelaskan niat seolah semuanya sudah dipastikan. Permukaannya terlihat selesai. Masalahnya ada di bawah, pada asumsi yang tidak pernah diperiksa.

Tulisan ini adalah protokol review untuk situasi seperti itu. Ditujukan untuk engineer yang meng-approve merge, bukan untuk model yang membuat patch-nya.

Contoh diff di bawah ini hipotetis dan hanya untuk ilustrasi. Bukan kutipan pekerjaan klien, dan tidak ada klaim bahwa ELCODE sudah menjalankan proses tertentu atau mendapat hasil terukur tertentu.

1. Masalahnya: kode yang mulus, kegagalan yang diam

Kode yang enak dibaca membuat kita meninjau lebih dangkal. Ada tiga pola yang sering muncul.

  • Benar hanya di kasus normal (happy path). Kodenya jalan untuk input yang dibayangkan model, dan hanya untuk itu. Daftar kosong, field yang tidak ada, atau form yang terkirim dua kali tidak pernah masuk hitungan.
  • Perkiraan yang terdengar yakin. Fungsinya ada, nama metodenya nyata, tapi perilakunya salah: offset paginasi geser satu, zona waktu diterapkan dua kali, pembulatan uang di titik yang keliru.
  • Masuk akal di satu bagian, tapi salah untuk keseluruhan sistem. Perubahannya wajar kalau dilihat sendiri, tapi menduplikasi helper yang sudah ada, melewati lapisan validasi, atau melanggar konvensi yang dipakai bagian lain.

Review bukan soal memperbaiki kalimat. Pertanyaannya bukan apakah diff-nya enak dibaca, tapi apakah sistemnya masih aman dijalankan setelah perubahan itu masuk.

2. Pahami niat perubahannya dulu

Sebelum membuka file, tanyakan empat hal.

  • Masalah apa yang diselesaikan, dan untuk siapa?
  • Requirement atau tiket mana yang dijawab perubahan ini?
  • Apa yang sengaja tidak termasuk?
  • Apa yang rusak kalau perubahan ini dibatalkan besok?

Kalau jawabannya tidak muat dalam tiga kalimat, perubahannya terlalu besar untuk direview dengan benar. Pecah dulu. Diff kecil dengan niat yang jelas masih bisa ditinjau; diff besar dengan cerita yang meyakinkan tidak.

Tinjau niatnya dulu, implementasinya belakangan. Kegagalan review yang paling mahal biasanya bukan sintaks yang terlewat, tapi perubahan yang sejak awal tidak perlu, atau menyelesaikan masalah yang berbeda dari yang disebut.

3. Kebenaran fungsional

Di bagian ini, "kelihatan benar" paling berisiko. Periksa hal-hal berikut terhadap requirement yang sebenarnya, bukan terhadap komentar di kodenya.

  • Input kosong dan tidak ada: null, string kosong, array kosong, nol, field opsional yang tidak dikirim.
  • Nilai di batas: halaman pertama, halaman terakhir, tepat sebanyak ukuran halaman, panjang maksimum yang diizinkan.
  • Perubahan status: submit dua kali, retry, event yang datang tidak berurutan, cache kedaluwarsa, dua orang mengubah record yang sama.
  • Jalur gagal: timeout, gagal sebagian, layanan di atasnya membalas 200 tapi isinya error.
  • Bagian lain yang bisa ikut rusak: siapa lagi yang memanggil fungsi, endpoint, atau tabel ini?

Contoh hipotetis. Anggap faktur dimiliki oleh sebuah akun, dan pemanggil hanya boleh mengakses faktur di akun itu. Endpoint wajib menegakkan aturan tersebut di sisi server. Versi di bawah ini tidak melakukannya.

// Sebelum: terlihat wajar, dan salah
export async function getInvoice(req, res) {
  const invoice = await db.invoice.findUnique({
    where: { id: req.params.id }
  });
  if (!invoice) return res.status(404).json({ error: "Not found" });
  return res.json(invoice);
}

Tidak ada yang rusak secara sintaks. Tapi ini celah otorisasi per objek: siapa pun yang sudah login bisa membaca faktur orang lain hanya dengan menebak id. Autentikasi menjawab "kamu siapa". Autentikasi tidak menjawab "boleh atau tidak kamu melihat data ini".

// Sesudah: dibatasi ke akun pemanggil
export async function getInvoice(req, res) {
  const invoice = await db.invoice.findFirst({
    where: { id: req.params.id, accountId: req.user.accountId }
  });
  if (!invoice) return res.status(404).json({ error: "Not found" });
  return res.json(invoice);
}

404 di sini disengaja. Kalau mengembalikan 403, artinya kita memberi tahu bahwa data itu memang ada di akun lain.

4. Keamanan

Keamanan adalah bagian yang paling gampang menipu dari diff yang percaya diri, karena kodenya bisa lolos semua tes fungsional tapi tetap bisa dieksploitasi.

  • Validasi di pintu masuk. Semua input dari luar divalidasi sekali, di batas sistem, dengan skema yang jelas: body, query, path, header, dan file upload.
  • Otorisasi di server. Menyembunyikan tombol di UI bukan kontrol akses. Setiap baca dan tulis dicek terhadap identitas pelakunya, termasuk data turunan.
  • Rahasia. Tidak ada key di bundle klien, log, pesan error, atau file fixture. Apa pun yang dikirim ke browser harus dianggap publik.
  • Dependensi. Asisten AI suka menambah paket. Periksa setiap entri baru di lockfile: siapa maintainer-nya, dari mana asalnya, ada install script atau tidak, dan apakah library standar sebenarnya sudah cukup.
  • Injeksi. SQL, shell, template, dan log injection semuanya berawal dari penggabungan string.

Contoh hipotetis kedua.

// Sebelum: kueri disusun dari string
const rows = await db.query(
  SELECT  FROM orders WHERE status = '${status}' ORDER BY created_at DESC LIMIT 50
);
// Sesudah: terparameterisasi
const rows = await db.query(
  "SELECT  FROM orders WHERE status = $1 ORDER BY created_at DESC LIMIT 50",
  [status]
);

Meloloskan tanda kutip dengan tangan bukan solusi. Solusinya parameterisasi, karena memisahkan kode dari data di level protokol.

5. Pengujian

Tes adalah bukti, bukan pelengkap. Tes yang lolos sebelum dan sesudah perubahan tidak membuktikan apa pun tentang perubahan itu.

  • Tesnya gagal kalau patch-nya tidak ada? Coba jalankan di commit sebelumnya.
  • Yang diuji perilaku yang terlihat pengguna atau pemanggil, atau urutan pemanggilan internal?
  • Edge case yang ditemukan saat review sudah jadi tes? Kalau belum, dia akan kembali sebagai bug.
  • Mock-nya menyembunyikan kegagalan? Repository tiruan yang tidak pernah mengembalikan hasil kosong tidak akan pernah menangkap kasus kosong.
  • Cakupan regresinya sudah termasuk endpoint, pemanggil, dan pihak yang memakai hasilnya?

Satu tes integrasi yang benar-benar menguji sesuatu bisa menangkap kegagalan yang tidak akan terlihat oleh sepuluh tes unit hasil generate yang isinya cuma mengulang implementasi.

6. Keterpeliharaan

Kode hasil AI cenderung membuat solusi yang kelihatan umum dan meyakinkan. Itu biaya perawatan, bukan kelebihan.

  • Keterbacaan: nama menjelaskan maksudnya dalam domain, bukan langkah implementasinya.
  • Kompleksitas: if bersarang dan fungsi panjang adalah tanda perlu dirapikan, bukan ditambah komentar.
  • Duplikasi: helper yang sama sekarang ada dua versi dengan nama berbeda.
  • Konsistensi: perubahannya mengikuti arsitektur, format error, dan penamaan yang sudah ada, atau menjelaskan kenapa harus berbeda.
  • Kode spekulatif: abstraksi, opsi, dan fallback yang ditambahkan "untuk jaga-jaga" padahal tidak ada yang memakai.

Pertanyaan yang perlu diajukan peninjau: kalau kode ini rusak enam bulan lagi, apakah orang yang mendebugnya akan paham kenapa kode ini ada?

7. Kesiapan produksi

Pemeriksaan terakhir sifatnya operasional, dan paling sering dilewati karena fiturnya sudah jalan di laptop.

  • Log: terstruktur, dengan correlation id yang menyambungkan request, job, dan error jadi satu jejak.
  • Observabilitas: ada metrik atau alarm yang menunjukkan fitur ini gagal sebelum pengguna melapor.
  • Rollback: perubahan bisa dibatalkan tanpa memulihkan data? Migrasinya reversibel atau hanya menambah?
  • Konfigurasi: environment variable baru terdokumentasi, punya nilai default, dan divalidasi saat aplikasi start.
  • Isolasi kegagalan: timeout, retry dengan backoff, dan idempotensi untuk apa pun yang bisa terkirim dua kali.
  • Seberapa luas dampaknya: apakah ini menyentuh tabel bersama, antrean bersama, atau key cache bersama?

8. Checklist praktis

Niat

  • Saya bisa menjelaskan perubahan ini dalam tiga kalimat, termasuk apa yang tidak termasuk.
  • Diff ini menjawab satu requirement, bukan ide yang terdengar bagus.

Kebenaran fungsional

  • Input kosong, tidak ada, batas, dan ganda ditangani dengan jelas.
  • Kalau gagal, error-nya jelas, bukan diam-diam dianggap sukses.
  • Pemanggil fungsi dan endpoint yang berubah sudah dicek.

Keamanan

  • Otorisasi dicek di server, per objek, bukan hanya per route.
  • Tidak ada rahasia, token, atau URL internal yang bocor ke klien, log, atau pesan error.
  • Dependensi baru sudah ditinjau, atau dihindari.
  • Query dan perintah memakai parameterisasi untuk nilai; nama kolom dinamis dan opsi sorting dibatasi ke allowlist eksplisit.

Pengujian

  • Setidaknya satu tes gagal tanpa patch ini.
  • Edge case yang ditemukan saat review sudah jadi tes.
  • Mock tidak menghilangkan mode kegagalan yang sedang diuji.

Operasional

  • Log dan metrik bisa menunjukkan kegagalan sebelum pengguna menyadarinya.
  • Alarm: tentukan seperti apa kegagalannya, siapa yang menerima notifikasi, dan apa yang harus dilakukan setelahnya.
  • Rollback bisa dilakukan tanpa memulihkan data.
  • Konfigurasi baru terdokumentasi dan tervalidasi.

Penutup

AI mempersingkat jarak antara ide dan patch yang jalan. Itu memang berguna, dan biasanya bukan hambatan terbesar tim. Yang jadi hambatan justru penilaian: memutuskan apa yang perlu ada, apa yang harus benar sebelum rilis, dan siapa yang bertanggung jawab kalau gagal.

Model bisa menulis patch-nya. Model tidak bisa menanggung akibatnya. Tanggung jawab itu tetap milik engineer, dan justru bagian itu yang perlu dijaga.

R. Luki Rahayu Agung

Founder & Lead Architect di ELCODE

Frontend engineer dan spesialis integrasi sistem yang berbasis di Bandung, Indonesia, serta pendiri ELCODE. Ia membangun platform analitik sektor publik, sistem visualisasi data, dan eksperimen AI terapan, termasuk pada Program SKALA bersama Bappenas.

← Kembali ke Indeks Jurnal