[Edisi Khusus] Cara Menghindari Kode Spaghetti — Kapan, Bagaimana, dan Siapa yang Harus Meninjau Refaktorisasi

221.131.***.***
11

Ini adalah seri spin-off dari catatan pengembangan tunaFlow. Kali ini saya akan membahas "bagaimana cara memelihara kode yang ditulis bersama agen". Jika ada yang mau membantah, kalian semua tetap benar. 😁

Jujur saja, ketika saya melihat proyek yang dibuat orang lain menggunakan agen, saya langsung membuka kodenya. Dan begitu melihatnya, saya sudah berpikir "pasti kode spageti lagi". Prasangka ini sebenarnya sering terbukti benar. Umumnya, orang yang tidak memiliki pengalaman pengembangan perangkat lunak (tidak harus developer), atau yang memulai hanya dengan satu ide dan berpikir "kalau jalan, lanjut saja!", membiarkan kode terus membengkak seperti bola salju tanpa sekali pun melakukan refactoring. Akibatnya, satu file seperti app.js bisa melebihi 1000 baris, dan ketika agen harus memperbaiki satu hal di tengah kode, ia membaca seluruh file dari awal sehingga konteks mencapai batasnya dan mulai memperbaiki bagian yang salah. Itulah yang terjadi.


Apa itu "kode spageti"

Ini adalah istilah yang saya pakai. Definisinya seperti ini.

  • Tampak luar tidak ada masalah — nama fungsi tidak buruk, tipe sudah sesuai, bahkan mungkin ada test

  • Satu file melebihi 2.000 baris — awalnya hanya 200 baris, tapi hasil agen yang terus mengulang "saya tambahkan di sini" menjadi seperti ini

  • Logika yang sama ada di tiga tempat — agen sering memilih menulis ulang daripada membaca kode yang sudah ada

  • Batas antar modul runtuh — ada pemanggilan DB di komponen UI, ada permintaan HTTP di store, tapi semuanya "bisa jalan"

  • Tidak bisa dipelihara setelah 1 bulan — tapi kalau memang tidak akan disentuh setelah 1 bulan, tidak masalah

LLM sangat pandai menulis satu fungsi yang rapi. Tapi batas antar modul dan pemisahan tanggung jawab akan runtuh jika tidak secara sadar diarahkan oleh manusia. Di sinilah titik terjadinya spageti.


Kapan melakukan refactoring

Saya menggunakan tiga momen sebagai sinyal.

1. Tepat sebelum menambahkan fitur baru

Ini yang paling penting. Sebelum meminta agen untuk "tambahkan fitur X", saya meninjau kode terkait dan membuat keputusan ini:

  • Jika file yang akan ditambahkan fitur adalah god-file lebih dari 1.000 baris, pecah dulu baru mulai

  • Jika ada logika serupa yang sudah ada tapi lokasinya tidak jelas, satukan dulu di satu tempat baru mulai

  • Jika kode yang ada sudah konsisten, lanjutkan saja

Agen bekerja jauh lebih akurat di atas kode yang bersih. Menambahkan satu sampah di sebelah sampah lainnya tidak ada biayanya bagi agen, tapi membaca dan mempertahankan konteks itu tidak gratis bagi saya.

Dalam catatan nyata tunaFlow, pada sesi s26~s29, saya memecah 5 god-file besar menjadi 18 modul. Baru setelah itu penambahan fitur baru dilakukan. Saat itu saya terus meyakinkan diri sendiri bahwa ini "terlihat seperti pemborosan tapi bukan pemborosan", dan sekarang saya melihat bahwa berkat pemecahan saat itu, dalam sesi hari ini saya bisa melacak di mana apa berada bahkan sambil merge 30+ PR.

2. Cacat yang sama muncul dua kali berturut-turut

Jika jenis temuan yang sama berulang dalam tahap review, itu bukan bug implementasi, melainkan masalah desain. Bukan salah agen, tapi saya yang menyuruhnya bekerja di atas struktur yang salah.

Dalam workflow Review tunaFlow, ada logika untuk mendeteksi ini. Jika fail berturut-turut muncul di file yang sama, event design_review_suggested akan dicatat, dan jika lebih dari 3 kali, peringatan doom loop akan ditampilkan. Situasi paling berbahaya adalah ketika agen terus mencoba memperbaiki sendiri dan terus tersandung di tempat yang sama.

3. "Rule of three"

Titik kritis adalah saat pola yang sama direplikasi untuk ketiga kalinya. Sampai dua kali masih bisa kebetulan. Ketiga kalinya adalah sinyal struktural.

Yang penting di sini adalah bukan menangkapnya tepat sebelum kemunculan ketiga, melainkan melakukan abstraksi setelah kemunculan ketiga. Melakukan abstraksi lebih awal sebelum dua kali kemunculan pun belum terjadi saya sebut "abstraksi spekulatif", dan agen sangat sering melakukan ini. Kode yang diawali dengan "untuk skalabilitas di masa depan..." tidak perlu dibuat kompleks sekarang. Sebelum kemunculan ketiga, copy-paste saja lebih baik.


