Fundamental #
Ada dua cara tim engineering bisa bergerak cepat. Cara pertama: minimalkan proses, langsung push ke main, review nanti kalau ada masalah. Cara ini terasa cepat di awal — tidak ada yang menghalangi kode untuk masuk ke codebase. Tapi cepat yang ini adalah cepat yang meminjam dari masa depan. Bug yang seharusnya tertangkap di review lolos ke produksi. Keputusan arsitektur yang buruk mengeras menjadi technical debt yang tidak bisa diubah tanpa biaya besar. Pengetahuan tentang sistem terkonsentrasi di satu atau dua orang — dan ketika mereka pergi, pengetahuan itu ikut pergi.
Cara kedua: bangun proses pull request dan code review yang benar sejak awal. Cara ini terasa lebih lambat di permukaan — ada waktu yang dihabiskan untuk menulis deskripsi, menunggu review, merespons komentar. Tapi tim yang melakukan ini dengan benar justru bergerak lebih cepat dalam jangka panjang, karena kualitas yang konsisten mengurangi rework, knowledge yang tersebar ke seluruh tim mengurangi bottleneck, dan keputusan teknis yang terdokumentasi membuat onboarding engineer baru jauh lebih mudah.
Pull request dan code review bukan ritual administratif. Mereka adalah dua dari sedikit mekanisme yang tersedia untuk tim engineering dalam menjaga kualitas, menyebarkan pengetahuan, dan membangun sistem yang bisa bertahan lama. Memahami mengapa — bukan hanya bagaimana — adalah awal dari menggunakannya dengan benar.
Apa yang Sebenarnya Terjadi dalam Sebuah Pull Request #
Pull request sering diartikan sempit sebagai “mekanisme untuk merge code”. Ini benar secara teknis, tapi melewatkan sebagian besar nilai yang seharusnya ada di sana. PR yang berfungsi dengan baik adalah sebuah unit komunikasi — ia menyampaikan tiga hal sekaligus kepada siapapun yang membacanya, sekarang maupun enam bulan ke depan.
Apa yang berubah. Diff kode menunjukkan perubahan yang terjadi secara eksplisit. Ini adalah bagian yang paling mudah dilihat.
Mengapa berubah. Konteks bisnis, teknis, atau operasional yang mendorong perubahan ini. Tanpa ini, reviewer tidak bisa menilai apakah pendekatan yang dipilih sudah tepat. Dan enam bulan kemudian, tidak ada yang tahu mengapa keputusan tertentu dibuat.
Bagaimana cara memverifikasinya. Instruksi testing, edge case yang perlu diperhatikan, dan hal-hal yang mungkin terdampak tapi tidak terlihat di diff.
Ketiga hal ini sama pentingnya. PR yang hanya berisi diff kode tanpa konteks adalah PR yang memaksa reviewer menebak-nebak — dan reviewer yang menebak-nebak tidak bisa memberikan review yang bermakna.
PR sebagai unit komunikasi:
┌──────────────────────────────────────────────────────────┐
│ Pull Request: "Add rate limiting to payment endpoint" │
├──────────────────────────────────────────────────────────┤
│ WHAT │ Diff kode — perubahan yang terlihat di file │
├────────┼─────────────────────────────────────────────────┤
│ WHY │ Deskripsi: konteks, masalah yang dipecahkan, │
│ │ keputusan yang diambil dan alasannya │
├────────┼─────────────────────────────────────────────────┤
│ HOW │ Cara testing, dampak ke sistem lain, │
│ │ hal yang perlu diperhatikan reviewer │
└────────┴─────────────────────────────────────────────────┘
PR tanpa WHY dan HOW → reviewer menebak → review tidak bermakna
PR dengan ketiganya → review yang fokus dan produktif
Mengapa Code Review Lebih dari Sekadar Mencari Bug #
Banyak tim memahami code review hanya sebagai mekanisme untuk mencegah bug masuk ke produksi. Ini valid — tapi hanya sebagian dari nilainya. Ada setidaknya empat fungsi lain yang sama pentingnya.
Distribusi Pengetahuan #
Setiap kali seorang engineer me-review PR orang lain, ia belajar sesuatu tentang bagian sistem yang mungkin selama ini tidak ia sentuh. Setiap kali PR-nya direview, ia menerima perspektif dari orang dengan konteks yang berbeda. Seiring waktu, proses ini membangun pemahaman kolektif tentang sistem — tidak ada satu titik pun di mana pengetahuan terkonsentrasi.
Tim yang tidak melakukan code review secara serius hampir selalu memiliki pola yang sama: ada satu atau dua orang yang “paling tahu” tentang bagian-bagian kritis sistem, dan ketika mereka tidak ada — sakit, libur, resign — tim tidak bisa bergerak.
Konsistensi Arsitektur #
Bug bisa ditemukan oleh testing. Tapi inkonsistensi arsitektur — pola yang berbeda untuk masalah yang sama, abstraksi yang tumpang tindih, keputusan yang berlawanan dengan keputusan yang sudah dibuat di tempat lain — hanya bisa ditangkap oleh orang yang membaca kode dengan konteks sistem yang lebih luas.
Code review adalah satu-satunya mekanisme yang secara rutin membawa perspektif “sistem secara keseluruhan” ke setiap perubahan yang dibuat.
Dokumentasi yang Hidup #
Diskusi di thread komentar PR adalah dokumentasi yang paling berharga tentang mengapa sesuatu diimplementasi dengan cara tertentu. Lebih berharga dari wiki yang tidak diupdate, lebih berharga dari komentar di kode yang sering tertinggal. Ketika enam bulan kemudian ada yang bertanya “kenapa kita pakai pendekatan ini?”, jawaban seringkali ada di thread PR lama.
Standar yang Terjaga tanpa Bergantung pada Individu #
Tim yang melakukan code review dengan konsisten tidak membutuhkan satu “wali kode” yang mengontrol semua keputusan teknis. Standar dijaga secara kolektif melalui proses review — dan standar ini bisa berkembang melalui diskusi di PR, bukan hanya melalui dekrit dari satu orang.
Biaya yang Tidak Terlihat dari Code Review yang Buruk #
Code review yang buruk — terlalu cepat, terlalu lambat, atau tidak fokus — memiliki biaya yang sering tidak dihitung karena tidak terlihat secara langsung.
Biaya tersembunyi code review yang buruk:
Terlalu cepat ("LGTM" tanpa baca):
→ Bug yang seharusnya tertangkap lolos ke produksi
→ Biaya fix di produksi: 10-100x lebih mahal dari fix di PR
→ Pola buruk masuk ke codebase dan menjadi preseden
Terlalu lambat (PR menganggur berhari-hari):
→ Author kehilangan konteks — harus context switch kembali
→ Merge conflict meningkat seiring waktu
→ Frustrasi → developer mulai buat PR yang lebih besar
agar "tidak sering" menunggu
Tidak fokus (hanya komentar style, lewatkan logika):
→ Komentar banyak tapi nilai sedikit
→ Author merasa di-nitpick, bukan dibantu
→ Bug logika tetap lolos meski review "selesai"
Defensif (reviewer/author tidak bisa terima feedback):
→ Diskusi teknis berubah menjadi konflik personal
→ PR menjadi sesuatu yang dihindari, bukan dimanfaatkan
→ Kualitas turun karena orang tidak mau feedback
Prinsip untuk Author: Membuat PR yang Bisa Direview #
Sebelum membuka PR, ada tanggung jawab yang ada di sisi author — tanggung jawab untuk memastikan PR tersebut bisa direview dengan efektif. Review yang baik tidak bisa terjadi pada PR yang tidak disiapkan dengan baik.
Fokus: Satu PR untuk Satu Tujuan #
Sebuah PR harus bisa dijawab dengan satu kalimat: “PR ini melakukan X.” Kalau jawabannya adalah “PR ini melakukan X, Y, refactor Z, dan memperbaiki beberapa style issue”, itu adalah tanda bahwa PR terlalu lebar.
PR yang terlalu lebar — masalah yang sering terjadi:
PR: "Implement user authentication"
├── Tambah tabel users dan sessions (schema change)
├── Implement login endpoint
├── Implement logout endpoint
├── Refactor error handling di semua endpoint lain
├── Update dependency versions
└── Fix typo di beberapa file
Masalah:
→ Reviewer harus memahami konteks yang sangat berbeda sekaligus
→ Satu bug di bagian refactor bisa memblokir seluruh feature
→ Jika PR perlu di-rollback, semua perubahan ikut ter-rollback
→ Review memakan waktu lama → reviewer kehilangan fokus
PR yang lebih baik (dipecah):
PR 1: "Add users and sessions schema"
PR 2: "Implement login/logout endpoints"
PR 3: "Refactor error handling" (PR terpisah, tidak tergantung timing)
Deskripsi yang Memberikan Konteks #
Deskripsi PR adalah satu-satunya kesempatan author untuk berbicara langsung kepada reviewer sebelum reviewer melihat satu baris kode pun. Gunakan kesempatan ini dengan baik.
## Contoh deskripsi PR yang buruk:
Add rate limiting
---
## Contoh deskripsi PR yang baik:
### Latar Belakang
Endpoint `/api/payments` saat ini tidak memiliki rate limiting. Selama incident
minggu lalu, satu client yang mengalami bug di sisi mereka mengirimkan 500 request/detik
ke endpoint ini selama 10 menit, menyebabkan database overload.
### Perubahan
- Tambah middleware rate limiting menggunakan token bucket algorithm
- Limit: 60 request/menit per user_id, 10 request/menit per IP untuk guest
- Respons 429 dengan header `Retry-After` yang menunjukkan kapan bisa coba lagi
### Keputusan yang Diambil
Menggunakan Redis sebagai store untuk rate limit counter (bukan in-memory) karena
kita memiliki multiple app instance. Token bucket dipilih vs leaky bucket karena
lebih toleran terhadap burst request yang wajar.
### Cara Testing
1. Jalankan endpoint 70x dalam satu menit dengan user yang sama
2. Request ke-61 seharusnya mendapat 429
3. Tunggu 1 menit, request berikutnya seharusnya berhasil lagi
### Dampak
- Endpoint `/api/payments` saja yang terdampak
- Redis dependency sudah ada, tidak ada dependency baru
- Perlu update dokumentasi API untuk mencantumkan rate limit
Perbedaan antara dua contoh ini bukan soal panjang atau pendek — tapi soal seberapa banyak konteks yang diberikan kepada reviewer untuk membuat penilaian yang bermakna.
Self-Review Sebelum Request Review #
Sebelum menambahkan reviewer, author harus menjadi reviewer pertama untuk PR-nya sendiri. Ini adalah langkah sederhana yang sering dilewatkan tapi menghilangkan banyak noise dari review orang lain.
Checklist self-review sebelum request review:
□ Baca seluruh diff dari perspektif reviewer — apakah alurnya masuk akal?
□ Apakah ada kode yang kamu tulis tapi lupa dipahami sendiri?
□ Apakah ada TODO atau komentar sementara yang harus dihapus?
□ Apakah test sudah mencakup happy path dan edge case yang relevan?
□ Apakah ada perubahan yang tidak sengaja masuk (whitespace, debug log)?
□ Apakah CI sudah hijau — linting, test, build semuanya lolos?
□ Apakah deskripsi PR sudah mencerminkan apa yang sebenarnya berubah?
Prinsip untuk Reviewer: Memberi Feedback yang Bernilai #
Tanggung jawab reviewer sama besarnya dengan tanggung jawab author. Review yang efektif bukan tentang seberapa banyak komentar yang diberikan — tapi tentang seberapa bermakna setiap komentar itu.
Prioritaskan yang Penting #
Tidak semua masalah dalam kode sama pentingnya. Reviewer yang efektif tahu cara memprioritaskan apa yang harus difokuskan.
Hirarki prioritas dalam code review:
Level 1 — HARUS diperbaiki sebelum merge:
→ Bug logika yang bisa menyebabkan data salah atau crash
→ Security vulnerability (SQL injection, XSS, credential leak)
→ Melanggar kontrak penting (breaking API, data contract)
→ Race condition yang nyata
→ Performance issue yang signifikan di path kritikal
Level 2 — Sangat disarankan, bisa block merge:
→ Desain yang akan sulit di-maintain atau di-extend
→ Missing edge case yang kemungkinan terjadi di produksi
→ Melanggar pola arsitektur yang sudah disepakati tim
→ Error handling yang tidak tepat
Level 3 — Saran yang baik, tidak block merge:
→ Cara alternatif yang lebih idiomatik
→ Naming yang lebih ekspresif
→ Refactor minor yang meningkatkan readability
Level 4 — Nitpick (tandai eksplisit sebagai nitpick):
→ Formatting yang tidak ditangani linter
→ Preferensi gaya personal yang tidak ada di guideline
→ Hal yang tidak ada bedanya di luar preferensi
Berikan Konteks di Setiap Komentar #
Komentar tanpa penjelasan hampir selalu memicu defensif. Komentar dengan penjelasan yang jelas membuka diskusi yang produktif.
Komentar review yang buruk vs baik:
✗ "Ini salah."
→ Tidak ada informasi tentang apa yang salah atau mengapa
✗ "Kenapa pakai pendekatan ini?"
→ Pertanyaan tanpa arah yang bisa ditafsirkan sebagai kritik
✗ "Coba pakai X."
→ Tidak ada alasan, author tidak belajar apapun
✓ "Fungsi ini akan gagal jika `user` null — ada skenario di mana
endpoint bisa dipanggil sebelum user diload? Kalau ya, perlu
tambahkan null check atau early return di awal."
✓ "Pertimbangkan menggunakan `findOrCreate` di sini daripada
`find` + conditional `create` — lebih aman dari race condition
di antara dua operasi. Referensi: [link ke dokumentasi]"
✓ "[nitpick] `userData` → `user` lebih konsisten dengan naming
di file lain. Tidak urgent, bisa diubah di PR berikutnya."
Bedakan Komentar Blocking dan Non-Blocking #
Reviewer harus eksplisit tentang mana komentar yang harus diselesaikan sebelum merge dan mana yang hanya saran. Tanpa kejelasan ini, author tidak tahu mana yang harus diprioritaskan dan proses review bisa berlarut-larut.
Konvensi label komentar yang umum digunakan:
[blocking] → Harus diperbaiki sebelum merge
"Ini akan menyebabkan data corruption jika X terjadi."
[suggestion] → Disarankan, tapi tidak harus sebelum merge
"Mungkin lebih mudah dibaca kalau..."
[question] → Pertanyaan yang butuh klarifikasi, belum tentu perlu perubahan
"Apakah ini sengaja menangani kasus Y juga?"
[nitpick] → Preferensi minor, tidak block merge
"[nitpick] Nama variabel bisa lebih deskriptif"
[praise] → Apresiasi untuk pendekatan yang baik
"Suka cara ini handle edge case — bersih!"
Dengan label ini, author langsung tahu:
→ Apa yang harus diselesaikan sekarang (blocking)
→ Apa yang bisa dipertimbangkan (suggestion)
→ Apa yang tidak perlu ditindaklanjuti segera (nitpick)
Apresiasi yang Baik #
Review tidak harus selalu berisi komentar tentang hal yang perlu diperbaiki. Komentar positif — “pendekatan ini elegan”, “test coverage di sini bagus” — bukan basa-basi. Ia membantu author memahami apa yang sudah benar sehingga bisa diulang, dan menciptakan atmosfer diskusi yang lebih terbuka.
Siklus PR yang Sehat #
PR yang baik mengikuti ritme yang jelas dari awal sampai merge.
Siklus PR yang sehat:
Author Reviewer
────── ────────
1. Buat branch, tulis kode
2. Self-review
3. Tulis deskripsi PR yang baik
4. Request review
5. Review dalam 1-2 hari kerja
6. Berikan komentar dengan label jelas
7. Respons setiap komentar
(setuju/tidak setuju, tapi
dengan alasan)
8. Push perubahan jika perlu
9. Re-review perubahan
10. Approve atau request changes lagi
11. Merge setelah approved
12. Delete branch
Prinsip timing:
→ Author harus request review dalam state yang benar-benar siap
→ Reviewer harus merespons dalam 1 hari kerja (SLA yang wajar)
→ Author harus merespons komentar dalam hari yang sama
→ Jangan biarkan PR menganggur lebih dari 2 hari tanpa update
Draft PR: Alat yang Sering Dilewatkan #
Draft PR adalah PR yang secara eksplisit ditandai sebagai “belum siap untuk review final” — ia ada untuk mendapatkan feedback awal tentang arah atau desain, bukan untuk approval.
Kapan menggunakan Draft PR:
✓ Ingin feedback tentang arah desain sebelum menulis semua kode
✓ Pekerjaan masih dalam progress, tapi ingin visibility ke tim
✓ Butuh diskusi tentang pendekatan sebelum investasi waktu lebih lanjut
✓ Implementasi kompleks yang perlu validasi asumsi awal
Yang tidak boleh dilakukan:
✗ Membiarkan Draft PR terbuka terlalu lama tanpa update
✗ Menggunakan Draft PR untuk menghindari review yang sebenarnya
✗ Request review formal sebelum mengubah Draft ke Ready
Anti-Pattern yang Harus Dihindari #
Dari sisi Author:
✗ PR monster — ribuan baris perubahan yang tidak mungkin direview dengan baik
dalam satu sesi. Reviewer akan approve tanpa benar-benar membaca.
✗ PR tanpa deskripsi — "Fix bug" atau "Update" tanpa konteks apapun.
Reviewer dipaksa menebak apa yang terjadi dan mengapa.
✗ Push langsung ke main tanpa PR — "cepat" yang mengorbankan semua manfaat
review, visibility, dan dokumentasi.
✗ Mengabaikan komentar tanpa respons — reviewer tidak tahu apakah komentar
dibaca, disetujui, atau ditolak. Loop tidak pernah selesai.
✗ Resolve komentar tanpa menyelesaikannya — menandai komentar sebagai resolved
padahal belum ada perubahan. Reviewer perlu re-review dari awal.
────────────────────────────────────────────────────────────────────────────────
Dari sisi Reviewer:
✗ "LGTM" tanpa membaca — ini bukan review, ini stamp approval.
Memberikan false sense of security dan tidak memberikan nilai apapun.
✗ Hanya komentar style — menghabiskan energi untuk hal yang seharusnya
ditangani oleh linter otomatis, melewatkan masalah logika yang nyata.
✗ Komentar pasif-agresif — "Ini harusnya sudah kamu tahu", "Kenapa
nggak pakai cara yang benar?". Membuat author defensif dan merusak
budaya team.
✗ Terlalu lambat merespons — PR yang menganggur 3-5 hari membuat author
kehilangan konteks, merge conflict menumpuk, dan frustrasi meningkat.
✗ Blocking atas preferensi personal — menahan merge karena tidak setuju
dengan style atau pendekatan yang tidak ada di coding guideline.
Ini abuse of reviewer power.
Checklist Review Fundamental PR #
SEBAGAI AUTHOR — sebelum request review:
□ PR menjawab satu tujuan yang bisa diringkas dalam satu kalimat
□ Deskripsi menjelaskan latar belakang, perubahan, keputusan, dan cara testing
□ Self-review sudah dilakukan — tidak ada kode debug, TODO, atau perubahan tidak sengaja
□ Test sudah ada dan CI sudah hijau (linting, test, build)
□ PR tidak lebih dari ~300-400 baris — jika lebih, pertimbangkan pecah menjadi beberapa PR
SEBAGAI REVIEWER — saat melakukan review:
□ Baca deskripsi dulu sebelum melihat diff
□ Prioritaskan bug, security, dan desain — bukan style
□ Setiap komentar memiliki penjelasan mengapa, bukan hanya apa
□ Komentar blocking dan non-blocking dibedakan dengan label eksplisit
□ Komentar nitpick ditandai sebagai nitpick agar tidak menghalangi merge
□ Ada keputusan jelas di akhir: Approve, Request Changes, atau Comment
SEBAGAI TIM:
□ Ada SLA yang disepakati untuk response time review (contoh: 1 hari kerja)
□ Ada template PR yang membantu author memberikan konteks yang konsisten
□ Linter dan formatter berjalan otomatis di CI — komentar style tidak perlu dari manusia
□ Code review diperlakukan sebagai investasi, bukan beban tambahan
Ringkasan #
- PR adalah unit komunikasi, bukan hanya mekanisme merge — ia harus menyampaikan apa yang berubah, mengapa berubah, dan bagaimana cara memverifikasinya. Tanpa konteks, review tidak bisa bermakna.
- Code review mendistribusikan pengetahuan — setiap review adalah kesempatan untuk menyebarkan pemahaman tentang sistem ke lebih banyak orang. Tim yang review dengan serius tidak punya single point of failure di level pengetahuan.
- PR yang fokus lebih mudah direview dengan baik — satu PR untuk satu tujuan. PR yang terlalu lebar dipaksa direview tanpa fokus, atau di-approve tanpa benar-benar dibaca.
- Deskripsi PR sama pentingnya dengan kodenya — latar belakang, keputusan yang diambil, dan cara testing adalah bagian dari PR yang tidak bisa dilihat dari diff saja.
- Self-review adalah langkah pertama — author harus menjadi reviewer pertama untuk PR-nya sendiri sebelum meminta orang lain melakukannya.
- Prioritaskan bug, security, dan desain — bukan style — style adalah pekerjaan linter, bukan reviewer manusia. Reviewer harus fokus pada hal yang hanya bisa ditangkap oleh pemikiran manusia.
- Setiap komentar butuh penjelasan — komentar tanpa konteks memicu defensif. Komentar dengan penjelasan membuka diskusi. Jelaskan mengapa, bukan hanya apa.
- Bedakan komentar blocking dan nitpick secara eksplisit — tanpa label yang jelas, author tidak tahu mana yang harus diselesaikan sekarang dan mana yang bisa ditindaklanjuti nanti.
- Response time review adalah kunci — PR yang menganggur berhari-hari membunuh momentum, menciptakan merge conflict, dan membuat developer frustrasi. SLA review yang jelas adalah bagian dari proses yang sehat.
- Kode adalah aset tim, bukan milik individu — review bukan audit personal. Yang dikritik adalah implementasi, bukan orangnya. Budaya ini harus dibangun dan dijaga secara aktif.