Code Review Checklist #

Code review tanpa struktur yang jelas menghasilkan dua masalah yang tampak berlawanan tapi sama berbahayanya. Yang pertama: review yang terlalu longgar — reviewer menyetujui hampir semua PR dengan komentar minimal karena tidak ada panduan tentang apa yang perlu diperiksa. Yang kedua: review yang terlalu ketat di hal yang salah — banyak energi dihabiskan untuk mendebat naming convention dan formatting sementara bug logika dan celah keamanan lolos tanpa perhatian.

Checklist adalah solusi untuk kedua masalah itu. Bukan karena checklist membuat review menjadi mekanis — justru sebaliknya. Checklist yang baik membebaskan reviewer dari kekhawatiran “apakah ada yang terlewat?” sehingga mereka bisa berfokus pada berpikir, bukan mengingat. Ia memastikan standar yang sama diterapkan terlepas dari siapa reviewernya, seberapa sibuk harinya, atau seberapa familiar reviewer dengan area kode yang sedang direview.

Yang penting dipahami: checklist adalah panduan berpikir, bukan daftar kewajiban yang harus dicentang semua. Tidak semua item relevan untuk semua PR. Tugas reviewer adalah menggunakan checklist untuk menentukan item mana yang perlu diperiksa mendalam untuk PR yang ada di depannya — bukan mencentang semua item secara mekanis.

Cara Menggunakan Checklist Ini #

Checklist ini dibagi menjadi dua perspektif: author (sebelum request review) dan reviewer (saat melakukan review). Keduanya penting — review yang baik adalah tanggung jawab bersama.

flowchart LR
    subgraph Author["Author — sebelum request review"]
        A1[Checklist PR Readiness]
        A2[Checklist Self-Review]
    end

    subgraph Reviewer["Reviewer — saat review"]
        R1[Functional & Business Logic]
        R2[Code Quality]
        R3[Architecture & Design]
        R4[Error Handling]
        R5[Security]
        R6[Performance]
        R7[Testing]
        R8[Ops & Observability]
    end

    Author --> |PR siap| Reviewer
    Reviewer --> |Semua area diperiksa| Decision{Keputusan}
    Decision --> |Tidak ada blocking| Approve
    Decision --> |Ada blocking issue| RequestChanges[Request Changes]

Perspektif Author: Sebelum Request Review #

Sebelum menambahkan reviewer, author adalah reviewer pertama untuk PR-nya sendiri. Checklist ini membantu memastikan PR sudah dalam kondisi yang layak direview — bukan menghabiskan waktu reviewer untuk masalah yang seharusnya bisa ditemukan sendiri.

Checklist PR Readiness #

PR READINESS — kondisi dasar yang harus terpenuhi:

  □ CI sudah hijau — linting, test, dan build semuanya lulus
    ✗ "Nanti perbaiki setelah review" — CI merah berarti PR belum siap
    ✓ Semua pipeline hijau sebelum request review

  □ Judul PR menggunakan format yang konsisten (Conventional Commits)
    ✗ "Update code" / "Fix stuff" / "WIP"
    ✓ "feat(auth): add rate limiting to login endpoint"

  □ Deskripsi PR sudah diisi — latar belakang, perubahan, cara testing
    ✗ Deskripsi kosong atau hanya "see commit messages"
    ✓ Menjelaskan konteks, keputusan teknis, dan cara verifikasi

  □ PR punya satu tujuan yang bisa diringkas dalam satu kalimat
    ✗ PR berisi refactor + feature + bug fix sekaligus
    ✓ "PR ini menambahkan rate limiting ke endpoint login"

  □ Ukuran PR reasonable — tidak melebihi kemampuan review satu sesi
    ✗ PR 1500+ baris tanpa alasan yang kuat
    ✓ Sudah dipertimbangkan apakah bisa dipecah menjadi lebih kecil

Checklist Self-Review #

