- Kompleksitas adalah elemen paling berbahaya dalam pengembangan
- Efisiensi yang sesungguhnya lahir dari pendekatan pragmatis yang menghindari kompleksitas, seperti "solusi 80/20"
- Penting untuk menjaga sikap yang seimbang dan fleksibel terhadap pengujian dan refactoring
- Menekankan pemanfaatan alat serta kebiasaan menulis kode yang mudah dibaca dan mudah dikelola
- Mewaspadai abstraksi yang berlebihan dan tren sesaat, serta merekomendasikan sikap yang mengejar kesederhanaan
Pendahuluan
- Tulisan ini adalah kumpulan pemikiran seorang pengembang Grug Brain yang merangkum hal-hal yang dipelajari dari pengalaman selama bertahun-tahun mengembangkan perangkat lunak
- Pengembang Grug Brain menganggap dirinya tidak terlalu pintar, tetapi telah belajar banyak lewat waktu panjang yang dihabiskan untuk pemrograman
- Ia membagikan pemahaman yang didapat dengan cara yang mudah dan lucu, berharap orang lain bisa belajar dari kesalahan
- Kompleksitas adalah musuh terbesar dalam hidup seorang pengembang
- Kompleksitas menyusup diam-diam ke dalam codebase, membuat kode yang awalnya mudah dipahami perlahan menjadi nyaris mustahil diubah
Menghadapi roh jahat kompleksitas
- Kompleksitas meresap tanpa suara seperti roh tak kasatmata, dan sering kali tidak disadari dengan baik oleh manajer proyek maupun pengembang non-Grug
- Cara terbaik untuk mencegah kompleksitas adalah dengan mengatakan "tidak"
- "Saya tidak akan membuat fitur ini"
- "Saya tidak akan memperkenalkan abstraksi ini"
- Tentu saja, dari sisi karier, berteriak "ya" mungkin lebih menguntungkan, tetapi pengembang Grug Brain lebih mementingkan pilihan yang jujur pada diri sendiri
- Bergantung pada situasinya, kompromi ("ok") juga diperlukan, dan dalam kasus seperti ini ia lebih menyukai cara menyelesaikan masalah secara sederhana dengan solusi 80/20 (menerapkan hukum Pareto)
- Tidak memberi tahu manajer proyek semuanya dan diam-diam menyelesaikannya dengan pendekatan 80/20 juga merupakan strategi yang cerdas
Struktur kode dan abstraksi
- Unit kode yang tepat (cut point) akan terlihat secara alami seiring waktu, sehingga abstraksi di tahap awal sebaiknya dihindari
- Cut point yang baik idealnya memiliki antarmuka yang sempit dengan bagian sistem lainnya
- Upaya abstraksi terlalu dini mudah gagal, dan pengembang berpengalaman biasanya mencoba menata struktur perlahan setelah bentuk kode cukup mapan
- Pengembang yang kurang berpengalaman atau yang "big brain" cenderung mencoba abstraksi berlebihan di awal proyek dan meninggalkan beban pemeliharaan
Strategi pengujian
- Penting untuk menjaga keseimbangan dalam obsesi terhadap pengujian
- Ia lebih suka menulis pengujian setelah prototyping selesai dan kode sudah agak stabil
- Unit test dipakai di tahap awal, tetapi dalam praktiknya tahap menengah (integration test) memberi dampak paling besar
- End-to-end test juga diperlukan, tetapi jika terlalu banyak akan menjadi mustahil dirawat, jadi pertahankan hanya sedikit jalur yang benar-benar penting
- Saat ada laporan bug, perbaiki bug itu hanya setelah menambahkan pengujian reproduksi
Proses, Agile, dan refactoring
- Agile tidak buruk bagi pengembang Grug, dan juga bukan yang terburuk, tetapi menaruh harapan berlebihan pada "dukun Agile" itu berbahaya
- Prototyping, alat, dan rekan kerja yang baik sebenarnya adalah faktor keberhasilan yang lebih penting
- Refactoring juga kebiasaan yang baik, tetapi refactoring besar dan dipaksakan itu berisiko
- Memaksakan abstraksi yang rumit justru dapat menyebabkan proyek gagal
Pemeliharaan, perfeksionisme, dan kerendahan hati
- Membongkar sistem yang sudah ada tanpa alasan jelas itu berbahaya, dan menghapus begitu saja "struktur yang tidak diketahui alasan keberadaannya" adalah kebiasaan yang buruk
- Idealisme yang memimpikan kode sempurna pada kenyataannya sering menimbulkan masalah
- Semakin banyak pengalaman, semakin terasa bahwa "kode yang bekerja harus dihormati"
Alat dan produktivitas
- Alat pengembangan yang baik (pelengkapan kode IDE, debugger, dll.) sangat meningkatkan produktivitas, dan penting untuk memahaminya secara mendalam
- Ditekankan bahwa nilai nyata dari type system terletak pada "auto-completion" dan pencegahan kesalahan, sementara abstraksi berlebihan dan generic justru berbahaya
Gaya kode dan pengulangan
- Disarankan gaya seperti memecah ekspresi kondisi menjadi beberapa baris demi kode yang lebih mudah dibaca dan di-debug
- Prinsip DRY (Don’t Repeat Yourself) tetap dihormati, tetapi yang penting adalah keseimbangan, bukan memaksakan penghapusan kode berulang
- Dalam banyak situasi, pengulangan sederhana lebih baik daripada implementasi DRY yang rumit
Prinsip desain perangkat lunak
- Daripada prinsip SoC (Separation of Concerns), ia lebih memilih locality of behavior, dengan argumen bahwa "kode yang menjalankan perilaku itu harus berada di objek tersebut agar lebih mudah dipelihara"
- Callback/closure, type system, generic, abstraksi, dan sebagainya sebaiknya hanya digunakan secukupnya dan secara tepat
- Penyalahgunaan closure dapat menciptakan "callback hell" di JavaScript
Logging, operasional
- Logging sangat penting; log perlu ditinggalkan di setiap percabangan utama, dan di lingkungan cloud perlu disusun agar bisa dilacak dengan request ID dan semacamnya
- Jika bisa memanfaatkan level log dinamis dan log per pengguna, itu sangat membantu pelacakan masalah saat operasional
Konkruensi, optimisasi
- Untuk konkruensi, ia hanya mempercayai model yang sesederhana mungkin (request web tanpa state, worker queue yang dipisahkan, dll.)
- Optimisasi sebaiknya benar-benar dilakukan hanya setelah memperoleh data profile kinerja yang nyata
- Perlu berhati-hati terhadap biaya tersembunyi seperti network I/O, dan berbahaya jika hanya melihat kompleksitas CPU semata
Desain API
- API yang baik harus mudah digunakan, dan desain atau abstraksi yang terlalu rumit merusak pengalaman pengembang
- Disarankan struktur berupa "API sederhana yang cocok untuk use case utama" dan "API berlapis yang tetap memungkinkan kasus rumit diimplementasikan"
Pengembangan parser
- Recursive descent parser memang kurang dihargai di kalangan akademis, tetapi merupakan metode yang paling cocok dan paling mudah dipahami untuk kode produksi nyata
- Berdasarkan sebagian besar pengalaman pengembangan parser, parser yang dihasilkan oleh tool justru terlalu rumit sehingga menjadi penghambat pemecahan masalah
- Buku yang paling direkomendasikan adalah "Crafting Interpreters", yang memuat banyak nasihat praktis
Frontend dan tren
- Frontend modern (React, SPA, GraphQL, dll.) justru memanggil lebih banyak roh jahat kompleksitas dan sering kali tidak perlu
- Grug sendiri lebih menyukai cara mengurangi kompleksitas dengan alat yang sederhana seperti htmx dan hyperscript
- Di frontend memang terus muncul percobaan baru, tetapi perlu diingat bahwa banyak di antaranya hanya pengulangan ide lama
Faktor psikologis, sindrom impostor
- Kebanyakan pengembang sering merasa "saya tidak tahu apa yang sedang saya lakukan", dan mereka perlu lebih bebas dari fenomena FOLD (Fear Of Looking Dumb)
- Jika pengembang senior secara terbuka mengatakan "ini juga sulit buat saya, ini terlalu rumit", pengembang junior juga bisa melepaskan bebannya
- Sindrom impostor adalah perasaan yang umum, dan pembaca didorong bahwa mereka tetap bisa tumbuh sambil terus belajar
Kesimpulan
- Dalam pemrograman, kompleksitas harus selalu diwaspadai, dan menjaga kesederhanaan adalah inti dari pengembangan yang sukses
- Pengalaman, pemanfaatan alat secara efektif, kerendahan hati, dan penghormatan terhadap kode yang benar-benar bekerja akan menghasilkan pengembangan yang efisien dan bernilai dalam jangka panjang
- "Kompleksitas sangat, sangat buruk"—kalimat ini harus selalu diingat
1 komentar
Komentar Hacker News
print. Bahkan saat saya mencoba menunjukkan workflow saya ke rekan kerja, reaksinya nyaris tidak ada. Saya setuju bahwa titik awal terbaik untuk memahami sistem adalah debugger. Berhenti di baris kode yang menarik saat pengujian lalu melihat stack jauh lebih mudah daripada mengikuti kode di kepala. Kalau sudah belajar memakai debugger, itu seperti mendapat superpower kecil yang nyata. Kalau bisa, saya benar-benar merekomendasikan untuk mencobanyaprintadalah satu-satunya pilihan yang memungkinkan. Bahkan kalau sistem logging juga bermasalah, atau program keburu crash sebelum sempat mengeluarkan log, makaprintpun tidak bisa dipakaiprintdan kode self-checking. Menambahkanprintjauh lebih cepat daripada masuk langkah demi langkah dengan debugger. Selain itu, kodeprinttetap tinggal di program, sedangkan sesi debugging menghilang." Saya juga setuju dengan pandangan ini. Dalam sebagian besar proses development, loopprint-hipotesis-jalankan memberi penyelesaian masalah yang jauh lebih cepat. Bukan berarti saya "menjalankan" kode itu di kepala, melainkan saya sudah punya model kerja atas alur kode, sehingga ketikaprintmenunjukkan keluaran yang salah, biasanya saya bisa cepat menangkap kenyataan yang sebenarnya. Tautan terkait: The unreasonable effectiveness of print debuggingprintfselalu umum di dunia Linux adalah karena environment GUI debugger tidak bisa diandalkan. GUI Linux sering tidak stabil sehingga sulit dipercaya. Buat saya sendiri, saya mulai benar-benar memakai debugger ketika (1) GUI di Windows berjalan baik tetapi CLI sering rusak, dan (2) saya beberapa kali mengalami kode debuggingprintikut masuk ke versi rilis dan menimbulkan masalah. Setelah itu saya menjalani berbagai petualangan dengan debugger CLI, dan merasa proses Junit+debugger (berbasis IDE seperti Eclipse), di mana saya bisa langsung menulis kode eksperimental lalu meninggalkannya sebagai test, senyaman Python REPL. Tentu, ada investasi awal untuk menyiapkan debugger agar cocok dengan environment