Free

3.000 Baris dalam Satu Commit: Mengapa Fitur Kompleks Butuh Plan Mode Dulu

Insting saat menghadapi fitur kompleks adalah "coba saja dulu" — tapi keputusan arsitektur bersembunyi di model ketiga, edge case kelima. Nilai sejati plan mode adalah memindahkan percakapan setingkat arsitektur ke teks sebelum kode. Kasus nyata: sistem juri komunitas Pickful — 3.032 baris, 119 spec hijau, satu commit, nol kerja ulang arsitektur setelahnya.


Di hadapan fitur kompleks, insting mayoritas orang adalah "coba dulu yang simpel".

Masalahnya: sistem kompleks punya satu karakteristik — keputusan arsitektur bersembunyi di model ke-3, di edge case ke-5, di aturan poin ke-8. Menceburkan diri ke dalam kode dan menabrak keputusan itu di tengah jalan, lalu kembali mengubahnya, harganya 10× lipat dibandingkan dibicarakan lebih dulu di level teks.

Saya memakai plan mode Claude Code untuk membangun TopicReview, sistem juri komunitas di Pickful, dan melihat versi ekstrem dari ketimpangan ini: satu commit, 3.032 baris, 119 spec hijau, dikirim sekali jalan. Tak ada satu pun dari belasan commit berikutnya yang merupakan kerja ulang arsitektur — semuanya penyetelan parameter, pemolesan UI, tambalan edge case. Sistem berjalan stabil sejak itu dan kini menjadi inti cara komunitas memoderasi dirinya sendiri.

Tulisan ini membahas mengapa fitur kompleks butuh plan mode dulu, apa yang sesungguhnya dilakukan fase plan, dan kapan percakapan cukup matang untuk mulai menulis kode.

Seberapa Kompleks Sistemnya

TopicReview adalah pemungutan suara komunitas untuk memutuskan apakah sebuah post berkualitas rendah dihapus. Satu kalimat untuk diutarakan — tapi spesifikasinya terbuka berlapis-lapis:

  • 5 status: open → voting → decided → appealed → closed
  • 3 putusan: remove / warn / keep
  • 2 tahap: peninjauan awal oleh 12 juri; banding oleh 5 hakim (diundi dari 20 pengguna dengan poin tertinggi)
  • Aliran poin multidimensi: penalti provisional 10 pt, stake banding 10 pt, +5 untuk juri yang memilih seiring putusan, +10 untuk hakim yang memilih seiring putusan, +3 untuk pelapor yang laporannya dibenarkan, refund saat menang banding = stake + bonus + penalti provisional
  • 4 jenis job terjadwal: jendela voting awal 24h, jendela banding 24h, jendela peninjauan banding 24h, dan — poin juri ditunda 24h setelah putusan remove (karena banding bisa membalikkan)
  • Aliran paralel + rollback: bila suara berbalik saat removal provisional, post harus dipulihkan dan penalti provisional dikembalikan; bila banding membatalkan putusan, stake dikembalikan plus bonus, mungkin bersama penalti provisional, dan juri dihitung ulang sesuai putusan baru

Inti kompleksitasnya tidak di satu aturan — melainkan di bagaimana aturan saling berinteraksi. Setiap aturan yang ditambah bisa memicu rollback di tempat lain.

Tembok yang Kau Tabrak Tanpa Plan

Saat menulis langsung, masalah tersulit bukan fakta yang kelihatan — tapi pertanyaan yang tak pernah terpikir untuk ditanyakan. Membaca kode TopicReview terbalik, setidaknya ada empat tembok yang pasti akan menabrakmu kalau plan dilewati:

Aturan kelayakan juri. Terlihat hanya seperti User.jurors_and_judges.sample(12). Namun aturan sebenarnya: kecualikan penulis post, kecualikan pelapor post, kecualikan yang sudah memilih di ronde awal (agar tidak memilih lagi di banding). Tiga pengecualian menumpuk. Menulis model sekaligus, kamu akan melewatkan satu-dua.