SELF-REVIEW — baca seluruh diff dari perspektif reviewer:

  □ Tidak ada kode debug yang tertinggal
    ✗ console.log("debug"), fmt.Println("test"), print("DEBUG HERE")
    ✓ Semua output debug sudah dihapus

  □ Tidak ada TODO tanpa nomor tiket
    ✗ // TODO: fix this later
    ✓ // TODO(JIRA-456): optimize this query after load test

  □ Tidak ada kode yang di-comment out tanpa alasan
    ✗ // old_function(x)  — kode yang sudah tidak dipakai
    ✓ Hapus saja — git history ada jika suatu hari perlu kembali

  □ Tidak ada file yang tidak sengaja ter-include
    ✗ .env, credentials, file binary, file yang tidak terkait PR
    ✓ git diff menunjukkan hanya perubahan yang memang disengaja

  □ Semua edge case yang terpikirkan sudah ditangani atau di-comment
  □ Test sudah berjalan lokal dan hasilnya hijau

Perspektif Reviewer: Delapan Area Review #

1. Functional & Business Logic #

Ini adalah area terpenting dan paling sering menjadi sumber bug yang lolos ke produksi. Kode yang syntax-nya sempurna bisa tetap salah jika asumsi bisnisnya keliru.

FUNCTIONAL & BUSINESS LOGIC:

  Correctness:
  □ Apakah perubahan ini melakukan apa yang dideskripsikan di PR?
  □ Apakah logika sudah benar untuk semua kasus yang mungkin terjadi?
  □ Apakah ada asumsi implisit yang tidak terdokumentasi?
    Anti-pattern: fungsi yang mengasumsikan input tidak pernah null
    tanpa ada dokumentasi atau assertion yang memastikan itu

  Edge Case:
  □ Apakah behavior sudah benar untuk input kosong/null/zero?
  □ Apakah concurrent access ke resource yang sama sudah aman?
  □ Apakah race condition mungkin terjadi di antara dua operasi?
    Anti-pattern: SELECT → cek kondisi → UPDATE tanpa locking
    (bisa terjadi lost update jika dua request masuk bersamaan)

  Backward Compatibility:
  □ Apakah behavior yang sudah ada dan diandalkan tetap terjaga?
  □ Apakah ada breaking change yang tidak disebut di deskripsi PR?
  □ Apakah consumer lain dari fungsi/endpoint ini terdampak?

  Pertanyaan panduan untuk reviewer:
  "Jika saya seorang user yang menggunakan fitur ini dengan cara yang
   tidak biasa, apa yang bisa saya lakukan untuk membuatnya gagal?"

2. Readability & Maintainability #

Kode dibaca jauh lebih sering daripada ditulis. Engineer yang menulis kode ini mungkin tidak akan ada di tim satu atau dua tahun dari sekarang — kode harus bisa dipahami oleh orang yang tidak tahu konteksnya.

READABILITY & MAINTAINABILITY:

  Naming:
  □ Apakah nama variabel, fungsi, dan class mendeskripsikan maksudnya?
    Anti-pattern: d, tmp, data, result, helper — nama yang tidak memberi informasi
    ✓ userRegistrationDeadline, paymentGatewayTimeout, isEligibleForDiscount

  □ Apakah naming konsisten dengan konvensi yang sudah ada di codebase?
    Anti-pattern: mix antara camelCase dan snake_case di bahasa yang sama
    Anti-pattern: getUserData() di satu tempat, fetchUserInfo() di tempat lain
    untuk operasi yang identik

  Fungsi dan Method:
  □ Apakah setiap fungsi melakukan satu hal saja (SRP)?
    Anti-pattern: fungsi 200 baris yang melakukan validasi, kalkulasi,
    database operation, dan mengirim email sekaligus
    ✓ Pecah menjadi fungsi-fungsi yang lebih kecil dengan nama yang jelas

  □ Apakah ada logika yang duplikat yang seharusnya di-extract?
    Anti-pattern: blok validasi yang sama muncul di 3 endpoint berbeda
    ✓ Extract ke fungsi validateInput() yang bisa digunakan bersama

  Komentar:
  □ Apakah komentar menjelaskan MENGAPA, bukan APA?
    Anti-pattern: // increment counter by 1 — kode sudah menjelaskan ini
    ✓ // Using exponential backoff to prevent thundering herd
       // after gateway recovery (see RFC-123)

3. Architecture & Design #

Kualitas arsitektur dari sebuah PR tidak selalu terlihat dari diff-nya. Diperlukan pemahaman tentang bagaimana sistem bekerja secara keseluruhan untuk menilai apakah sebuah perubahan konsisten dengan arah yang sudah disepakati.

