Tidak ada penulis yang bisa melihat semua kesalahan tulisannya sendiri; begitu pula tidak ada programmer yang kebal terhadap buta logika pada kodenya sendiri. Code review memasukkan pasangan mata kedua sebelum perubahan menyatu ke basis kode: bug tertangkap saat masih murah, pengetahuan menyebar antar anggota tim, dan keputusan desain mendapat pengecekan silang. Pada chapter ini kita bahas cara melakukan review yang terstruktur — mulai dari urutan prioritas pemeriksaan, checklist yang konsisten, sampai cara memberi dan menerima umpan balik tanpa melukai siapa pun.

Tujuan Review: Keabsahan, Desain, Lalu Keterbacaan #

Energi reviewer terbatas, sehingga urutan pemeriksaan menentukan hasilnya. Urutan yang sehat adalah keabsahan (correctness) lebih dulu, desain kedua, keterbacaan terakhir. Keabsahan berarti perubahan menghasilkan perilaku yang benar untuk semua masukan — bug yang lolos review akan hidup berhari-hari atau berminggu-minggu di produksi sebelum ditemukan. Desain berarti perubahan menempatkan logika pada lapisan yang tepat dan tidak menanam masalah struktural yang akan membebani fitur berikutnya. Keterbacaan berada terakhir bukan karena tidak penting, melainkan karena paling subjektif dan paling mudah diotomasi dengan formatter.

Urutan ini bukan teori kosong. Reviewer yang menghabiskan energi pada perdebatan gaya penulisan akan kehabisan waktu sebelum sampai pada pertanyaan penting: apakah kasus batas sudah ditangani, dan apakah kegagalan ditangani dengan benar. Ketika gaya sudah ditangani mesin, sisa energi manusia bisa dipakai untuk hal yang hanya manusia bisa lihat.

Checklist Review sebagai Data #

Review yang baik antar reviewer konsisten: dua orang berbeda memeriksa hal yang sama dengan kedalaman yang setara. Cara termudah mencapainya adalah checklist. Kode berikut menyimpan checklist sebagai struktur data agar mudah diperbarui dan ditinjau oleh seluruh tim:

php
<?php
$checklistReview = [
    "keabsahan" => [
        "Apakah ada tes yang mengunci perilaku baru beserta kasus batasnya?",
        "Apakah kegagalan (exception, null, input tidak valid) sudah ditangani?",
        "Spot-check keamanan: escape output, parameter binding, validasi input?",
    ],
    "desain" => [
        "Apakah perubahan berada di lapisan yang tepat, bukan menembus batas modul?",
        "Apakah ada logika yang menduplikasi kode yang sudah ada?",
        "Apakah kontrak publik (signature, return type) tidak melonggar sembarangan?",
    ],
    "keterbacaan" => [
        "Apakah nama variabel dan fungsi menyampaikan maksud tanpa konteks tambahan?",
        "Apakah komentar menjelaskan mengapa, bukan apa?",
        "Apakah formatter dan linter sudah dijalankan sebelum meminta review?",
    ],
];

foreach ($checklistReview as $kategori => $butir) {
    echo ucfirst($kategori), " (", count($butir), " butir):\n";
    foreach ($butir as $nomor => $pertanyaan) {
        echo "  ", $nomor + 1, ". ", $pertanyaan, "\n";
    }
}

Output yang Dihasilkan:

text
Keabsahan (3 butir):
  1. Apakah ada tes yang mengunci perilaku baru beserta kasus batasnya?
  2. Apakah kegagalan (exception, null, input tidak valid) sudah ditangani?
  3. Spot-check keamanan: escape output, parameter binding, validasi input?
Desain (3 butir):
  1. Apakah perubahan berada di lapisan yang tepat, bukan menembus batas modul?
  2. Apakah ada logika yang menduplikasi kode yang sudah ada?
  3. Apakah kontrak publik (signature, return type) tidak melonggar sembarangan?
Keterbacaan (3 butir):
  1. Apakah nama variabel dan fungsi menyampaikan maksud tanpa konteks tambahan?
  2. Apakah komentar menjelaskan mengapa, bukan apa?
  3. Apakah formatter dan linter sudah dijalankan sebelum meminta review?

