Bagaimana Saya Menjaga Kode Saya Tetap Terbaca bagi Orang Berikutnya
On this page
- Pendahuluan
- A. Siapa orang berikutnya itu
- B. Studi kasus: storage service bernama OSW
- C. Lima hambatan yang menghalangi
- Metode
- A. Nama yang menyatakan maksudnya
- B. Fungsi yang mengerjakan satu hal
- C. Error yang membawa maknanya dalam tipe
- D. Komentar untuk alasan, test untuk perilaku
- E. Kode yang bertahan di depan pembaca tanpa ingatan
- Hasil dan Pembahasan
- A. Yang berubah di OSW
- B. Aturan yang sama di bahasa lain
- C. Yang tak bisa diantisipasi buku itu
- D. Di mana saya berhenti
- Simpulan
- Istilah
Sebagian besar kode yang saya tulis akan dibaca orang yang tidak hadir ketika kode itu ditulis. Kadang ia rekan kerja yang melanjutkan service buatan saya. Lebih sering ia saya sendiri, enam bulan kemudian, tanpa ingatan kenapa sebuah fungsi berbentuk seperti itu. Dan makin sering pula ia AI agent yang membuka repository tanpa ingatan apa pun, membaca beberapa baris di sekitar hasil pencarian, lalu diharapkan mengubahnya dengan benar.
Saya bekerja di beberapa bahasa — Rust, Python, Node.js, PHP — dan aturan yang saya pakai supaya kode tetap terbaca bagi pembaca berikutnya itu sama di semuanya. Artikel ini menguraikan aturan-aturan itu beserta alasan saya memegangnya, dengan satu service kecil sebagai studi kasus. Service itu kebetulan ditulis dalam Rust. Aturannya tidak bicara soal Rust.
Pendahuluan
Kode jauh lebih sering dibaca daripada ditulis. Robert C. Martin menaksir perbandingan waktu membaca dan menulis kode jauh di atas sepuluh banding satu, dan sebabnya sederhana begitu disadari: hampir setiap perubahan dimulai dengan membaca. Sebelum menambah satu baris, saya membaca fungsi tempat baris itu masuk, fungsi-fungsi yang memanggilnya, dan tipe-tipe yang disentuhnya. Kalau membacanya lambat, menulisnya pun lambat, secepat apa pun saya mengetik. Biaya sebuah codebase tidak terletak pada versi pertamanya. Biaya itu ada di setiap perubahan sesudahnya, dan setiap perubahan itu dimulai dari seseorang yang berusaha memahami kode yang sudah ada.
Itulah seluruh argumen clean code dalam satu paragraf, dan argumen itu pula yang diajukan Martin dalam Clean Code: A Handbook of Agile Software Craftsmanship (2008). Buku itu berisi deretan panjang aturan praktis — cara memberi nama, seberapa besar fungsi sepatutnya, cara menangani error, kapan menulis komentar — dan tiap aturan melayani tujuan yang sama: membuat pembacaan berikutnya lebih murah. Tak satu pun aturan dasar di bawah ini saya ciptakan sendiri. Yang saya lakukan adalah menerapkannya bertahun-tahun di berbagai bahasa dan project, memperhatikan mana yang paling berpengaruh, lalu menambahkan satu argumen yang belum mungkin dibuat buku itu ketika ditulis.
A. Siapa orang berikutnya itu
Ketika saya menyebut "orang berikutnya", yang saya maksud tiga pembaca berbeda, dan kebutuhan ketiganya sedikit berlainan.
Pembaca pertama adalah rekan kerja. Ia paham bahasa dan framework-nya, tetapi tidak paham niat saya. Ia bisa bertanya, tapi hanya bila saya sempat, dan tiap pertanyaan yang terpaksa ia ajukan menandakan kodenya gagal menjawab sendiri.
Pembaca kedua adalah saya di kemudian hari — dan inilah pembaca yang paling sering diremehkan. Saya kenal gaya saya sendiri, tetapi detailnya sudah lupa: edge case mana yang memaksa percabangan aneh itu, kenapa satu nilai divalidasi di sini dan tidak di sana. Kode yang terasa jelas saat saya menulisnya berubah jadi teka-teki dalam hitungan minggu, karena yang membuatnya jelas adalah konteks di kepala saya, dan konteks itu tidak bertahan.
Pembaca ketiga adalah AI coding agent — language model yang bekerja di dalam repository dengan mencari file, membaca potongan, menjalankan perintah, dan menyunting kode secara berulang. Agent kini bagian dari cara kerja hampir semua programmer yang saya kenal, dan cara mereka membaca kode berbeda dari manusia dalam satu hal penting: mereka tak punya ingatan di luar apa yang sedang ada di depannya.
Batas itu punya nama. Context window sebuah model adalah jumlah teks yang bisa ia pegang sekaligus — percakapan, file yang sudah dibuka, keluaran perintah-perintahnya. Ketika sesi yang panjang memenuhi window itu, banyak tools menjalankan compaction: bagian percakapan yang lebih lama diganti ringkasan pendek supaya pekerjaan bisa berlanjut. Ringkasan itu menyimpan intinya dan membuang detailnya, dan detail yang terbuang sering kali justru definisi yang dibaca agent sejam sebelumnya. Sesi baru dimulai dengan window kosong. Berganti ke agent lain pun mulai dari nol. Di semua keadaan itu, agent berjumpa lagi dengan kode sebagai orang asing.
Agent juga jarang membaca satu file utuh. Ia mencari sebuah nama, membuka dua puluh atau empat puluh baris di sekitar tiap hasil, lalu bernalar dari potongan itu. Makna yang tidak tampak di baris-baris itu — di nama, di tipe, di komentar yang menempel pada keputusannya — saat itu memang tidak ada baginya.
B. Studi kasus: storage service bernama OSW
Contoh-contoh di artikel ini diambil dari OSW, service kecil yang saya rawat dan berdiri di depan object storage. Object storage adalah cara menyimpan file sebagai objek utuh di dalam wadah bernama yang disebut bucket, dicapai lewat HTTP dan bukan lewat filesystem. Antarmuka yang paling dikenal adalah Amazon S3, sedangkan MinIO adalah server self-hosted yang berbicara dengan protokol yang sama. Aplikasi berbicara ke OSW, tidak langsung ke storage-nya. OSW membagikan presigned URL — tautan sementara yang mengizinkan browser meng-upload atau men-download satu file tertentu tanpa pernah memegang kredensial storage — dan mengelola siklus hidup sederhana: upload baru mendarat di area sementara, dipindahkan ke tempat permanennya begitu aplikasi mengonfirmasi, dan digeser ke area terhapus ketimbang langsung dimusnahkan.
OSW cocok jadi studi kasus karena ia ada dalam dua versi. Versi lamanya service yang berjalan baik, kira-kira dua ribu baris, dengan seluruh logika di satu folder service yang datar. Tugasnya beres, tapi mengubahnya susah. Saya membangunnya ulang mengikuti aturan di artikel ini sambil menjaga setiap response tetap identik bagi aplikasi yang sudah memakainya. Syarat kedua itu penting di sini: artinya setiap beda antara kedua versi adalah beda keterbacaan, bukan beda perilaku. Keduanya Rust, tetapi persoalan di versi lama bukan persoalan Rust. Semuanya pernah saya jumpai di Python, JavaScript, dan PHP, dan perbaikannya bisa dipindahkan langsung — bagian Hasil menunjukkan caranya.
Versi lama itu tidak buruk dalam arti yang lazim. Tak ada yang sembrono di dalamnya. Ia ditulis oleh orang yang memahami seluruh service pada saat menulis, dan persis itulah yang membuatnya sulit dibaca oleh siapa pun yang tidak memahaminya.
C. Lima hambatan yang menghalangi
Jadi tujuannya mudah dirumuskan: menulis kode yang bisa dipahami pembaca berikutnya — rekan kerja, saya di masa depan, atau agent tanpa ingatan — dari kodenya sendiri, cukup cepat sehingga mengubahnya tetap murah, apa pun bahasa yang dipakai project itu.
Gampang dirumuskan, canggung dijalankan. Kebanyakan kode yang sulit dibaca bukan lahir dari kecerobohan, melainkan dari lima kebiasaan yang masing-masing terasa masuk akal saat itu, dan masing-masing punya jalan keluar yang tampak jelas tapi sebenarnya tidak berhasil.
Hambatan pertama: nama yang menyembunyikan maksudnya. Semua fungsi di OSW versi lama bernama execute, dan nama tipenya berakhiran Entity entah ia memang entity atau bukan. Obat yang langsung terpikir adalah dokumentasi: README atau halaman wiki yang menjelaskan semuanya. Saya menolaknya, karena dokumentasi tinggal di tempat lain, dan pembaca yang sedang melihat satu pemanggilan tidak melihatnya. Nama adalah satu-satunya penjelasan yang ikut terbawa bersama kode ke setiap tempat ia dipakai.
Hambatan kedua: fungsi yang mengerjakan lebih dari satu hal. Satu fungsi membuat presigned URL dari delapan argumen terpisah. Fungsi lain menghapus file dengan salah satu dari dua cara yang sangat berbeda, tergantung sebuah boolean. Pembelaan yang biasa: memecahnya hanya menambah jumlah fungsi yang harus diingat. Padahal fungsi yang perilakunya ditentukan flag adalah dua fungsi yang berbagi nama, dan pemanggilan dengan delapan argumen berurutan tak bisa dibaca tanpa membuka signature-nya.
Hambatan ketiga: error yang berpindah sebagai teks. Ketika file tidak ada, versi lama mengembalikan error yang pesannya diawali NOT_FOUND:, dan lapisan HTTP memeriksa awalan itu untuk memutuskan 404. Godaannya adalah menganggap semuanya baik karena berjalan. Ia berjalan hanya selama dua potong kode di dua file berbeda sepakat soal satu string, dan tak ada yang menjaga kesepakatan itu — bukan compiler, bukan test, bukan pembaca yang cuma melihat salah satu sisinya.
Hambatan keempat: alasan di balik kode tidak ada di dalam kode. Banyak baris di storage service ada karena alasan yang tak terlihat dari barisnya: validasi yang sengaja diletakkan di awal, URL yang dibangun dengan cara tak lazim karena signature-nya bergantung pada itu. Jalan yang biasa diambil orang adalah tanpa komentar sama sekali ("kodenya harus bicara sendiri") atau komentar yang mengulang apa yang dikerjakan kode. Yang pertama kehilangan alasannya; yang kedua basi begitu kodenya pertama kali berubah. Di sampingnya ada ketiadaan test, yang membuat pembaca tak bisa membedakan perilaku mana yang disengaja dan mana yang kebetulan.
Hambatan kelima: pembaca berikutnya mungkin tak ingat apa-apa. Semua aturan di atas ditulis untuk manusia, yang setidaknya makin akrab dengan sebuah codebase seiring waktu. AI agent tidak. Pemahamannya dibangun ulang dari nol setiap kali context-nya di-compact, dikosongkan, atau diserahkan ke tools lain. Jalan pintasnya — menulis penjelasan panjang di percakapan atau di file instruksi untuk agent — gagal dengan sebab yang sama seperti dokumentasi: letaknya terpisah dari baris yang sedang dibaca, dan teks semacam itulah yang paling dulu dibuang compaction.
Urutannya penting. Nama didahulukan karena semua yang lain dibangun darinya: fungsi dinilai dari namanya, tipe error adalah sebuah nama, dan test sebagian besar berupa nama yang diikuti assertion. Hambatan kelima diletakkan terakhir karena ia bukan teknik tersendiri. Ia alasan baru kenapa keempat hambatan sebelumnya kini lebih penting daripada ketika buku itu ditulis.
Metode
A. Nama yang menyatakan maksudnya
Inilah jawaban atas hambatan pertama: saya memilih nama supaya pembaca yang hanya melihat tempat pemanggilannya sudah paham apa yang terjadi, tanpa membuka definisinya. Deskripsi clean code dari Ward Cunningham, yang dikutip Martin, jadi ujian saya: kita sedang membaca clean code bila setiap rutin ternyata kurang lebih persis seperti yang kita duga dari namanya.
Contoh paling jelas di OSW adalah siklus hidup file. Di versi lama, siklus itu hanya ada sebagai string literal: "temporary/" di satu fungsi, "deleted/" di tiga fungsi lain, masing-masing digabung atau dipotong dengan tangan. Pembaca harus menemukan semua kemunculannya untuk paham bahwa string-string itu bersama-sama membentuk state machine. Sekarang konsep itu punya nama sendiri:
// Posisi sebuah objek dalam siklus hidupnya saat ini.
Baris seperti Lifecycle::Deleted.apply(&path) terbaca sebagai kalimat. Ia menunjukkan bahwa file punya status, bahwa "deleted" salah satunya, dan bahwa status itu tersimpan di path — tak satu pun pernah disampaikan literal "deleted/".
Aturan yang sama saya pakai untuk nama tipe. Sufiks Entity saya buang karena ia menjelaskan di mana tipe itu diletakkan, bukan apa tipe itu: FileMetadataEntity menjadi FileMetadata, dan enam fungsi bernama execute menjadi use case yang dinamai menurut kerjanya — CommitTemporaryFile, RestoreFile, GeneratePresignedUrl. Saya selalu memilih nama panjang ketimbang nama pendek. Nama panjang menambah beberapa karakter di tiap pemakaian; nama yang kabur menambah satu perjalanan ke definisi di tiap pemakaian, dan yang menanggung perjalanan itu pembacanya, bukan penulisnya.
B. Fungsi yang mengerjakan satu hal
Fungsi sepatutnya mengerjakan satu hal, pada satu tingkat detail, dengan argumen yang cukup sedikit untuk dibaca tanpa dicari. Fungsi pembuat presigned URL dulu menerima delapan argumen terpisah. Pemanggilannya berupa deretan nilai yang maknanya sepenuhnya ditentukan posisi. Saya menggantinya dengan satu input bernama:
Kini tiap nilai berlabel di mana pun command itu dibangun, dan dependensi yang tak pernah berubah antar-pemanggilan — client storage, clock, generator ID — pindah ke constructor use case, tidak lagi dioper di setiap pemanggilan. Bentuk yang sama ada di setiap bahasa yang saya pakai: dataclass di Python, object biasa bertipe di TypeScript, class kecil readonly di PHP.
Flag argument adalah separuh lain dari aturan ini. Keberatan Martin: parameter boolean mengumumkan bahwa fungsinya mengerjakan dua hal, satu ketika flag bernilai true dan satu lagi ketika false. OSW masih punya satu, dan saya lebih suka mengatakannya terus terang daripada menampilkan gambaran yang lebih rapi dari kodenya: DeleteFile bercabang berdasarkan is_soft_delete. Yang berubah, flag itu kini berada di dalam command bernama, bukan sebagai true telanjang di deretan argumen, dan tiap cabang mengembalikan hasil dengan nama berbeda — DeleteOutcome::soft_deleted atau DeleteOutcome::hard_deleted — sehingga pembaca selalu tahu jalur mana yang berjalan. Memecahnya jadi dua use case adalah langkah wajar berikutnya. Bedanya, flag yang tersisa kini terlihat dan bernama, dan justru itu yang membuat langkah tadi gampang diambil.
C. Error yang membawa maknanya dalam tipe
Hambatan ketiga punya penyelesaian paling rapuh di versi lama. Bentuknya kurang lebih begini:
// versi lama, disederhanakan ke bentuknya
return Err;
// … di file lain, di lapisan HTTP
if msg.starts_with Tak ada yang menghubungkan kedua belahan itu kecuali string yang kebetulan dieja sama oleh keduanya. Ubah satu, dan file yang tidak ada diam-diam berubah jadi server error. Saya mengganti teks itu dengan tipe yang menamai setiap cara sebuah use case bisa gagal:
Penerjemahan ke HTTP kini terjadi di tepat satu fungsi di tepi service, dan compiler memastikan tiap varian ditangani:
Aturan yang sama muncul di bab penanganan error buku Martin, sebagai anjuran memakai exception ketimbang kode kembali, dan sebagai nasihat untuk membekali error dengan konteks yang cukup untuk ditindaklanjuti. Di bahasa yang punya exception, padanannya adalah hierarki kecil class exception yang ditangkap di satu tempat. Yang penting bukan mekanismenya, melainkan bahwa makna kegagalan tinggal di dalam konstruksi yang bisa diperiksa bahasanya, dan bukan di teks yang hanya dibandingkan manusia.
D. Komentar untuk alasan, test untuk perilaku
Martin menulis bahwa pemakaian komentar yang tepat adalah untuk menutupi kegagalan kita mengungkapkan diri lewat kode. Saya setuju soal komentar yang menjelaskan apa yang dikerjakan kode: kalau kode membutuhkannya, berarti nama atau strukturnya keliru, dan komentar itu akan tertinggal zaman begitu seseorang pertama kali menyunting baris di bawahnya. Saya berbeda pendapat dengan pembacaan kaku atas aturan itu pada komentar yang mencatat kenapa. Tak ada nama yang bisa memikul alasan. Ini satu contoh dari OSW:
Tanpa komentar itu, pembaca yang sedang merapikan kode bisa saja memindahkan pemeriksaannya ke tempat nilai itu dipakai, dan service akan menyala dengan setelan rusak lalu gagal di request sungguhan pertamanya. Komentar itu membuat keputusannya terlihat tepat di titik tempat keputusan itu bisa dibatalkan.
Test menjawab separuh lainnya: perilaku mana yang disengaja. Saya menamai test sebagai kalimat tentang perilaku, sehingga daftar nama test terbaca seperti spesifikasi, dan saya menjalankan use case terhadap versi in-memory dari dependensinya supaya test tak butuh storage server sungguhan:
async Bagian kedua itu bergantung pada satu pilihan arsitektur: use case berbicara ke storage lewat interface — port, dalam kosakata Clean Architecture — dan tidak memanggil client S3 secara langsung. Di versi lama, service memanggil fungsi storage secara langsung, sehingga test mana pun untuknya butuh storage server yang menyala. Dengan test terpasang, merapikan kode tak lagi berisiko, dan itulah yang membuat perapian bisa berlangsung terus-menerus, bukan sesekali lewat penulisan ulang yang menegangkan.
E. Kode yang bertahan di depan pembaca tanpa ingatan
Hambatan terakhir inilah tempat pengalaman saya melampaui buku itu. Saya bekerja dengan AI agent setiap hari, dan yang berulang kali saya lihat: mutu perubahan yang dibuat agent sangat ditentukan oleh seberapa banyak makna yang dibawa kode di sekitarnya dengan sendirinya.
Bayangkan yang dilihat agent ketika diminta memperbaiki 404 yang berubah jadi 500. Di OSW versi lama, pencarian akan menemukan msg.starts_with("NOT_FOUND:"). Baris itu baru separuh cerita; separuhnya lagi ada di file mana pun yang menghasilkan string tersebut. Kalau agent sudah membaca file itu lebih awal di sesi yang sama, pengetahuannya mungkin sudah hilang dimakan compaction. Di sesi baru, ia memang tak pernah memilikinya. Perbaikan yang tampak masuk akal adalah menyesuaikan teks di sisi yang terlihat — yang diam-diam mematahkan setiap tempat lain yang bergantung pada ejaan lama. Di OSW sekarang, pencarian yang sama menemukan UseCaseError::NotFound { path, bucket }. Maknanya ada di tipe, tipenya diperiksa compiler, dan perubahan apa pun yang dibuat agent di satu sisi tanpa dicocokkan sisi lain akan gagal di-build, bukan gagal di production.
Jadi saya menambahkan satu ujian pada aturan-aturan di atas: bisakah pembaca memahami baris ini seandainya hanya baris inilah yang ia baca hari ini? Nama seperti Lifecycle::Deleted lolos. Literal "deleted/" tidak. Command struct dengan field berlabel lolos; delapan argumen berurutan tidak. Komentar yang menyatakan alasan di samping keputusannya lolos; penjelasan yang ditulis sebelumnya di percakapan yang kini sudah diringkas habis tidak. Ternyata clean code persis sifat yang membuat kehilangan konteks jadi tak berbahaya: ketika setiap baris menjelaskan dirinya, kehilangan sisa konteks hanya menelan sedikit biaya.
Ada bonus praktisnya juga. Agent menemukan kode dengan mencari, dan nama yang khas membuat pencarian tepat sasaran. Mencari Lifecycle::Deleted mengembalikan setiap tempat status terhapus dipakai dan tak ada yang lain; mencari "deleted/" ikut mengembalikan pesan log, komentar, dan path yang tak berhubungan di samping yang memang penting.
Hasil dan Pembahasan
A. Yang berubah di OSW
Membangun ulang OSW dengan cara ini mengubah strukturnya secara besar-besaran tanpa menyentuh perilaku luarnya. Setiap response yang diandalkan aplikasi tetap sama, sampai ke susunan kata pesan error-nya, dan deskripsi API yang diterbitkan tetap identik. Seluruh perbedaannya ada di sisi pembaca:
| Aspek | Versi lama | Versi sekarang |
|---|---|---|
| Use case | enam fungsi bernama execute dalam satu folder datar | use case bernama, dikelompokkan per resource: file, presigned URL, temporary |
| Helper response | dua belas fungsi yang nyaris kembar | satu presenter |
| Validasi bucket | pemeriksaan yang sama ditulis lima kali | satu modul validasi |
| Error | teks berawalan NOT_FOUND: | tipe error empat varian, diterjemahkan ke HTTP di satu tempat |
| Akses storage | dipanggil langsung dari service | di balik port, dengan versi in-memory untuk test |
| Test | tak satu use case pun bisa diuji tanpa storage server | 98 test, tak satu pun butuh server yang menyala |
Ukuran paling berguna justru tidak ada di tabel. Saya menambahkan dua fitur sesudahnya — copy di sisi server antar-bucket, dan endpoint batch yang menandatangani hingga seratus tautan download dalam satu request — dan masing-masing mendarat di tempat-tempat yang sama dan bisa ditebak: tipe input, use case dengan test-nya sendiri, tipe hasil di domain, method pada adapter storage bila memang perlu, handler, dan satu baris wiring. Strukturnya sudah menunjukkan letak tiap bagian sebelum saya menulisnya.
B. Aturan yang sama di bahasa lain
Tak satu pun dari semua ini khas Rust. Aturannya soal pembaca, dan setiap bahasa punya wujud untuknya:
| Aturan | Rust | Python | TypeScript (Node.js) | PHP |
|---|---|---|---|---|
| Namai konsep, jangan ulang literal | enum | Enum | string union atau enum | enum (8.1+) |
| Satukan banyak argumen jadi satu input | struct | @dataclass | parameter object bertipe | readonly class |
| Bawa makna kegagalan dalam tipe | enum error | class exception | subclass Error | class exception |
| Bergantung pada port, bukan client | trait | Protocol atau ABC | interface | interface |
Di service Python yang dibangun di atas FastAPI, misalnya, aturan error berwujud seperti ini — exception bernama, dan satu penerjemahan di tepi:
, = ,
# satu penerjemahan ke HTTP, di tepi service
return Yang berbeda adalah seberapa besar bantuan bahasanya dalam menegakkan aturan itu. Compiler Rust menolak match yang melupakan satu varian error; di Python, jaminan yang sama harus datang dari test. Aturannya tidak berubah mengikuti tooling. Yang berubah hanya jaring pengamannya.
C. Yang tak bisa diantisipasi buku itu
Clean Code terbit tahun 2008, jauh sebelum AI coding agent ada. Pembacanya programmer manusia, dan argumennya berkisar pada perhatian dan ingatan manusia. Argumen-argumen itu masih berlaku. Namun kini hampir semua programmer yang saya kenal bekerja berdampingan dengan agent, artinya sebagian besar pembacaan di sebuah codebase dilakukan oleh pembaca dengan kelemahan yang ganjil: ia lupa.
Pandangan saya, keadaan ini membuat buku itu makin relevan, bukan berkurang. Ketiadaan ingatan agent adalah versi ekstrem dari persoalan yang sudah dipecahkan Martin. Rekan kerja manusia lupa sedikit demi sedikit; agent lupa sepenuhnya, di setiap compaction, setiap sesi baru, setiap perpindahan ke tools lain. Aturan yang dirancang supaya kode bisa dipahami tanpa konteks penulisnya ternyata persis aturan yang membuat kode bisa dipahami tanpa konteks sama sekali. Nama yang baik adalah satu-satunya penjelasan yang masih ada ketika semua yang lain sudah diringkas habis.
Kebalikannya pun berlaku, dan bagian inilah yang paling saya rasakan dalam kerja sehari-hari. Codebase yang sulit dibaca bukan cuma lebih lambat bagi agent; ia lebih berbahaya. Manusia yang tak paham satu baris biasanya berhenti dan bertanya. Agent cenderung menghasilkan perubahan yang tampak masuk akal lalu melanjutkan. Makin sedikit kode menjelaskan dirinya, makin banyak perubahan masuk akal itu yang keliru dengan cara yang baru disadari belakangan.
D. Di mana saya berhenti
Clean code punya ongkos, dan saya berusaha menjaganya tetap sepadan. Mengganti nama demi mengganti nama menghasilkan diff besar yang mempersulit penelusuran riwayat, jadi saya mengganti nama ketika memang sedang mengubah kodenya atau ketika sebuah nama benar-benar menyesatkan. Saya tidak memecah fungsi hanya karena melewati batas jumlah baris yang sembarang; saya memecahnya ketika ia mengerjakan dua hal. Dan, seperti ditunjukkan flag di DeleteFile, saya lebih memilih ketidaksempurnaan yang terlihat dan bernama daripada yang tersembunyi. Tujuannya bukan codebase yang mematuhi setiap aturan, melainkan codebase yang pembaca berikutnya bisa mengerjakannya tanpa perlu bertanya apa pun kepada saya.
Simpulan
Saya menulis kode untuk pembaca yang datang berikutnya: rekan kerja, saya sendiri berbulan-bulan kemudian, atau AI agent yang berjumpa dengan kode sebagai orang asing setiap kali context-nya direset. Aturan yang saya pakai adalah aturan Robert C. Martin, dan semuanya bekerja dengan cara yang sama di Rust, Python, TypeScript, dan PHP: nama yang menyatakan maknanya, fungsi yang mengerjakan satu hal dengan sedikit argumen, error yang maknanya tinggal di dalam tipe, komentar yang mencatat alasan, dan test yang mencatat perilaku. OSW memperlihatkan dampaknya dalam praktik — perilaku yang sama, dicapai lewat kode yang bisa dibaca satu potongan demi satu potongan.
Satu gagasan yang ingin saya tambahkan pada buku itu adalah alasan kenapa semua ini kini lebih penting. Setiap aturan yang menolong manusia yang tak punya konteks penulis juga menolong pembaca yang tak punya konteks apa pun, dan pembaca semacam itu kini hadir di hampir setiap codebase, setiap hari. Kode yang terbaca adalah satu-satunya konteks yang bertahan.
Istilah
- Clean code
- Kode yang ditulis supaya pembaca berikutnya bisa memahami dan mengubahnya dengan murah. Istilahnya berasal dari buku Robert C. Martin berjudul sama.
- AI coding agent
- Language model yang bekerja di dalam repository kode dengan mencari, membaca potongan, menjalankan perintah, dan menyunting file secara berulang.
- Context window
- Jumlah teks yang bisa dipegang dan dinalar language model dalam satu waktu.
- Compaction
- Penggantian bagian lama sebuah sesi agent yang panjang dengan ringkasan pendek supaya sesi bisa berlanjut; detailnya, termasuk definisi yang dibaca sebelumnya, ikut hilang.
- Object storage
- Storage yang menyimpan file sebagai objek utuh di dalam bucket bernama, dicapai lewat HTTP. Amazon S3 menetapkan antarmuka yang umum; MinIO adalah server self-hosted yang kompatibel dengannya.
- Presigned URL
- Tautan sementara yang mengizinkan satu upload atau download tertentu tanpa memberikan kredensial storage kepada client.
- Flag argument
- Parameter boolean yang mengalihkan sebuah fungsi di antara dua perilaku berbeda.
- Port
- Interface tempat logika aplikasi menjangkau sistem luar, sehingga sistem itu bisa diganti — misalnya dengan versi in-memory saat test.
Technologies covered
- Python
- Rust