Code Review #

Code review dilakukan oleh hampir semua tim engineering. Tapi ada perbedaan besar antara tim yang melakukan code review dan tim yang melakukannya dengan benar. Tim pertama punya proses formal — setiap PR harus disetujui sebelum merge — tapi dalam praktiknya reviewnya dangkal, komentar yang diberikan berkisar pada style dan formatting, dan bug tetap lolos ke produksi. Tim kedua memperlakukan code review sebagai investasi nyata: waktu yang dihabiskan untuk review adalah waktu yang menghemat jauh lebih banyak waktu di debugging, hotfix, dan rework.

Perbedaan antara keduanya bukan pada tool atau proses formal yang digunakan. Perbedaannya ada pada pemahaman tentang apa yang sebenarnya dicari saat review, bagaimana memberikan feedback yang membuka diskusi alih-alih memicu defensifitas, dan bagaimana membangun ritual yang membuat semua pihak — reviewer dan author — merasa berinvestasi dalam kualitas yang sama.

Artikel ini membahas code review dari kedua sisi: bagaimana menjadi reviewer yang efektif, dan bagaimana menjadi author yang memudahkan reviewer melakukan pekerjaannya dengan baik.

Apa yang Sebenarnya Dicari dalam Code Review #

Sebelum membahas teknik, perlu dipahami dulu apa yang sebenarnya menjadi tujuan review — karena ini menentukan apa yang harus difokuskan dan apa yang bisa diabaikan.

Code review yang efektif mencari hal-hal yang tidak bisa ditangani oleh tool otomatis. Linter bisa menemukan formatting yang salah. Static analysis bisa menemukan beberapa pola yang berbahaya. Test bisa memverifikasi behavior pada input yang sudah didefinisikan. Yang tidak bisa dilakukan semua itu adalah: menilai apakah pendekatan yang dipilih sudah tepat untuk masalah yang ada, mendeteksi asumsi yang salah tentang behavior sistem, menemukan edge case yang belum terpikirkan, atau menilai apakah kode ini akan mudah di-maintain enam bulan dari sekarang.

Apa yang bisa dilakukan tool vs apa yang butuh manusia:

  Tool otomatis (linter, formatter, static analysis):
  → Formatting dan style yang konsisten
  → Unused variable, unreachable code
  → Beberapa pola berbahaya yang well-known (SQL injection pola sederhana, dsb)
  → Dependency vulnerability yang diketahui

  Reviewer manusia:
  → Apakah pendekatan yang dipilih sudah tepat untuk masalahnya?
  → Apakah ada asumsi yang salah tentang behavior sistem?
  → Apakah edge case X dan Y sudah dipertimbangkan?
  → Apakah ada race condition yang tidak terlihat?
  → Apakah ini akan mudah dipahami dan dimodifikasi 6 bulan lagi?
  → Apakah ini konsisten dengan pola arsitektur yang sudah ada?
  → Apakah ada implikasi keamanan yang tidak langsung terlihat?

Dengan pemahaman ini, reviewer bisa mengalokasikan energinya dengan tepat: biarkan tool menangani hal mekanis, fokuskan energi pada hal yang membutuhkan pemikiran.


Proses Review yang Sistematis #

Review yang efektif bukan tentang membaca kode dari atas ke bawah dan mengomentari setiap baris yang terlihat berbeda. Ada urutan yang lebih efektif.

flowchart TD
    A([Buka PR]) --> B[Baca deskripsi dan konteks]
    B --> C{Konteks sudah cukup?}
    C -- Tidak --> D[Minta klarifikasi ke author\nsebelum lanjut review]
    C -- Ya --> E[Pahami scope perubahan\nfile apa saja yang berubah]
    E --> F[Review high-level first:\ndesain, arsitektur, pendekatan]
    F --> G{Pendekatan sudah tepat?}
    G -- Tidak --> H[Komentar di level desain\nsebelum review detail]
    G -- Ya --> I[Review detail:\nlogika, edge case, security]
    I --> J[Review kualitas:\nnaming, readability, test]
    J --> K{Semua concern sudah dicatat?}
    K -- Ya --> L{Severity keseluruhan?}
    L -- Ada blocking issue --> M[Request Changes\njelaskan apa yang perlu diperbaiki]
    L -- Hanya suggestion/nitpick --> N[Approve dengan komentar\natau Comment tanpa decision]
    L -- Semua OK --> O[Approve]