Perhatikan bahwa urutan kategori pada array mengikuti urutan tujuan review dari section sebelumnya, dan setiap butir berbentuk pertanyaan — bukan statemen. Butir terakhir pada kategori keterbacaan sengaja menyerahkan urusan gaya kepada mesin: penulis menjalankan formatter sebelum meminta review, sehingga reviewer tidak perlu mengomentari spasi atau posisi kurung kurawal sama sekali.

Membaca Temuan dan Memutuskan #

Hasil review biasanya berupa kumpulan temuan dengan tingkat keparahan berbeda. Kode berikut memodelkan empat temuan nyata lalu menghitung keputusan akhirnya: adanya temuan blocker berarti perubahan wajib diperbaiki, temuan mayor berarti boleh disetujui dengan catatan wajib, dan temuan minor saja berarti cukup disetujui:

php
<?php
$temuan = [
    ["kategori" => "keabsahan", "tingkat" => "blocker", "komentar" => 'Query di cariPengguna() memakai rangkaian string, bukan parameter binding'],
    ["kategori" => "keabsahan", "tingkat" => "blocker", "komentar" => 'Balap data: dua request bisa membuat nomor pesanan kembar'],
    ["kategori" => "desain", "tingkat" => "mayor", "komentar" => 'Perhitungan pajak menempel di controller, bukan di service'],
    ["kategori" => "keterbacaan", "tingkat" => "minor", "komentar" => 'Variabel $x akan lebih jelas bernama $sisaStok'],
];

function keputusanReview(array $temuan): string
{
    $blocker = count(array_filter($temuan, fn (array $t): bool => $t["tingkat"] === "blocker"));
    $mayor = count(array_filter($temuan, fn (array $t): bool => $t["tingkat"] === "mayor"));

    return match (true) {
        $blocker > 0 => "minta perubahan",
        $mayor > 0 => "setuju dengan komentar wajib",
        default => "setujui",
    };
}

foreach ($temuan as $temuanSatu) {
    echo "[", strtoupper($temuanSatu["tingkat"]), "] ", $temuanSatu["kategori"], " - ", $temuanSatu["komentar"], "\n";
}
echo "Keputusan: ", keputusanReview($temuan), "\n";

Output yang Dihasilkan:

text
[BLOCKER] keabsahan - Query di cariPengguna() memakai rangkaian string, bukan parameter binding
[BLOCKER] keabsahan - Balap data: dua request bisa membuat nomor pesanan kembar
[MAYOR] desain - Perhitungan pajak menempel di controller, bukan di service
[MINOR] keterbacaan - Variabel $x akan lebih jelas bernama $sisaStok
Keputusan: minta perubahan

Aturan pada keputusanReview() sengaja sederhana dan dapat ditulis dalam satu kalimat: blocker selalu menahan persetujuan. Kedua temuan blocker pada contoh memang berasal dari kategori keabsahan — query rentan injeksi dan potensi balap data (race condition) adalah jenis temuan yang tidak boleh menunggu. Sebaliknya, nama variabel yang kurang deskriptif tidak selayaknya menahan perubahan yang sudah benar secara perilaku.

Memberi Umpan Balik yang Bertanya, Bukan Memerintah #

Cara berkomentar menentukan apakah umpan balik diterima atau dipersoalkan. Prinsip pertama: bertanya, jangan memerintah. Pertanyaan mengundang penulis menjelaskan konteks yang mungkin tidak diketahui reviewer, sementara perintah menutup dialog dan terdengar seperti menggurui. Prinsip kedua: rujuk aturan, bukan selera. “Ini melanggar PSR-12 bagian fungsi” bisa didiskusikan dan diverifikasi; “saya tidak suka gaya ini” hanya berujung debarasan.

Kurang membantuLebih membantu
“Ganti nama variabel ini.”“Apakah $x dimaksudkan sebagai sisa stok? Kalau benar, boleh dinamai $sisaStok?”
“Ini salah, jangan rangkai string.”“Bagaimana kalau query ini memakai parameter binding? Rangkaian string mentah membuka celah SQL injection.”
“Kode ini rumit.”“Bagian perulangan ini butuh saya baca tiga kali; bisakah dipecah dengan extract method seperti di tagihan()?”
“Jangan tulis begitu.”“Konvensi tim di PSR-1 dan PSR-12 memakai kurung kurawal pada baris berikutnya — boleh disesuaikan?”

