Small vs Big Pull Request #

Ada anggapan yang sangat umum di tim engineering: semakin banyak kode yang diselesaikan dalam satu PR, semakin produktif developer-nya. Logikanya terasa masuk akal — lebih banyak pekerjaan terselesaikan, lebih sedikit overhead koordinasi. Tapi anggapan ini keliru secara fundamental, dan dampaknya nyata: review yang tidak efektif, bug yang lolos, bottleneck yang melambat, dan frustrasi yang terakumulasi di kedua sisi — author maupun reviewer.

Ukuran PR bukan soal preferensi personal atau gaya kerja. Ia adalah keputusan yang berdampak langsung pada kualitas review, kecepatan delivery, kemudahan rollback, dan kesehatan kolaborasi tim. Artikel ini membahas mengapa PR kecil hampir selalu lebih baik, kapan PR besar memang tidak terhindarkan, dan strategi konkret untuk memecah pekerjaan besar menjadi PR yang bisa direview dengan efektif.

Apa yang Sebenarnya Terjadi saat Reviewer Membaca PR Besar #

Sebelum membahas strategi, perlu dipahami dulu apa yang terjadi di sisi reviewer ketika menghadapi PR besar. Ini bukan soal malas atau tidak profesional — ini soal bagaimana kognisi manusia bekerja.

Saat membaca PR dengan 50 baris perubahan, reviewer bisa membangun model mental yang lengkap tentang apa yang berubah, mengapa, dan apa dampaknya. Mereka bisa mendeteksi asumsi yang salah, edge case yang terlewat, atau pendekatan yang tidak konsisten dengan pola yang ada di sistem.

Saat membaca PR dengan 800 baris perubahan di 15 file, sesuatu yang berbeda terjadi:

Efek kognitif pada reviewer seiring ukuran PR bertambah:

  PR 50 baris    → Working memory cukup → model mental lengkap
                   Reviewer menemukan: bug logika, edge case, design issue

  PR 200 baris   → Working memory mulai terbatas → model mental parsial
                   Reviewer menemukan: bug yang jelas, masalah lokal
                   Mulai melewatkan: konsistensi arsitektur, interaksi antar komponen

  PR 500 baris   → Working memory kewalahan → review terfragmentasi
                   Reviewer menemukan: masalah yang sangat jelas saja
                   Melewatkan: bug halus, asumsi implisit, dampak tidak langsung

  PR 1000+ baris → Fatigue → approval tanpa pemahaman penuh
                   "LGTM" karena tidak mungkin memahami seluruhnya
                   PR masuk ke main dengan review yang hampir tidak bermakna

Ini bukan kelemahan reviewer — ini adalah batas kapasitas kognitif manusia yang tidak bisa dihindari. PR yang besar secara struktural mencegah review yang bermakna.


Dampak Ukuran PR terhadap Seluruh Proses #

Ukuran PR bukan hanya mempengaruhi kualitas review. Ia mempengaruhi hampir setiap aspek dari siklus development.

flowchart LR
    A[Ukuran PR] --> B[Waktu Review]
    A --> C[Kualitas Review]
    A --> D[Risiko saat Merge]
    A --> E[Kemudahan Rollback]
    A --> F[Merge Conflict]

    B --> |PR besar = lebih lama| G[Delivery Lambat]
    C --> |PR besar = lebih lemah| H[Bug Lolos ke Production]
    D --> |PR besar = risiko lebih tinggi| I[Incident Production]
    E --> |PR besar = sulit| J[Recovery Lambat]
    F --> |PR besar = lebih banyak| K[Rework Tinggi]

Waktu Review #

PR besar membutuhkan waktu review yang lebih lama — dan waktu itu tidak bertambah secara linear. Di atas 400 baris perubahan, efektivitas review menurun drastis sementara waktu yang dibutuhkan terus bertambah.

Hubungan ukuran PR dengan waktu dan kualitas review:

  < 100 baris   → Review selesai 15–30 menit. Komentar berkualitas tinggi.
  100–300 baris → Review selesai 30–60 menit. Komentar masih fokus.
  300–500 baris → Review butuh 1–2 jam. Energi mulai habis di tengah jalan.
  500–800 baris → Review kerap ditunda ("nanti kalau ada waktu lebih").
  > 800 baris   → Review berakhir LGTM tanpa benar-benar dibaca,
                  atau PR menganggur berhari-hari karena tidak ada yang mau mulai.