Langkah 1: Baca Konteks Sebelum Melihat Diff #

Ini adalah langkah yang paling sering dilewatkan. Reviewer langsung membuka tab “Files changed” dan mulai membaca kode. Padahal, tanpa konteks dari deskripsi PR, reviewer tidak punya lensa yang tepat untuk mengevaluasi kode yang dibaca.

Baca deskripsi terlebih dahulu. Pahami masalah yang coba diselesaikan. Pahami pendekatan yang dipilih author. Baru kemudian buka diff — dan kamu akan melihat kode itu dengan mata yang jauh lebih tajam.

Langkah 2: High-Level Review Sebelum Detail #

Sebelum mengomentari nama variabel atau cara sebuah loop ditulis, evaluasi dulu apakah pendekatan keseluruhan sudah tepat. Memberikan feedback detail pada implementasi yang fundamentally salah adalah buang waktu — author harus membuang banyak kode dan menulis ulang, dan semua komentar detail tadi menjadi tidak relevan.

Urutan prioritas yang benar:

  Level 1 — Desain dan pendekatan (review ini dulu):
  → Apakah pendekatan ini tepat untuk masalahnya?
  → Apakah ada cara yang jauh lebih sederhana/aman yang terlewat?
  → Apakah ini konsisten dengan arsitektur yang sudah ada?
  → Apakah ada asumsi fundamental yang salah?

  Level 2 — Correctness dan keamanan:
  → Apakah logika bisnis sudah benar untuk semua kasus?
  → Apakah ada edge case yang tidak ditangani?
  → Apakah ada celah keamanan?
  → Apakah ada race condition?

  Level 3 — Kualitas dan maintainability:
  → Apakah naming sudah ekspresif dan konsisten?
  → Apakah kode mudah dipahami tanpa komentar tambahan?
  → Apakah test mencakup kasus yang relevan?
  → Apakah error handling sudah tepat?

  Level 4 — Nitpick (opsional, tandai eksplisit):
  → Preferensi minor yang tidak ada di guideline
  → Style yang tidak ditangani linter

Cara Memberikan Komentar yang Efektif #

Komentar adalah output utama dari proses review. Komentar yang baik membuka diskusi yang produktif. Komentar yang buruk memicu defensifitas dan merusak hubungan kerja.

Jelaskan Mengapa, Bukan Hanya Apa #

Komentar tanpa penjelasan hampir selalu diinterpretasikan lebih negatif dari yang dimaksud. Author tidak tahu apakah ini blocker, preferensi, atau sekadar pertanyaan — dan lebih penting lagi, mereka tidak bisa belajar dari komentar yang tidak menjelaskan reasoning-nya.

Komentar yang tidak efektif vs efektif:

  ✗ "Ini salah."
    → Tidak ada informasi. Salah dalam hal apa? Mengapa salah?

  ✗ "Gunakan X."
    → Instruksi tanpa penjelasan. Author mengikuti tapi tidak belajar.

  ✗ "Kenapa begini?"
    → Pertanyaan yang bisa terasa seperti tuduhan tergantung konteks.

  ✓ "Fungsi ini tidak menangani kasus ketika `user` adalah null.
     Ini bisa terjadi jika endpoint dipanggil sebelum session diinisialisasi.
     Pertimbangkan early return atau guard clause di awal fungsi."
    → Menjelaskan masalah, konteks, dan arah solusi.

  ✓ "Pertimbangkan menggunakan `findOrCreate` daripada `find` + conditional `create`.
     Pendekatan saat ini rentan race condition jika dua request masuk bersamaan —
     keduanya bisa lewat check `find` sebelum salah satu sempat `create`.
     [Link ke dokumentasi findOrCreate]"
    → Menjelaskan masalah, mengapa berbahaya, dan referensi solusi.

  ✓ "[nitpick] `userData` → `user` lebih konsisten dengan naming di file lain.
     Ini preferensi minor, bisa diubah di PR berikutnya jika tidak mengganggu flow."
    → Label jelas, penjelasan singkat, tidak blocking.