ARCHITECTURE & DESIGN:

  Konsistensi Pola:
  □ Apakah perubahan mengikuti pola arsitektur yang sudah disepakati tim?
    Anti-pattern: menambahkan business logic langsung di controller
    ketika tim sudah sepakat semua logic ada di service layer

  □ Apakah layer boundary dijaga dengan benar?
    Anti-pattern: repository layer yang tahu tentang HTTP request/response
    Anti-pattern: controller yang langsung query database

  Dependency:
  □ Apakah dependency baru benar-benar diperlukan?
    Anti-pattern: menambahkan library 50KB hanya untuk satu fungsi utility
    yang bisa ditulis sendiri dalam 10 baris

  □ Apakah perubahan ini membuat coupling yang lebih ketat dari sebelumnya?
    Anti-pattern: service A yang sebelumnya independen kini langsung
    mengimport dan memanggil service B — coupling yang tidak perlu

  Extensibility:
  □ Apakah desain ini akan menyulitkan perubahan di masa depan?
    (tapi jangan over-engineer untuk kasus yang belum ada — YAGNI)
  □ Apakah ada hardcoded value yang seharusnya bisa dikonfigurasi?
    Anti-pattern: timeout dikodekan sebagai 5000ms di dalam fungsi
    ✓ Baca dari konfigurasi atau terima sebagai parameter

4. Error Handling & Reliability #

Error handling yang buruk adalah salah satu sumber insiden produksi yang paling umum. Kode yang “berjalan” dalam kondisi normal bisa gagal secara tidak terduga ketika terjadi error di luar happy path.

ERROR HANDLING & RELIABILITY:

  Penanganan Error:
  □ Apakah semua operasi yang bisa gagal sudah ditangani error-nya?
    Anti-pattern (Go): mengabaikan nilai return error
    result, _ := doSomething()  // ← error diabaikan
    ✓ result, err := doSomething(); if err != nil { ... }

  □ Apakah error message cukup informatif untuk debugging?
    Anti-pattern: return errors.New("error")
    ✓ return fmt.Errorf("failed to process payment for order %d: %w", orderID, err)

  □ Apakah error di-log di level yang tepat?
    Anti-pattern: log.Error untuk sesuatu yang expected (user not found)
    Anti-pattern: log.Info untuk sesuatu yang butuh perhatian segera

  Resilience:
  □ Apakah failure di satu komponen bisa menyebabkan cascading failure?
    Anti-pattern: tidak ada timeout pada HTTP call ke service eksternal
    → satu service lambat bisa membuat seluruh request chain hang

  □ Apakah ada mekanisme retry yang diperlukan?
    Anti-pattern: gagal sekali → langsung return error tanpa retry
    pada operasi yang transient error-nya lumrah (network timeout)

  □ Apakah resource (connection, file, lock) selalu di-release?
    Anti-pattern: resource leak karena tidak ada defer/finally untuk cleanup
    ✓ defer conn.Close() langsung setelah connection dibuka
Error yang di-swallow (ditangkap tapi tidak dilakukan apapun) adalah salah satu bug yang paling sulit ditemukan di produksi. Pastikan tidak ada catch (e) {} atau if err != nil { return } tanpa logging atau proper handling. Jika memang sengaja di-ignore, tambahkan komentar eksplisit yang menjelaskan mengapa.

5. Security & Data Safety #

Celah keamanan hampir selalu lebih mudah dicegah di code review daripada diperbaiki setelah production. Satu kali melewatkan SQL injection atau improper authorization bisa menjadi insiden keamanan yang mempengaruhi seluruh pengguna.