Feedback Loop #

PR kecil memperpendek feedback loop — waktu antara “kode selesai ditulis” dan “kode di-merge ke main”. PR besar memperpanjangnya, dan setiap hari yang berlalu membuat konteks makin basi.

gantt
    title Perbandingan Feedback Loop: Small PR vs Big PR
    dateFormat HH:mm
    axisFormat %H:%M

    section Small PR
    Tulis kode      :a1, 00:00, 2h
    Review          :a2, after a1, 1h
    Revisi          :a3, after a2, 30m

    section Big PR
    Tulis kode      :b1, 00:00, 8h
    Menunggu reviewer :b2, after b1, 4h
    Review sesi 1   :b3, after b2, 2h
    Revisi besar    :b4, after b3, 3h
    Review sesi 2   :b5, after b4, 2h
    Revisi minor    :b6, after b5, 1h

Merge Conflict #

PR yang lama tidak di-merge terus bergerak menjauhi main. Semakin lama PR terbuka, semakin besar kemungkinan terjadi merge conflict dengan PR lain yang sudah di-merge duluan. Merge conflict berarti rework — dan rework berarti waktu yang terbuang.

Rollback #

Ketika sebuah perubahan menyebabkan masalah di production, kemampuan rollback yang cepat adalah perbedaan antara incident kecil dan incident besar. PR kecil yang terfokus mudah di-revert: satu PR, satu tujuan, satu revert. PR besar yang mencampur beberapa perubahan tidak bisa di-revert sebagian — semua atau tidak sama sekali.


Definisi “Kecil”: Bukan Soal Angka #

“Kecil” dalam konteks PR bukan threshold absolut — bukan “di bawah 300 baris” atau “di bawah 5 file”. Definisi yang lebih berguna adalah: apakah satu reviewer bisa membangun model mental yang lengkap tentang PR ini dalam satu sesi review tanpa kehilangan konteks?

Faktor yang membuat PR terasa lebih BESAR dari ukurannya:
  → Perubahan di domain yang tidak familiar bagi reviewer
    (300 baris di area yang reviewer tidak pernah sentuh = terasa seperti 1000 baris)
  → Banyak file yang saling bergantung dengan cara yang tidak jelas
  → Tidak ada deskripsi yang membantu reviewer membangun konteks
  → Campuran refactor dan behavior change — reviewer tidak tahu mana yang prioritas

Faktor yang membuat PR terasa lebih KECIL dari ukurannya:
  → Deskripsi yang sangat baik — reviewer sudah punya model mental sebelum baca diff
  → Perubahan di satu domain yang reviewer sudah familiar
  → Commit yang terstruktur baik — bisa direview commit per commit
  → Perubahan yang sangat lokal tanpa side effect ke komponen lain

Kapan Big PR Memang Tidak Terhindarkan #

Ada situasi di mana PR besar bukan karena kurangnya disiplin, tapi karena sifat perubahan itu sendiri yang tidak bisa dipecah tanpa kehilangan koherensi.

Situasi di mana big PR legitimate:

  1. Migrasi framework atau major refactor
     → Perubahan yang saling bergantung satu sama lain
     → Memecah bisa meninggalkan codebase dalam state yang tidak konsisten

  2. Penambahan fitur yang atomic
     → Fitur yang hanya bermakna ketika semua komponen sudah ada
     → Contoh: sistem pembayaran baru yang melibatkan gateway, webhook, dan UI sekaligus

  3. Perubahan schema database yang berdampak luas
     → Schema change + migration + update semua query yang terdampak

  4. Security patch yang butuh perubahan di banyak tempat
     → Tidak bisa di-merge setengah-setengah karena security hole tetap ada

  5. Generated code atau automated changes
     → Perubahan yang di-generate oleh tool (protobuf, OpenAPI, dsb)
     → Ukurannya besar tapi tidak perlu direview baris per baris
Big PR yang legitimate tetap membutuhkan persiapan ekstra: deskripsi yang lebih detail, panduan review yang jelas (bagian mana yang krusial, bagian mana yang generated/boilerplate), dan kemungkinan review session bersama daripada async review. “Big PR tidak bisa dihindari” bukan berarti “big PR boleh dibuka tanpa persiapan”.

Strategi Memecah PR Besar #