Pembayaran poin tertunda. Putusan remove biasanya memicu pembayaran ke juri. Tapi banding bisa membatalkan remove — dan ketika dibatalkan, juri yang memilih seiring putusan lama kini salah pihak, sehingga pembayaran harus dihitung ulang terhadap putusan baru. Maka putusan remove harus menunggu jendela banding 24h tutup dulu sebelum membayar juri. Lewati menulis aturan ini, bayar duluan, lalu kamu akan mencoba menarik kembali poin — 10× lebih berantakan daripada membayar terlambat.

Desain antarmuka job terjadwal. CloseTopicReviewJob terlihat seperti "mengakhiri sebuah review". Dalam praktiknya ia menangani tiga situasi:

# Jendela voting awal berakhir
CloseTopicReviewJob.set(wait_until: voting_ends_at).perform_later(review.id)
# Pembayaran juri tertunda setelah putusan remove
CloseTopicReviewJob.set(wait: 24.hours).perform_later(review.id, award_juror_points: true)
# Jendela banding berakhir
CloseTopicReviewJob.set(wait: 24.hours).perform_later(review.id, appeal_id: appeal.id)

Tanpa perencanaan di depan, kamu menulis signature pertama, dan di kasus kedua menyadari antarmuka harus berubah.

Tembok termahal: bisakah banding saat removal provisional? Saat suara remove melewati ambang, post langsung disembunyikan (provisional), tapi keseluruhan review masih di status voting — belum decided. Bisakah user banding sekarang?

  • Bisa → kamu harus memperluas transisi antara decided dan appealed
  • Tak bisa → UX buruk: post sudah disembunyikan dan user harus menunggu 24h untuk bisa protes
  • Pilihan sebenarnya TopicReview: ya, banding diperbolehkan saat removal provisional — tapi review harus dulu finalize → decided sebelum appeal dibuka

Satu keputusan itu merambat ke belakang dan mengubah pemeriksaan parameter dalam open_appeal! dan logika state machine. Memutuskan di tengah jalan = menulis ulang setengah sistem.

Apa yang Dilakukan Fase Plan

Plan mode Claude Code adalah mode di mana menulis kode tidak diizinkan — Claude boleh membaca repo, memikirkan pendekatan, berdiskusi denganmu, tapi setiap modifikasi file di-hard-block sampai kamu menyetujui sebuah plan.

Pembatasan mekanis itulah intinya: ia memaksa percakapan setingkat arsitektur terjadi dalam teks.

Beberapa hal yang dilakukan fase plan dalam praktik:

1. Menggambar state machine dan peran. 5 status, 4 peran (juri / hakim / penulis post / pelapor), apa yang bisa dan tidak bisa dilakukan tiap peran di tiap status — semua dalam beberapa baris markdown. Beberapa baris vs. puluhan file. Biaya perubahan berbeda dua orde besar.

2. Menelusuri alur tiap peran:

  • Juri: menerima notifikasi → membuka review → membaca post dan alasan → memilih dengan reasoning → menerima poin
  • Penulis post: menerima notifikasi → melihat putusan → kalau remove mempertimbangkan banding → menyetor stake → menunggu
  • Hakim: menerima appeal yang diundi dari top-20 → memilih → menerima poin

Di mana pun penelusuran mandek, muncul pertanyaan tersembunyi: "apakah juri melihat vote juri lain?", "apa yang bisa dilakukan penulis saat removal provisional?"

3. Menginterogasi edge case. Fase plan bukan mendesain "jalur normal" — ia dengan sengaja mengajukan pertanyaan yang biasanya tidak muncul:

  • Bagaimana kalau jendela berakhir dengan 0 suara? (Pilihan final: default keep.)
  • Bagaimana kalau banding berakhir dengan 0 suara? (dismissed; stake hangus.)
  • Bisakah seorang hakim memilih untuk post yang ia sendiri laporkan? (Tidak — aturan pengecualian yang sama.)
  • Beberapa juri memicu finalize bersamaan — apakah race menggandakan pembayaran? (Tambahkan field juror_points_awarded untuk idempotensi.)

95% pertanyaan ini tidak muncul secara alami saat menulis kode. Fase plan memaksa menjawabnya satu per satu.