Bagaimana cara melakukan refactoring

Memotong menjadi PR kecil

Jika Anda meminta agen untuk "pecah god-file ini menjadi 18 modul" sekaligus, biasanya akan berantakan. Cara saya:

  1. Pertama, gambar peta dekomposisi secara manual (modul apa berisi apa)

  2. Ekstrak setiap modul satu per satu dalam satu PR

  3. Syarat setiap PR adalah test tetap lolos (tidak ada perubahan fungsionalitas, semua tipe/test lulus)

  4. Semakin kecil PR semakin baik, agar bisa langsung rollback jika ada masalah di tengah jalan

Dari 30 PR yang di-merge dalam sesi hari ini, yang terbesar adalah 800 baris, dan sebagian besar berkisar 50~200 baris. Ini adalah desain yang disengaja. Satu PR besar sekaligus terlihat lebih cepat, tapi jika berantakan di tengah jalan, seluruhnya harus dikembalikan sehingga pada akhirnya lebih lambat.

Minta agen membaca kode yang ada terlebih dahulu

Ketika meminta agen untuk melakukan modifikasi, saya hampir selalu mengatakan "baca semua file terkait dulu, pahami strukturnya, baru bicara". Agen cenderung melewati pembacaan dan langsung ke penulisan, dan jika ini tidak secara eksplisit dicegah, ia akan membuat solusi baru dengan cara berbeda untuk sesuatu yang sudah diselesaikan oleh orang lain sebelumnya.

Mode "hanya periksa, jangan modifikasi"

Instruksi yang sering saya berikan kepada agen sebelum memasuki refactoring. "Hanya periksa kondisi kode, jangan lakukan modifikasi. Modifikasi setelah saya membuat keputusan." Satu kalimat ini ternyata cukup efektif untuk mencegah spageti. Jika agen membereskan secara mandiri di tahap tengah sesuai penilaiannya sendiri, niat menjadi bercampur dan review menjadi sulit.


Cara agar agen tidak tersesat dalam kode yang panjang

tunaFlow saat ini berukuran Rust 25k baris + TypeScript 24k baris. Pada skala ini, satu agen tidak bisa mengetahui keseluruhan sekaligus. Untuk menjaga arah, diperlukan injeksi konteks dalam tiga sumbu.

Sumbu 1: Konteks proyek (ContextPack)

Setiap permintaan, saya merakit dan menyerahkan hal-hal berikut kepada agen:

  • Identitas proyek (dokumen setara CLAUDE.md)

  • Plan pekerjaan saat ini dan temuan sebelumnya

  • Indeks file terkait (embedding + graph)

  • Ringkasan percakapan terbaru (memori jangka panjang)

Karena tidak mungkin menyalin ini secara manual setiap kali, saya malah membuat alat (tunaFlow) khusus untuk ini. "Kualitas konteks lebih penting dari kualitas model" — pernyataan itu benar-benar terasa nyata di sini. Saat membuat tunaFlow, saya menempatkan Opus untuk perencanaan dan Opus untuk membuat atau memperbaiki kode di sesi (jendela) yang berbeda, membiarkan Opus perencanaan (arsitek) menganalisis secara mendalam dan membuat dokumentasi, lalu menyalinnya ke Opus pembuat kode.

Sumbu 2: Pemisahan peran

Bahkan agen yang sama pun akan berperilaku berbeda jika diberi persona yang berbeda per peran:

  • Architect: "Buat keputusan desain. Jangan langsung masuk ke implementasi. Jalankan setelah disetujui."

  • Developer: "Jangan menyentuh apa pun di luar cakupan Plan. Hanya modifikasi bagian yang diminta."

  • Reviewer: "Tunjukkan hanya cacat yang objektif. Jangan menyimpulkan masalah yang belum dikonfirmasi. Pisahkan pendapat sebagai rekomendasi."

Meskipun ketiga persona menggunakan model yang sama (misalnya: Claude Sonnet), perilaku mereka terpisah berkat batasan prompt per peran. Terutama dengan memberikan batasan seperti "dilarang memodifikasi di luar cakupan plan" pada Developer persona, kita bisa mencegah agen melebar ke "sekalian ini juga...".

Sumbu 3: Pembagian proses

Kebiasaan hanya meminta satu hal sekaligus. Secara konkret:

  • Memaksakan alur plan → dev → review (pipeline workflow tunaFlow)

  • Gerbang persetujuan pengguna di setiap tahap

  • Hanya satu topik per PR (kualitas review menurun jika dua atau lebih hal dicampur)

Yang pernah meminta agen untuk "lakukan A sekaligus B dan C juga" pasti tahu hasilnya. Ketiganya jadi tanggung. Selesaikan A dulu lalu setujui, selesaikan B lalu setujui, selesaikan C lalu setujui — itu justru pada akhirnya lebih cepat.


Bagaimana cara menggunakan agen reviewer

Inilah hal yang paling ingin saya sampaikan hari ini.

Mereview kode sendiri sendiri akan gagal