SECURITY & DATA SAFETY:

  Input Validation:
  □ Apakah semua input dari user/external sudah divalidasi?
    Anti-pattern: menggunakan input langsung tanpa sanitasi
    ✓ Validasi tipe, format, panjang, dan range sebelum diproses

  □ Apakah ada potensi injection attack?
    Anti-pattern SQL: "SELECT * FROM users WHERE email = '" + email + "'"
    ✓ Parameterized query: "SELECT * FROM users WHERE email = ?" + [email]

    Anti-pattern XSS: innerHTML = userInput
    ✓ textContent = userInput, atau sanitasi dengan library yang tepat

  Data Sensitif:
  □ Apakah credential, token, atau PII tidak ter-log atau ter-expose?
    Anti-pattern: log.Info("User login: email=%s, password=%s", email, pass)
    Anti-pattern: mengembalikan seluruh user object (termasuk password hash)
    di API response

  □ Apakah data sensitif di-handle sesuai regulasi (GDPR, dsb)?
    Anti-pattern: menyimpan nomor kartu kredit dalam plaintext
    Anti-pattern: log yang menyimpan PII tanpa retention policy yang jelas

  Authorization:
  □ Apakah authorization dicek di tempat yang tepat?
    Anti-pattern: authorization hanya di UI, tidak di backend
    Anti-pattern: mengasumsikan bahwa jika user bisa login, ia bisa
    mengakses semua resource

  □ Apakah ada Insecure Direct Object Reference (IDOR)?
    Anti-pattern: GET /orders/{id} tanpa memverifikasi bahwa order tersebut
    milik user yang sedang login
    ✓ Selalu verifikasi ownership sebelum memberikan akses ke resource

6. Performance & Scalability #

Bug performa sering tersembunyi di dalam kode yang tampak bersih dan berjalan baik. Mereka baru terlihat ketika data bertumbuh atau traffic meningkat — seringkali pada waktu yang paling tidak tepat.

PERFORMANCE & SCALABILITY:

  Database Query:
  □ Apakah ada N+1 query problem?
    Anti-pattern: query di dalam loop
    for user in users:
        orders = db.query("SELECT * FROM orders WHERE user_id = ?", user.id)
    ✓ Gunakan JOIN atau batch loading:
    orders = db.query("SELECT * FROM orders WHERE user_id IN (?)", user_ids)

  □ Apakah query menggunakan index yang ada?
    Anti-pattern: WHERE LOWER(email) = ? — fungsi pada kolom membuat index tidak terpakai
    ✓ WHERE email = LOWER(?) — fungsi pada nilai, bukan kolom

  □ Apakah SELECT hanya mengambil kolom yang diperlukan?
    Anti-pattern: SELECT * ketika hanya butuh 3 kolom dari 30 kolom
    ✓ SELECT id, name, email FROM users

  Kompleksitas Algoritma:
  □ Apakah ada operasi O(n²) atau lebih buruk yang bisa dihindari?
    Anti-pattern: nested loop yang bertumbuh quadratic dengan jumlah data
    ✓ Gunakan hash map atau sorted list untuk mengurangi kompleksitas

  □ Apakah ada operasi mahal di dalam hot path (request handler, loop)?
    Anti-pattern: membaca konfigurasi dari disk setiap request
    ✓ Load konfigurasi sekali saat startup, cache di memory

  Resource:
  □ Apakah ada potensi memory leak?
    Anti-pattern: event listener yang ditambahkan tapi tidak pernah dihapus
  □ Apakah perlu caching untuk mengurangi beban operasi yang sering diulang?

7. Testing & Validation #

Test adalah dokumentasi yang executable. Test yang baik menjelaskan apa yang seharusnya dilakukan kode, membuktikan ia melakukannya, dan menjadi guard yang mencegah regresi di masa depan.

TESTING & VALIDATION:

  Coverage:
  □ Apakah ada test untuk logika baru yang ditambahkan?
    Anti-pattern: PR yang menambahkan 200 baris logic tanpa satu pun test baru
    ✓ Setiap path yang penting ada test yang memverifikasinya

  □ Apakah test mencakup happy path DAN edge case?
    Anti-pattern: hanya test "user berhasil login" tanpa test "user tidak ada",
    "password salah", "akun terkunci", dsb
    ✓ Test mencakup semua skenario yang relevan secara bisnis

  Kualitas Test:
  □ Apakah test benar-benar bisa gagal jika logic rusak?
    Anti-pattern: test yang hanya mengecek bahwa fungsi berjalan tanpa error
    tanpa memverifikasi output atau side effect-nya
    ✓ Assert nilai spesifik, bukan hanya "tidak ada exception"

  □ Apakah test mudah dipahami — jelas apa yang ditest dan apa yang diharapkan?
    Anti-pattern: test dengan setup 100 baris tanpa komentar tentang apa
    yang sedang ditest
    ✓ Nama test yang deskriptif + AAA pattern (Arrange, Act, Assert)
    TestProcessPayment_WhenBalanceSufficient_ShouldDeductAndReturnSuccess

  □ Apakah test tidak tergantung pada urutan eksekusi atau state global?
    Anti-pattern: test B gagal jika test A tidak dijalankan dulu
    ✓ Setiap test bisa berjalan sendiri dan menghasilkan hasil yang sama