Hambatan terbesar untuk membuat PR kecil bukan teknis — ini mental. “Kalau fiturnya belum lengkap, bagaimana bisa di-merge?” Ini pertanyaan yang valid, dan ada beberapa strategi konkret untuk menjawabnya.

Strategi 1: Pisahkan Refactor dari Feature #

Ini adalah pemisahan paling fundamental dan paling sering diabaikan. Refactor dan behavior change adalah dua jenis perubahan yang berbeda dan harus direview dengan cara yang berbeda.

flowchart TD
    A[Feature baru yang membutuhkan refactor] --> B[PR 1: Refactor murni]
    B --> C{Behavior berubah?}
    C -- Tidak --> D[Merge ✓]
    D --> E[PR 2: Feature baru]
    E --> F[Merge ✓]
    C -- Ya --> G[Pisah lagi sampai murni]
Contoh pemisahan yang benar:

  Task: Tambahkan fitur export laporan ke CSV
  (Butuh refactor service layer terlebih dahulu)

  ✗ Cara yang salah — satu PR besar:
    PR: "Add CSV export feature"
    - Refactor ReportService dari class ke function
    - Tambah method exportToCSV()
    - Tambah endpoint /reports/export
    - Tambah test untuk semua

  ✓ Cara yang benar — dua PR kecil:
    PR 1: "refactor(report): convert ReportService to functional style"
    - Refactor murni, tidak ada behavior change
    - Test yang ada tetap lulus — ini jaminan bahwa refactor aman
    - Reviewer bisa fokus: "apakah refactor ini benar tanpa mengubah behavior?"

    PR 2: "feat(report): add CSV export endpoint"
    - Menambahkan method dan endpoint baru di atas pondasi yang bersih
    - Reviewer bisa fokus: "apakah implementasinya benar?"

Strategi 2: Feature Flag untuk Merge Inkremental #

Feature flag memungkinkan kode di-merge ke main sebelum fitur siap diaktifkan untuk user. Ini adalah salah satu teknik paling powerful untuk menghindari long-lived branch dan PR besar.

Alur development dengan feature flag:

  Week 1 — PR 1: Infrastructure
  ├── Tambah database schema baru (migration)
  ├── Tambah repository layer
  └── Feature flag: ENABLE_NEW_PAYMENT = false (belum aktif)
  → Merge ke main, tidak ada impact ke user

  Week 2 — PR 2: Core Logic
  ├── Implementasi payment service
  ├── Unit test untuk service
  └── Masih di balik feature flag
  → Merge ke main, tidak ada impact ke user

  Week 3 — PR 3: API Layer
  ├── Tambah endpoint baru
  ├── Integration test
  └── Masih di balik feature flag
  → Merge ke main, QA testing di staging dengan flag aktif

  Week 4 — PR 4: Activation
  ├── Aktifkan feature flag untuk 10% user (canary release)
  └── Monitor metric
  → Rollout bertahap, rollback cukup matikan flag

  Hasilnya:
  → Tidak ada PR yang lebih dari 300–400 baris
  → Setiap PR bisa direview dengan fokus
  → Main branch selalu dalam state yang bisa di-deploy
  → Rollback semudah mematikan feature flag

Strategi 3: Stacked PR (PR Berantai) #

Stacked PR adalah teknik di mana satu PR bergantung pada PR sebelumnya — PR 2 di-branch dari PR 1, bukan dari main. Ini memungkinkan development yang linear pada fitur yang memiliki dependensi sequential.

gitGraph
    commit id: "main"
    branch pr-1-schema
    checkout pr-1-schema
    commit id: "add users schema"
    commit id: "add migration"
    checkout main
    merge pr-1-schema id: "Merge PR 1"
    branch pr-2-service
    checkout pr-2-service
    commit id: "add UserService"
    commit id: "add unit tests"
    checkout main
    merge pr-2-service id: "Merge PR 2"
    branch pr-3-api
    checkout pr-3-api
    commit id: "add /users endpoint"
    commit id: "add integration tests"
    checkout main
    merge pr-3-api id: "Merge PR 3"