4. Buku besar poin. Sistem poin cukup kompleks hingga diskusi saja tidak cukup — kamu harus benar-benar menggambar tabel: setiap aliran poin dengan pemicu, jumlah, dan jalur rollback-nya. Begitu buku besar seimbang, semua edge case (refund provisional, refund banding, bonus) dapat direkonsiliasi.

Seberapa Matang Sebelum Mulai Menulis Kode

Standar yang keras:

  • Kamu bisa menelusuri setiap jalur tanpa tersangkut — setiap kombinasi dari membuka review hingga closed (keep / remove / warn × banding / tidak × provisional / tidak) — kamu bisa menceritakannya dari ujung ke ujung
  • Setiap edge case punya perilaku yang didefinisikan — bukan "nanti lihat saja", melainkan "0 suara default keep", "provisional mengizinkan appeal tapi harus finalize dulu"
  • Tidak ada "oh, itu belum saya pikirkan" — setiap pertanyaan yang kamu bisa rumuskan sudah punya jawaban

Ketika standar ini tercapai, menulis kode menjadi menerjemahkan plan ke Ruby.

Seperti Apa Fase Eksekusi Sekali Jalan

Ship pertama sistem ini:

d162f1e Add community moderation system with jury/judge review and appeals
  63 files changed, 3032 insertions(+)
  119 specs, 0 failures

63 file, 3.032 baris, 119 spec hijau. Dikirim sekali jalan.

Belasan commit berikutnya membuktikan arsitekturnya bertahan:

Apply legacy penalty (1pt) for posts created before 2026-03-26
Fix topic review appeal bugs: window mismatch and verdict not updated
Add topic_removed status for posts removed by community review
Default to keep verdict when review expires with zero votes
Reduce provisional removal penalty to 1pt during trial period
Allow topic creator to withdraw active reviews
Add handled tab to jury dashboard, fix tabs styling

Masing-masing adalah tuning / fix / polish / fitur kecil. Tak satu pun "kembali mendesain ulang state machine" atau "model poin perlu diubah". Kerangka tulangnya benar sepenuhnya.

Inilah imbalan sesungguhnya dari plan: eksekusi tidak terganggu. Tidak ada "tunggu, jalur ini bagaimana" — plan sudah mencakupnya. Tidak ada "edge case ini belum saya pikirkan" — fase plan sudah menanyakannya. Tidak ada "antarmuka ini harus didesain ulang" — plan sudah merapikannya.

Berjam-jam menulis kode, hanya melakukan satu hal: menerjemahkan desain yang jelas ke papan ketik.

Kapan Tidak Menggunakan Plan

Tidak semua tugas layak plan:

  • Perbaikan bug sederhana — temukan + perbaiki + tambahkan regression test; tak perlu diskusi dulu
  • Refactor mekanis — ganti nama, ekstrak fungsi, pindahkan file; jalur tunggal, plan adalah langkah tambahan
  • Hanya ada satu jalur yang jelas — tambah satu GET endpoint, satu UI toggle; tak ada yang perlu didebatkan
  • Prototipe eksploratif — kamu sendiri belum tahu yang kau mau; menjalankan versi kasar mengalahkan berpikir lebih lama

Keuntungan plan mode datang dari menurunkan biaya penulisan ulang. Bila tugas tak punya risiko penulisan ulang, plan hanyalah overhead.

Penutup

"Pikirkan dulu sebelum bertindak" terdengar seperti nasihat kepribadian. Plan mode bukan soal itu.

Plan mode adalah membuat percakapan setingkat arsitektur terjadi di tempat 10 kata bisa mengubahnya — bukan di tempat 30 file harus diubah.

Di bawah kompleksitas, pepatah lama industri "words are cheap, code is expensive" perlu dibalik — bukan berarti "tulis saja kode dulu dan lihat" (itu cuma berlaku saat selisih biaya kecil), melainkan memanfaatkan selisih biaya itu: menempatkan percakapan mahal di depan, dalam medium murah.

3.000 baris dalam satu commit terasa hebat. Bukan karena saya menulis cepat — tapi karena plan mengubah "menulis" menjadi tindakan yang murni mekanis.