Pola pada kolom kanan konsisten: kalimat pertanyaan atau ajakan, alasan yang eksplisit, dan rujukan ke aturan yang bisa diperiksa bersama. Umpan balik semacam ini juga otomatis lebih spesifik — penyebut lokasi, alasan, dan alternatif memaksa reviewer berpikir sebelum menulis.

Menerima Umpan Balik #

Sisi penerima umpan balik punya tanggung jawab yang sama besar. Perlakukan komentar sebagai informasi tentang kode, bukan penilaian tentang kemampuan Anda. Ketika komentar tidak jelas, tanyakan — “maksudnya bagian yang mana, dan skenario apa yang rusak?” jauh lebih produktif daripada menduga-duga. Anda boleh menjelaskan alasan di balik keputusan, dan reviewer boleh salah karena tidak memegang konteks penuh; diskusi singkat biasanya cukup untuk menyepakati jalan tengah. Yang tidak sehat adalah mengabaikan komentar tanpa jejak, karena reviewer berikutnya akan menanyakan hal yang sama.

Ukuran PR dan Kualitas Review #

Pull request kecil hampir selalu direview lebih teliti dan lebih cepat daripada yang besar. Reviewer manusia kehilangan fokus setelah ratusan baris: komentar awal diperiksa cermat, bagian akhir cenderung dilewati dengan asumsi “sepertinya sudah oke”. Karena itu pecah fitur besar menjadi rangkaian PR kecil yang saling menumpuk — fondasi dulu, lalu lapisan berikutnya. PR kecil juga mempercepat iterasi: penulis menunggu balasan hitungan jam, bukan hari, dan revisi tidak menyeret perubahan yang tidak berkaitan.

Jebakan Review yang Umum #

Beberapa pola buruk muncul berulang di banyak tim. Mengenali bentuknya adalah setengah jalan menghindarinya:

  • Nitpicking gaya. Mengomentari indentasi dan penamaan yang sebenarnya sudah diatur PHP CS Fixer atau PHPStan berarti membuang waktu manusia untuk pekerjaan mesin. Otomatiskan lewat CI, lalu review hanya pelanggaran yang lolos dari alat.
  • Menyetujui tanpa membaca. Menekan tombol approve demi menghabiskan antrean review membuat seluruh proses menjadi teater — bahaya terbesar dari semua jebakan di daftar ini.
  • Bikeshedding. Perdebatan panjang soal hal kecil (nama variabel, warna tombol) sementara pertanyaan arsitektural yang menentukan belum tersentuh sama sekali.
  • Review sebagai ajang pamer. Reviewer yang sibuk menunjukkan kehebatannya sendiri membuat penulis defensif dan enggan meminta review berikutnya.

Persetujuan Bukan Kepemilikan #

Ketika Anda menyetujui sebuah perubahan, kepemilikan kode tetap berada di tangan penulisnya. Persetujuan bukan tanda serah terima tanggung jawab, melainkan pernyataan bahwa pemeriksaan sudah dilakukan dengan sungguh-sungguh dan tidak ditemukan alasan untuk menahannya. Implikasinya dua arah: reviewer tidak boleh memaksakan selera pribadinya atas keputusan penulis, dan penulis tidak boleh memakai persetujuan sebagai tameng ketika masalah muncul kemudian. Kode yang baik lahir dari dialog antara keduanya.

Ringkasan #

Review yang sehat punya urutan prioritas (keabsahan, desain, keterbacaan), checklist yang konsisten antar reviewer, dan budaya umpan balik yang bertanya serta merujuk aturan. PR kecil memperpendek siklusnya, otomasi menyerahkan urusan gaya kepada mesin, dan persetujuan dipahami sebagai hasil pemeriksaan sungguh-sungguh, bukan serah terima kepemilikan. Meski begitu, tidak semua perubahan selesai mulus: keputusan cepat kemarin kadang meninggalkan jejak yang harus dikelola hari ini — topik yang kita bahas pada chapter berikutnya, technical debt.

Referensi #