Gunakan Label yang Konsisten #

Label membantu author memahami prioritas tanpa harus menebak dari tone komentar. Ini juga membantu reviewer sendiri untuk berkomunikasi dengan lebih presisi tentang severity concern yang mereka miliki.

Konvensi label komentar yang disarankan:

  [blocking]
  → Harus diperbaiki sebelum merge. Bisa berupa bug, security issue,
    atau pelanggaran arsitektur yang serius.
  Contoh: "[blocking] Ini akan menyebabkan data loss jika transaction
           di-rollback setelah step pertama berhasil."

  [suggestion]
  → Disarankan kuat tapi tidak memblokir merge.
    Bisa ditindaklanjuti di PR ini atau dicatat untuk PR berikutnya.
  Contoh: "[suggestion] Menggunakan batch query di sini akan mengurangi
           N+1 calls yang akan terasa saat data besar."

  [question]
  → Pertanyaan yang butuh klarifikasi. Belum tentu menghasilkan perubahan,
    mungkin hanya butuh penjelasan tambahan di deskripsi.
  Contoh: "[question] Apakah ini sengaja menangani kasus X juga?
           Tidak terlihat di test tapi ingin memastikan."

  [nitpick]
  → Preferensi minor, tidak memblokir merge, tidak perlu segera.
    Author boleh mengabaikan jika tidak setuju.
  Contoh: "[nitpick] `processPaymentCallback` bisa jadi `handlePaymentCallback`
           — lebih konsisten dengan naming di handler lain."

  [praise]
  → Apresiasi untuk pendekatan yang baik atau solusi yang elegan.
    Bukan basa-basi — membantu author tahu apa yang sudah benar.
  Contoh: "[praise] Penanganan retry dengan exponential backoff di sini bersih
           dan sudah mempertimbangkan jitter. "

Tawarkan Solusi, Bukan Hanya Masalah #

Reviewer yang hanya menunjukkan masalah tanpa arah solusi melemparkan beban kembali ke author tanpa membantu. Reviewer yang menawarkan solusi konkret — bahkan jika hanya sebagai saran awal — menciptakan diskusi yang jauh lebih produktif.

# Komentar yang hanya menunjukkan masalah:
# "Ini tidak aman."

# Komentar yang jauh lebih berguna:
# "Penggunaan string formatting langsung di query ini rentan SQL injection.
#  Gunakan parameterized query:
#
#  ANTI-PATTERN (saat ini):
#  query = f"SELECT * FROM users WHERE email = '{email}'"
#
#  BENAR:
#  query = "SELECT * FROM users WHERE email = ?"
#  cursor.execute(query, (email,))
#
#  Atau jika menggunakan ORM:
#  User.objects.filter(email=email)"

Kapan Approve, Request Changes, atau Comment #

Keputusan akhir reviewer adalah salah satu hal yang paling sering tidak jelas dalam proses review. Apa threshold untuk approve? Kapan harus request changes? Kapan cukup comment tanpa keputusan?

flowchart TD
    A[Review selesai] --> B{Ada blocking issue?}
    B -- Ya --> C[Request Changes\nJelaskan apa yang HARUS diperbaiki\ndan mengapa]
    B -- Tidak --> D{Ada suggestion/pertanyaan?}
    D -- Ya, tapi tidak blocking --> E{Ingin author merespons\nsebelum merge?}
    E -- Ya --> F[Comment\ntanpa keputusan,\ntunggu klarifikasi]
    E -- Tidak --> G[Approve dengan komentar\nautor bisa merge tapi\npertimbangkan suggestion]
    D -- Tidak --> H[Approve bersih]

Approve berarti: “Saya memahami perubahan ini, tidak ada concern yang memblokir merge, dan saya siap mempertanggungjawabkan bersama jika ada masalah kemudian.”

Request Changes berarti: “Ada satu atau lebih hal yang harus diperbaiki sebelum PR ini bisa di-merge. Saya menjelaskan apa yang perlu berubah dan mengapa.” Gunakan ini hanya untuk blocking issue nyata — bukan untuk preferensi.