8. Ops & Observability #

Kode yang berjalan di produksi perlu bisa di-observe, di-debug, dan di-deploy dengan aman. Masalah yang tidak bisa dideteksi atau di-debug adalah masalah yang tidak bisa diperbaiki.

OPS & OBSERVABILITY:

  Logging:
  □ Apakah log yang ditambahkan cukup untuk debugging di produksi?
    Anti-pattern: tidak ada log sama sekali pada operasi kritis
    Anti-pattern: log yang terlalu verbose di hot path (memenuhi disk)
    ✓ Log yang useful: "Payment processed: order_id=%d, amount=%d, duration=%dms"

  □ Apakah log menggunakan level yang tepat?
    DEBUG: informasi detail untuk pengembangan
    INFO:  kejadian normal yang perlu dicatat
    WARN:  sesuatu tidak ideal tapi sistem masih berjalan
    ERROR: sesuatu gagal dan perlu perhatian

  Deployment:
  □ Apakah ada migration database yang perlu dijalankan?
    Jika ya: apakah migration bisa di-rollback? Apakah backward compatible?
  □ Apakah ada environment variable atau konfigurasi baru yang diperlukan?
  □ Apakah perubahan ini aman jika perlu di-rollback?
    Anti-pattern: schema change yang tidak backward compatible dengan kode lama

  Feature Flag:
  □ Apakah fitur yang berisiko tinggi perlu dibungkus feature flag?
    ✓ Feature flag memungkinkan rollout bertahap dan rollback instan tanpa redeploy

Template Checklist Siap Pakai #

Ini adalah versi ringkas yang bisa langsung dimasukkan ke PR template tim. Sesuaikan dengan konteks dan stack teknologi yang digunakan.

## Code Review Checklist

### Author (sebelum request review)
- [ ] CI hijau (linting, test, build)
- [ ] Deskripsi PR lengkap (latar belakang, perubahan, cara testing)
- [ ] PR fokus pada satu tujuan
- [ ] Self-review: tidak ada debug log, TODO tanpa tiket, kode ter-comment

### Reviewer — Functional
- [ ] Logic sudah benar untuk semua kasus yang relevan
- [ ] Edge case (null, empty, concurrent) sudah ditangani
- [ ] Tidak ada asumsi implisit yang berbahaya

### Reviewer — Code Quality
- [ ] Naming ekspresif dan konsisten dengan codebase
- [ ] Tidak ada duplikasi logika yang tidak perlu
- [ ] Komentar menjelaskan WHY, bukan WHAT

### Reviewer — Architecture
- [ ] Mengikuti pola arsitektur yang sudah disepakati
- [ ] Layer boundary terjaga
- [ ] Dependency baru justified

### Reviewer — Error Handling
- [ ] Semua error path ditangani dengan tepat
- [ ] Error message informatif untuk debugging
- [ ] Resource selalu di-release (connection, file, lock)

### Reviewer — Security
- [ ] Input user divalidasi sebelum diproses
- [ ] Tidak ada injection vulnerability
- [ ] Data sensitif tidak ter-log atau ter-expose
- [ ] Authorization dicek di tempat yang tepat

### Reviewer — Performance
- [ ] Tidak ada N+1 query
- [ ] Query menggunakan index yang ada
- [ ] Tidak ada operasi mahal di dalam hot path

### Reviewer — Testing
- [ ] Test ada untuk logika baru
- [ ] Test mencakup edge case yang relevan
- [ ] Test bisa gagal jika logic rusak

### Reviewer — Ops
- [ ] Log yang ditambahkan useful untuk debugging
- [ ] Migration dan deployment notes sudah ada (jika perlu)
- [ ] Perubahan aman untuk di-rollback