Cara kerja stacked PR:

  PR 1: "feat(auth): add users and sessions schema"
  → Branch dari main
  → Berisi: migration, schema definition saja

  PR 2: "feat(auth): implement UserService"
  → Branch dari branch PR 1 (sebelum PR 1 di-merge)
  → Reviewer bisa me-review PR 2 paralel dengan PR 1
  → Setelah PR 1 di-merge, rebase PR 2 ke main

  PR 3: "feat(auth): add user registration endpoint"
  → Branch dari branch PR 2
  → Reviewer hanya melihat diff PR 3 (bukan akumulasi PR 1 + PR 2)

  Catatan penting:
  → Jika PR 1 perlu perubahan besar, PR 2 dan PR 3 perlu di-rebase
  → Tools seperti ghstack (GitHub) atau Graphite memudahkan manajemen stacked PR
  → Cocok untuk tim yang sudah familiar dengan rebase workflow

Strategi 4: Spike Branch untuk Eksplorasi #

Ketika scope pekerjaan belum jelas, membuat spike branch untuk eksplorasi sebelum commit ke PR yang sebenarnya adalah pilihan yang lebih baik daripada langsung membuat PR besar yang ternyata harus diubah total.

Spike → Draft PR → PR siap review:

  Spike branch:
  → Eksplorasi bebas, tidak perlu clean code
  → Tidak ada ekspektasi review
  → Tujuan: memahami masalah dan menemukan pendekatan terbaik

  Draft PR:
  → Setelah pendekatan ditemukan, buat PR dengan deskripsi
  → Buka sebagai Draft untuk mendapat feedback arah sebelum implementasi penuh
  → "Apakah pendekatan ini sudah benar sebelum saya lanjutkan?"

  PR siap review:
  → Implementasi bersih berdasarkan hasil spike dan feedback Draft PR
  → Sudah dipecah menjadi PR kecil karena scope sudah dipahami

Tanda-tanda PR Perlu Dipecah #

Sinyal bahwa PR perlu dipecah:

  ✗ Diff lebih dari 400 baris dan masih terus bertambah
  ✗ Judul PR tidak bisa diringkas dalam satu kalimat yang spesifik
  ✗ PR menyentuh lebih dari 2–3 domain yang berbeda
  ✗ Ada refactor dan feature change yang dicampur
  ✗ Butuh meeting khusus untuk menjelaskan PR ke reviewer
  ✗ Reviewer meminta clarification tentang konteks yang seharusnya ada di deskripsi
  ✗ PR sudah terbuka lebih dari 3 hari tanpa kemajuan review
  ✗ Ada komentar dari reviewer: "ini terlalu besar untuk direview sekarang"

Pertanyaan yang membantu memutuskan:
  → Bisakah bagian ini di-merge sendiri tanpa menunggu bagian lain?
  → Apakah reviewer perlu memahami seluruh PR untuk me-review satu bagian?
  → Jika PR ini di-revert, apakah semua perubahan harus ikut di-revert?

Perbandingan Lengkap: Small vs Big PR #

AspekSmall PRBig PR
Waktu review15–60 menit1–4+ jam (atau ditunda)
Kualitas reviewTinggi, fokusMenurun seiring ukuran
Feedback loopCepat (jam/hari)Lambat (hari/minggu)
Merge conflictJarangSering
RollbackMudah dan tepatSulit, semua atau tidak
Blast radius jika bugKecil, terlokalisasiBesar, menyebar
Dokumentasi historisJelas per perubahanKonteks bercampur
Motivasi reviewerTinggiRendah (lelah sebelum mulai)
Deployment riskRendahTinggi

Anti-Pattern yang Harus Dihindari #

// ✗ Anti-pattern 1: "Biar sekalian"
Menambahkan perubahan tidak terkait ke PR yang sedang berjalan
karena "sudah buka file ini anyway"
→ Setiap penambahan memperluas scope dan memperpanjang review
// ✓ Buat PR terpisah, bahkan untuk perubahan kecil yang tidak terkait

// ✗ Anti-pattern 2: Long-lived branch
Branch yang hidup berminggu-minggu tanpa di-merge karena "belum selesai"
→ Semakin lama branch hidup, semakin besar PR-nya, semakin banyak merge conflict
// ✓ Gunakan feature flag atau stacked PR untuk merge inkremental ke main

// ✗ Anti-pattern 3: Campur refactor dan behavior change
Reviewer tidak bisa memisahkan mana perubahan yang "harusnya tidak mengubah
behavior" dan mana yang "memang mengubah behavior"
→ Review jadi jauh lebih sulit dan error-prone
// ✓ PR 1 untuk refactor (test yang ada harus tetap lulus)
   PR 2 untuk behavior change atau feature baru