Comment (tanpa approve/reject) berarti: “Saya punya pertanyaan atau saran tapi belum siap memberikan keputusan. Butuh klarifikasi atau diskusi lebih lanjut.”

Request changes yang terlalu sering untuk hal-hal non-blocking menciptakan friction yang tidak perlu dan bisa membuat proses review terasa seperti gatekeeper yang menghalangi, bukan kolaborator yang membantu. Simpan request changes untuk isu yang benar-benar perlu diselesaikan sebelum kode masuk ke main.

Tanggung Jawab Author dalam Proses Review #

Code review bukan hanya tanggung jawab reviewer. Author memiliki peran aktif yang sama pentingnya dalam memastikan review berjalan efektif.

Merespons Setiap Komentar #

Setiap komentar reviewer harus mendapat respons — bahkan jika responsnya adalah tidak setuju. Tanpa respons, reviewer tidak tahu apakah komentar sudah dibaca, dipertimbangkan, atau diabaikan. Loop tidak pernah selesai.

Cara merespons komentar yang baik:

  Jika setuju dan sudah diperbaiki:
  → "Fixed. Menambahkan null check di awal fungsi seperti yang disarankan."
  → Jangan hanya resolve thread tanpa komentar apapun.

  Jika setuju tapi akan ditindaklanjuti di PR lain:
  → "Setuju. Saya catat ini untuk PR refactor berikutnya — JIRA-456."
  → Buat tiket jika belum ada.

  Jika tidak setuju:
  → "Saya melihat concern-nya, tapi di konteks ini X karena Y.
     Pendekatan alternatif Z memang lebih umum, tapi akan membutuhkan
     perubahan di A dan B yang out of scope untuk PR ini.
     Apakah ini masih blocking menurutmu?"
  → Jelaskan reasoning, jangan sekadar menolak.

  Jika pertanyaan sudah terjawab oleh deskripsi PR:
  → "Sudah dijelaskan di bagian 'Keputusan Teknis' di deskripsi —
     singkatnya, kita pilih X karena Y. Saya update deskripsinya
     agar lebih jelas."

Tidak Defensif terhadap Feedback #

Menerima feedback adalah skill yang perlu dilatih. Komentar tentang kode terasa personal karena kita yang menulisnya — tapi komentar itu ditujukan kepada kode, bukan kepada kita sebagai orang.

Mindset yang membantu:

  "Komentar ini salah" → "Bagaimana saya bisa menjelaskan reasoning-nya
                          dengan lebih baik? Atau reviewer ada poin yang
                          saya lewatkan?"

  "Dia terlalu perfeksionis" → "Apakah ini blocking atau suggestion?
                                Jika blocking, apa yang membuatnya concern?"

  "Ini sudah benar" → "Saya sudah yakin ini benar — bagaimana saya bisa
                       meyakinkan reviewer juga? Mungkin butuh penjelasan
                       lebih atau test case tambahan."

Menghindari Bikeshedding #

Bikeshedding adalah fenomena di mana diskusi review menghabiskan banyak waktu pada detail trivial (warna sepeda motor) sementara hal-hal penting (desain reaktor nuklir) disetujui begitu saja — karena detail trivial lebih mudah dipahami semua orang.

Dalam konteks code review, bikeshedding muncul ketika thread panjang terjadi tentang nama variabel, apakah harus single quote atau double quote, atau apakah harus ada spasi setelah koma — sementara asumsi arsitektur yang salah lolos tanpa komentar karena lebih sulit dievaluasi.

Tanda-tanda bikeshedding dalam review:

  ✗ Thread 10 komentar tentang apakah method harus bernama
    "calculateTotal" atau "computeTotal"
  ✗ Perdebatan panjang tentang apakah harus pakai early return atau nested if
  ✗ Reviewer mengomentari formatting yang seharusnya sudah ditangani linter
  ✗ Diskusi tentang preferensi personal yang tidak ada di coding guideline

  Yang seharusnya terjadi:
  → Hal-hal mekanis (formatting, style) ditangani linter otomatis
  → Preferensi personal yang tidak ada di guideline → nitpick atau skip
  → Fokus pada logic, security, design, dan maintainability