Cara Mengadaptasi Checklist ke Konteks Tim #

Checklist di atas adalah checklist generik. Tim yang berbeda memiliki kebutuhan yang berbeda — backend, frontend, mobile, data engineering masing-masing punya concern yang unik. Checklist yang paling efektif adalah yang sudah disesuaikan dengan realita tim.

Panduan adaptasi checklist:

  Tambahkan item spesifik stack:
  → Backend Go: "Apakah goroutine bisa leak? Apakah context sudah di-propagate?"
  → Frontend React: "Apakah ada unnecessary re-render? Apakah accessibility sudah dipertimbangkan?"
  → Mobile: "Apakah UI tetap responsif? Apakah battery usage dipertimbangkan?"
  → Database migration: "Apakah migration bisa dijalankan di produksi tanpa downtime?"

  Hapus item yang tidak relevan:
  → Tim dengan linter ketat tidak perlu reviewer mengomentari formatting
  → Tim yang semua API-nya read-only tidak butuh checklist SQL injection

  Review dan update secara berkala:
  → Setelah setiap insiden produksi, tanya: "Apakah ada item checklist yang
    bisa mencegah ini?" Jika ya, tambahkan.
  → Setiap kuartal, review apakah ada item yang tidak pernah relevan
    dan bisa dihapus.

  Pisahkan checklist inti dari checklist kontekstual:
  → Checklist inti: selalu digunakan untuk semua PR
  → Checklist khusus: hanya untuk PR yang menyentuh area tertentu
    (payment, authentication, data migration, dsb)
Checklist yang terlalu panjang cenderung diabaikan — reviewer melewatinya secara mekanis tanpa benar-benar memikirkan setiap item. Lebih baik checklist yang lebih pendek tapi benar-benar dipikirkan daripada checklist 50 item yang dicentang semua dalam 30 detik. Mulai dengan checklist inti yang pendek dan tambahkan item hanya ketika ada alasan yang jelas.

Ringkasan #

  • Checklist adalah panduan berpikir, bukan daftar kewajiban — tidak semua item relevan untuk semua PR. Gunakan checklist untuk menentukan area mana yang perlu diperiksa mendalam, bukan untuk dicentang secara mekanis.
  • Delapan area review yang kritis — Functional & Business Logic, Readability, Architecture, Error Handling, Security, Performance, Testing, dan Ops & Observability. Masing-masing punya jenis masalah yang berbeda dan membutuhkan lensa yang berbeda.
  • Author adalah reviewer pertama — checklist self-review sebelum request review menghilangkan masalah yang seharusnya tidak perlu dilihat orang lain: debug log, TODO tanpa tiket, CI merah.
  • Functional & Business Logic adalah area terpenting — bug logika yang berbahaya sering tidak terlihat dari syntax, tapi dari asumsi bisnis yang salah atau edge case yang tidak ditangani.
  • Security tidak boleh ditunda — celah keamanan jauh lebih mudah dicegah di code review daripada diperbaiki setelah insiden. Perhatikan injection, authorization, dan data sensitif di setiap PR.
  • Error handling yang buruk adalah sumber insiden — error yang di-swallow, resource yang tidak di-release, dan tidak ada timeout pada external call adalah pola yang perlu selalu diwaspadai.
  • N+1 query adalah bug performa yang paling umum — mudah terlewat tapi dampaknya besar saat data bertumbuh. Selalu periksa apakah ada query di dalam loop.
  • Test yang bisa gagal lebih berharga dari test yang selalu lulus — verifikasi bahwa test benar-benar menguji behavior yang penting, bukan hanya memastikan fungsi berjalan tanpa error.
  • Observability adalah bagian dari kode yang baik — log yang informatif, metric yang tepat, dan deployment notes yang jelas adalah bagian dari tanggung jawab engineer, bukan afterthought.
  • Sesuaikan checklist dengan konteks tim — checklist generik adalah titik awal. Tambahkan item setelah setiap insiden, hapus item yang tidak pernah relevan, dan pisahkan checklist inti dari checklist kontekstual.

← Sebelumnya: Code Review Meeting   Berikutnya: Code Review Ethics →

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