// ✗ Anti-pattern 4: "Nanti kalau sudah lengkap baru PR"
Menunggu fitur 100% selesai sebelum membuka PR
→ Hasilnya PR monster dengan ribuan baris yang tidak mungkin direview efektif
// ✓ PR adalah alat kolaborasi, bukan hanya alat merge
   Buka Draft PR lebih awal untuk mendapat feedback arah lebih cepat

// ✗ Anti-pattern 5: Approve PR besar karena kelelahan
Reviewer meng-approve PR 1000 baris bukan karena sudah review dengan baik,
tapi karena tidak sanggup lagi membacanya
→ False sense of security yang paling berbahaya
// ✓ Jika PR terlalu besar untuk direview dengan baik, katakan itu
   Minta author memecahnya daripada approve tanpa membaca

Checklist Keputusan Ukuran PR #

SEBELUM MEMBUKA PR:
  □ Apakah PR ini bisa diringkas dalam satu kalimat yang spesifik?
  □ Apakah ada refactor dan behavior change yang dicampur? → pisahkan
  □ Apakah PR menyentuh lebih dari 2–3 domain berbeda? → pertimbangkan pecah
  □ Apakah ada bagian yang bisa di-merge sendiri sebelum yang lain selesai?
  □ Jika > 400 baris: apakah sudah dipikirkan cara memecahnya?

SAAT PR SUDAH TERLALU BESAR DAN TIDAK BISA DIHINDARI:
  □ Identifikasi bagian yang paling independent untuk dipecah duluan
  □ Pisahkan refactor murni ke PR terpisah
  □ Pertimbangkan feature flag untuk merge inkremental
  □ Pertimbangkan stacked PR jika ada dependensi sequential
  □ Tambahkan panduan review di deskripsi: mana yang harus dibaca pertama,
    mana yang generated/boilerplate yang bisa di-skim

SEBAGAI REVIEWER YANG MENERIMA PR BESAR:
  □ Jangan approve hanya karena lelah membaca — ini false security
  □ Komunikasikan ke author: "PR ini terlalu besar untuk direview efektif"
  □ Bantu author mengidentifikasi cara memecahnya jika dia tidak tahu caranya
  □ Jika big PR legitimate, tandai bagian yang perlu perhatian ekstra
    vs bagian yang bisa di-skim (generated code, boilerplate)

Ringkasan #

  • Ukuran PR mempengaruhi kualitas review secara langsung — semakin besar PR, semakin menurun kemampuan reviewer membangun model mental yang lengkap. Ini keterbatasan kognitif manusia, bukan kelemahan reviewer.
  • Small PR bukan berarti lambat — justru sebaliknya. PR kecil yang cepat di-merge menghasilkan delivery lebih cepat dari PR besar yang menganggur berhari-hari menunggu review.
  • “Kecil” didefinisikan oleh kemampuan review, bukan angka absolut — pertanyaannya bukan “apakah kurang dari 300 baris?”, tapi “apakah reviewer bisa membangun model mental yang lengkap dalam satu sesi?”
  • Pisahkan refactor dari behavior change — ini pemisahan paling fundamental. Refactor dan feature adalah dua jenis review yang berbeda dan tidak bisa dilakukan bersamaan dengan efektif.
  • Feature flag memungkinkan merge sebelum fitur selesai — kode bisa di-merge ke main tanpa diaktifkan ke user, menghilangkan kebutuhan long-lived branch yang berujung PR besar.
  • Stacked PR untuk perubahan sequential — PR yang saling bergantung bisa dibuat dalam rantai, memungkinkan review paralel dan merge bertahap.
  • Long-lived branch adalah akar masalah — branch yang hidup berminggu-minggu menghasilkan PR monster dengan merge conflict dan konteks yang sudah basi.
  • Big PR yang legitimate tetap butuh persiapan ekstra — deskripsi lebih detail, panduan review yang jelas, dan kemungkinan review session sinkron.
  • Jangan approve PR besar karena kelelahan — ini false sense of security yang paling berbahaya. Lebih baik minta author memecahnya daripada approve tanpa membaca dengan benar.

← Sebelumnya: Anatomi   Berikutnya: One PR, One Purpose →

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