Cara terbaik mencegah bikeshedding adalah dengan menegakkan linter dan formatter yang menangani semua hal mekanis secara otomatis, sehingga reviewer tidak punya “ruang” untuk mengomentarinya karena sudah konsisten.


Ritme Review yang Sehat #

Code review hanya efektif jika menjadi bagian dari ritme kerja tim yang terprediksi. PR yang menganggur berhari-hari membuat author kehilangan konteks, menciptakan merge conflict, dan membuat proses review terasa seperti bottleneck.

SLA review yang sehat:

  Skenario ideal:
  → PR dibuka di pagi hari → direview sebelum EOD hari yang sama
  → PR dibuka sore hari → direview pagi hari berikutnya
  → Revisi kecil → direview dalam 2-4 jam setelah author push

  Sinyal ritme review yang tidak sehat:
  ✗ PR menganggur lebih dari 2 hari tanpa response dari reviewer manapun
  ✗ Author harus ping berulang kali untuk mendapat review
  ✗ Reviewer hanya available di window waktu yang sangat sempit
  ✗ PR di-approve tanpa komentar apapun pada PR yang kompleks

  Yang membantu menjaga ritme:
  → Dedicated time slot untuk review (misalnya: setelah standup, sebelum lunch)
  → Notifikasi review request yang tidak tenggelam dalam noise
  → Rotasi reviewer agar tidak selalu orang yang sama yang kebanjiran
  → Budget time review dalam sprint planning — review adalah pekerjaan, bukan tambahan

Review sebagai Investasi Knowledge Sharing #

Salah satu nilai code review yang paling sering diremehkan adalah efek knowledge sharing-nya. Setiap PR adalah kesempatan bagi reviewer untuk memahami area sistem yang mungkin selama ini tidak mereka sentuh, dan bagi author untuk mendapat perspektif dari orang yang melihat sistem dari sudut yang berbeda.

graph LR
    A[Author menulis PR] --> B[Reviewer melihat solusi baru]
    B --> C[Reviewer belajar: teknik, pola, atau domain baru]
    A --> D[Reviewer memberikan feedback]
    D --> E[Author belajar: perspektif baru, blind spot, atau standar tim]
    C --> F[Knowledge tersebar di tim]
    E --> F
    F --> G[Bus factor berkurang]
    F --> H[Tim lebih resilient]

Tim yang melakukan code review dengan serius tidak memiliki situasi di mana satu orang menjadi satu-satunya yang paham satu area sistem kritis. Pengetahuan tersebar secara alami melalui setiap review yang dilakukan.


Anti-Pattern yang Harus Dihindari #

Dari sisi reviewer:

✗ "LGTM" tanpa membaca
  Memberikan false sense of security. PR yang di-approve tanpa dibaca
  adalah PR yang masuk ke main tanpa quality gate yang sebenarnya.

✗ Hanya mengomentari style dan formatting
  Menghabiskan energi pada hal yang seharusnya ditangani linter.
  Masalah logika dan security tidak terdeteksi.

✗ Menunda review sampai PR "kadaluarsa"
  PR yang menganggur selama seminggu membuat author kehilangan konteks
  dan menciptakan merge conflict yang butuh waktu untuk diselesaikan.

✗ Menggunakan review sebagai alat kekuasaan
  Menahan approve untuk PR yang secara teknis sudah baik,
  atau memberikan komentar yang sengaja mencari-cari masalah.
  Ini merusak budaya tim dan menciptakan ketakutan, bukan respek.

────────────────────────────────────────────────────────────────────────────

Dari sisi author:

✗ Merge PR sebelum semua komentar blocking terselesaikan
  Melanggar kepercayaan reviewer dan merusak nilai proses review.

✗ Resolve thread reviewer tanpa memberikan respons
  Reviewer tidak tahu apakah concern sudah ditangani atau diabaikan.
  Thread harus dijawab, bukan hanya di-resolve secara unilateral.

✗ Defensif terhadap semua komentar
  Menganggap semua feedback sebagai serangan personal.
  Code review adalah diskusi teknis, bukan evaluasi diri.