Berlaku untuk manusia, tapi agen jauh lebih parah. Jika agen mereview kode yang dia sendiri tulis, hasilnya hampir pasti "sepertinya sudah ditulis dengan baik ✅". Ia tidak sadar bahwa konteksnya sudah ada di "kepalanya" sendiri sehingga merasa tidak mungkin ada yang terlewat.

Serahkan ke agen lain (lebih baik lagi jika berbeda mesin)

Jika kode yang ditulis Claude direview oleh Codex atau Gemini, hasilnya jauh lebih baik. Ini karena semakin berbeda keluarga modelnya, semakin berbeda pula biasnya. Kombinasi Claude + Codex lebih mendekati cross-validation yang sesungguhnya dibandingkan menjalankan Claude dua kali.

Berikan hasil eksekusi nyata kepada reviewer

Inilah intinya. Jika Reviewer hanya disuruh "periksa hasil test yang dilaporkan Developer", maka jika Developer berbohong dengan halusinasi "test sudah lulus", Reviewer akan meneruskan pass begitu saja. Ini benar-benar terjadi.

Solusinya: Sebelum memanggil Reviewer, jalankan benar-benar cargo test / vitest, lalu masukkan outputnya ke prompt Reviewer dan serahkan bersama-sama. Di tunaFlow, tombol mulai Review RT bekerja dengan urutan ini:

  1. Eksekusi nyata run_project_tests

  2. Hasil stdout/stderr disimpan ke variabel testOutput

  3. Panggil startReviewRT(plan, messages, testOutput, engines)

  4. Diinjeksikan sebagai bagian ## Hasil Test ke prompt Reviewer

Dengan begini, penilaian dibuat berdasarkan hasil eksekusi, bukan laporan mandiri dari Developer. Tahun lalu Anthropic merangkum hal serupa dalam makalah "Agent-as-Judge", dan intinya adalah reviewer melihat hasil alat, bukan teks.

Gunakan rubrik numerik

Review format bebas ("pass / fail / findings: [...]") memiliki standar yang berbeda-beda tergantung reviewer. Saya memaksakan penilaian 5 dimensi:

  • plan_coverage (1~5): apakah subtask yang direncanakan benar-benar diimplementasikan

  • code_quality (1~5): cacat runtime/logika/keamanan

  • test_coverage (1~5): hasil test + cakupan

  • doc_quality (1~5): pembaruan komentar/dokumentasi

  • convention (1~5): konsistensi dengan gaya coding yang ada

Penilaian otomatis berdasarkan total skor (22+ pass / di bawah 10 fail / selainnya conditional). Agen tidak bisa mengeluarkan sesuatu seperti "rasanya pass". Karena harus memasukkan angka.

Hati-hati saat meminta review dari agen eksternal

Jika menyerahkan seluruh review kepada LLM eksternal seperti Gemini yang tidak mengetahui konteks proyek, satu kata seperti "kritisi habis-habisan" bisa melepas sabuk pengaman penegasan (lol). Penilaian yang tegas tanpa dasar bisa muncul, dan jika dipercaya, bisa terjadi kejadian di mana kode yang baik-baik saja malah dibuang.

Saya pernah mengalami ini dua hari lalu (sudah saya tulis secara rinci di tulisan lain). Review eksternal sebaiknya hanya digunakan sebagai stimulasi, dan setiap temuan harus diverifikasi satu per satu dengan mengutip baris kode. Dari satu review eksternal, biasanya hanya 1~2 dari 5 temuan yang bisa dipakai. Sisanya bisa diabaikan dengan tertawa. Namun karena bisa menangkap hal yang cukup kritis, disarankan untuk mendiskusikannya bersama arsitek.


Ringkasan

Bagi saya, pencegahan kode spageti pada akhirnya bermuara pada tiga hal.

  1. Bersihkan sebelum menambahkan fitur baru. Jangan menambahkan fitur di atas sampah

  2. Berikan injeksi konteks + batasan peran kepada agen. Persona + ContextPack + persetujuan per tahap

  3. Reviewer adalah agen lain + menerima hasil eksekusi nyata + menilai dengan rubrik numerik

Tidak ada trik khusus. Ini hampir sama dengan apa yang dilakukan senior saat bekerja dengan junior di tim manusia. Perbedaannya, agen menuntut pola ini tanpa henti selama 24 jam sehari. Karena itulah dibutuhkan alat, dan itulah mengapa saya sedang membangun tunaFlow sebagai alat tersebut.

Masih jauh dari selesai. Tapi setidaknya saya berusaha menghindari situasi di mana "saya yang mengejek kode spageti ternyata sedang membuat spageti sendiri". 😁


Referensi / Tulisan Terkait

  • Sebastian Raschka, Components of a Coding Agent (2025) — "model quality = context quality"
  • Addy Osmani, Orchestrating Coding Agents (2025) — pola orkestrasi multi-agen
로그인한 회원만 댓글 등록이 가능합니다.

개발한당

KR | ID | EN
  • IDR
  • KOR
8.34 =0.00

2026.07.11 KEB 하나은행 고시회차 1694회

다가오는 한인 행사일정

  • 등록 된 일정이 없어요!