✗ Request review pada PR yang belum siap
  CI masih merah, deskripsi kosong, kode masih ada debug log.
  Membuang waktu reviewer dan menciptakan kesan tidak profesional.

Checklist Review Code Review #

PERSIAPAN (sebagai author sebelum request review):
  □ Deskripsi PR sudah lengkap — latar belakang, perubahan, cara testing
  □ CI sudah hijau (linting, test, build)
  □ Self-review sudah dilakukan — tidak ada debug log, TODO tanpa tiket
  □ PR sudah fokus pada satu tujuan

PROSES REVIEW (sebagai reviewer):
  □ Baca deskripsi sebelum membuka diff
  □ Review desain dan pendekatan sebelum detail implementasi
  □ Setiap komentar memiliki penjelasan mengapa, bukan hanya apa
  □ Komentar menggunakan label: [blocking], [suggestion], [question], [nitpick]
  □ Tawarkan arah solusi, bukan hanya menunjukkan masalah
  □ Tidak mengomentari hal yang seharusnya ditangani linter otomatis
  □ Keputusan akhir jelas: Approve, Request Changes, atau Comment

MERESPONS REVIEW (sebagai author):
  □ Setiap komentar mendapat respons — setuju, tidak setuju, atau klarifikasi
  □ Perubahan berdasarkan feedback di-push dan reviewer di-notify
  □ Komentar blocking diselesaikan sebelum merge
  □ Thread di-resolve hanya setelah ada diskusi atau perubahan yang memadai

RITME TIM:
  □ Review dilakukan dalam 1 hari kerja setelah request
  □ Ada dedicated time untuk review — bukan hanya jika ada waktu sisa
  □ Reviewer di-rotate agar knowledge tersebar ke seluruh tim
  □ PR yang menganggur > 2 hari mendapat perhatian aktif

Ringkasan #

  • Code review mencari apa yang tidak bisa dilakukan tool — linter dan formatter menangani hal mekanis. Reviewer manusia fokus pada correctness, desain, edge case, dan maintainability jangka panjang.
  • Baca konteks sebelum membuka diff — deskripsi PR adalah lensa yang membuat semua yang ada di diff terlihat lebih jelas. Reviewer yang melewatkan langkah ini membutuhkan lebih banyak waktu dan menghasilkan komentar yang kurang tepat sasaran.
  • Review high-level sebelum detail — jangan habiskan energi untuk mengomentari implementasi yang fundamentally salah. Validasi desain dan pendekatan dulu, baru turun ke detail.
  • Setiap komentar harus menjelaskan mengapa — komentar tanpa penjelasan memicu defensifitas dan tidak mengajarkan apapun. Penjelasan yang baik membuka diskusi yang produktif.
  • Gunakan label komentar yang konsisten — [blocking], [suggestion], [question], [nitpick] menghilangkan ambiguitas dan membantu author memprioritaskan responsnya dengan tepat.
  • Approve berarti komitmen bersama — meng-approve PR berarti siap mempertanggungjawabkan bersama jika ada masalah. Ini bukan formalitas — ini adalah pernyataan kepercayaan.
  • Hindari bikeshedding — diskusi panjang tentang nama variabel dan style yang seharusnya ditangani linter adalah tanda review yang tidak efektif. Fokus pada hal yang membutuhkan pemikiran manusia.
  • Author merespons setiap komentar — resolve thread tanpa komentar adalah bentuk pengabaian yang merusak kepercayaan reviewer. Setiap concern butuh jawaban, bahkan jika jawabannya “saya tidak setuju karena X”.
  • Review adalah investasi knowledge sharing — setiap PR yang direview adalah kesempatan bagi tim untuk belajar dari satu sama lain dan mengurangi bus factor secara organik.
  • Ritme review yang terprediksi adalah fondasi — PR yang menganggur berhari-hari merusak momentum, menciptakan merge conflict, dan membuat developer frustrasi. Dedicated time untuk review bukan opsional — ini bagian dari pekerjaan.

← Sebelumnya: One PR, One Purpose   Berikutnya: Code Review Meeting →

About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact