Pengembang Grug Brained (2022)
(grugbrain.dev)- Melalui pengalaman seorang pengembang “otak kecil” yang telah lama melakukan pemrograman, ditekankan bahwa musuh terbesar dalam pengembangan perangkat lunak adalah kompleksitas, dan sikap untuk menguranginya harus menjadi pusat praktik kerja nyata
- Senjata dasar untuk mencegah kompleksitas adalah mengatakan “no”, dan ketika kompromi diperlukan, solusi yang realistis dicari melalui solusi 80/20, prototipe, refaktorisasi kecil, dan abstraksi yang ditunda
- Untuk pengujian, setelah kode cukup matang, integration test dijadikan pusat, dengan unit test dan sejumlah kecil end-to-end test sebagai pendukung, dan bug diperbaiki setelah terlebih dahulu direproduksi melalui regression test
- Alat, sistem tipe, logging, debugger, dan API yang sederhana mengurangi beban ingatan dan penalaran pengembang, tetapi generic, callback, microservice, dan framework frontend yang berlebihan dapat menambah kompleksitas
- Kode yang sudah ada dan proses organisasi perlu didekati dengan rendah hati, kode yang belum dipahami tidak boleh dihapus sembarangan, dan dibutuhkan budaya yang memungkinkan orang berkata, “ini terlalu kompleks”
Kompleksitas adalah musuh abadi pengembang
- Musuh paling berbahaya dalam pengembangan perangkat lunak adalah kompleksitas
- Kompleksitas masuk perlahan ke dalam codebase, dan membuat perubahan di satu tempat merusak bagian lain yang tampaknya tidak terkait
- Grug menyamakannya dengan “iblis” yang tak terlihat, sesuatu yang tidak bisa dilihat langsung atau dibasmi dengan mudah oleh pengembang
- Kompleksitas juga bisa masuk melalui pengembang atau manajer proyek yang berniat baik
- Semakin banyak fitur, abstraksi, dan proses, semakin sulit kode dipahami
- Grug sendiri mengakui bahwa kadang-kadang dialah orang yang membawa masuk kompleksitas itu
Cara memakai “no” dan “ok”
- Senjata paling kuat untuk mencegah kompleksitas adalah “no”
- Jangan membuat fitur yang tidak diperlukan
- Jangan membuat abstraksi yang tidak diperlukan
- Namun, meskipun “no” adalah nasihat rekayasa yang baik, itu tidak selalu menguntungkan secara karier
- “yes” bisa berujung pada lebih banyak imbalan atau posisi manajerial
- Meski begitu, jika ingin jujur pada diri sendiri sebagai pengembang, “no” itu penting
- Saat kompromi diperlukan, jawab dengan “ok” lalu cari solusi 80/20
- 80/20 solution adalah pendekatan yang memenuhi 80% kebutuhan dengan 20% kode
- Mungkin tidak memiliki semua fitur hiasan, tetapi tetap bisa memberikan sebagian besar nilai dan menahan kompleksitas
- Kadang lebih baik tidak menjelaskan semua detail implementasi kepada manajer proyek dan menyelesaikannya dengan cara yang sederhana
Menyusun struktur kode dan refaktorisasi
- Pada tahap awal proyek, aplikasi tidak boleh dipecah terlalu cepat
- Di awal, bentuk sistem masih belum jelas, dan apa yang sedang dibuat juga belum sepenuhnya dipahami
- Seiring waktu, cut point yang baik akan terlihat
- Cut point yang baik memiliki antarmuka yang sempit dengan sisa sistem
- Menyembunyikan kompleksitas internal dengan sejumlah kecil fungsi atau abstraksi
- Grug menyamakannya dengan mengurung iblis kompleksitas di dalam kandang kecil
- Saat pengembang berotak besar ingin membuat banyak abstraksi di awal proyek, dibutuhkan cara untuk mengurangi dampaknya
- Itu bisa dialihkan ke hasil kerja seperti diagram UML yang tidak langsung merusak kode
- Jika meminta “demo yang berfungsi besok”, mereka akan lebih cepat melihat kode yang benar-benar berjalan dan realitasnya
- Pendekatan demo ini bisa disebut prototipe
- Refaktorisasi berguna di tahap akhir proyek setelah kode mulai matang
- Semakin besar refaktorisasi, semakin besar kemungkinan gagal
- Ia lebih menyukai langkah-langkah kecil sambil menjaga sistem tetap berjalan
- End-to-end test menjadi penyelamat selama refaktorisasi, tetapi saat rusak, penyebabnya bisa sulit dipahami
- Abstraksi yang berlebihan dapat menyebabkan kegagalan refaktorisasi dan kegagalan sistem
- J2EE disebut sebagai contoh abstraksi yang berlebihan
- Penerapan OSGi dimaksudkan untuk mengurangi kompleksitas, tetapi justru menciptakan kompleksitas yang lebih kuat dan membutuhkan pengerjaan ulang selama bertahun-tahun
Strategi pengujian
- Test sangat menghemat waktu, tetapi ia skeptis terhadap pendekatan “selalu test dulu”
- Jika domain belum dipahami, sulit mengetahui apa yang harus diuji
- Grug lebih suka menulis sebagian besar test setelah prototipe, saat kode mulai matang
- Meski test ditulis belakangan, tetap dibutuhkan disiplin
- Test tidak boleh dilewati hanya karena “di mesin saya berjalan”
- Tidak ada jaminan itu akan berjalan di mesin lain atau di mesin yang sama pada masa depan
- Peran unit test, integration test, dan end-to-end test dibedakan
- unit tests membantu di awal proyek, tetapi mudah rusak saat implementasi berubah dan bisa mempersulit refaktorisasi
- end to end test menunjukkan perilaku seluruh sistem, tetapi saat rusak sulit mencari penyebabnya, dan jika terlalu sering rusak bisa diabaikan
- integration tests cukup tinggi untuk memverifikasi ketepatan sistem, tetapi cukup rendah untuk melihat penyebabnya lewat debugger, sehingga mendekati “sweet spot”
- Bentuk ideal susunan test adalah sebagai berikut
- Di awal, beberapa unit test
- Semakin sistem stabil dan cut point muncul, integration test yang kuat
- End-to-end test yang kecil dan terkelola baik, hanya mencakup fungsi UI yang paling umum dan edge case penting
- Mocking jarang digunakan, dan bila perlu pun ia lebih memilih memakainya hanya pada unit besar seperti batas sistem
- Saat memperbaiki bug, sebagai pengecualian, bug terlebih dahulu direproduksi dengan regression test lalu diperbaiki
Proses, agile, dan sikap terhadap kode lama
- Agile bukan yang terburuk, tetapi juga sulit disebut sepenuhnya baik
- Sebagai cara mengorganisasi tim pengembangan, ia bisa cukup layak
- Ia waspada terhadap pakar agile yang setiap kali gagal berkata, “agile-nya tidak dilakukan dengan benar”
- Untuk keberhasilan, prototipe, alat, dan perekrutan pengembang yang baik lebih penting
- Proses agile bisa membantu, tetapi jika dianggap terlalu serius justru bisa merugikan
- no silver club, yakni tidak ada peluru perak yang menyelesaikan semua masalah perangkat lunak
- Chesterton’s Fence digunakan sebagai peringatan terhadap penghapusan kode yang sudah ada
- Jika tidak tahu kegunaan suatu pagar, harus memahaminya dulu dan tidak langsung menyingkirkannya
- Bahkan kode yang jelek pun perlu dihormati jika hari ini masih berfungsi
- Terutama pada sistem besar, pahami dulu baru perbaiki
- Test bisa menjadi petunjuk tentang mengapa suatu “pagar” itu ada
Alat dan sistem tipe
- Alat mengurangi beban yang harus diingat dan ditalar langsung oleh pengembang
- Saat masuk ke lingkungan baru, meluangkan waktu untuk mempelajari alat di sekitarnya bisa meningkatkan produktivitas
- Jika tidak ada dokumentasi, mungkin perlu bertanya pada pengembang lain untuk memahaminya
- Code completion di IDE membuat pengembang tidak perlu mengingat seluruh API
- Ia menggambarkan pemrograman Java hampir mustahil tanpa code completion
- Debugger yang baik sangat penting
- Fitur seperti conditional breakpoint, evaluasi ekspresi, dan penelusuran stack perlu dipelajari secara mendalam
- Menurutnya, mempelajari debugger bisa mengajarkan pengembang baru lebih banyak tentang komputer daripada kuliah di universitas
- Nilai terbesar sistem tipe adalah “saat menekan titik (.), hal-hal yang bisa dilakukan langsung muncul”
- Ketepatan tipe itu bagus, tetapi bagi Grug dukungan alat dan code completion lebih bernilai
- Abstraksi tipe atau generic yang berlebihan dapat membuat kode kerja nyata lebih sulit
- Ia lebih suka membatasi penggunaan generic terutama pada kelas container
Ekspresi, DRY, dan separation of concern
- Ia lebih menyukai kode yang mudah di-debug daripada kode yang pendek
- Daripada menulis ekspresi kondisi kompleks dalam satu baris, memecahnya menjadi variabel antara membuat hasil dan makna tiap ekspresi lebih mudah dilihat
- Meski jumlah baris bertambah, pemahaman dan debugging terhadap kondisi jadi lebih mudah
- DRY adalah nasihat yang baik, tetapi perlu keseimbangan
- Duplikasi yang sederhana dan jelas bisa lebih baik daripada callback, closure, atau model objek yang kompleks
- Jika menghilangkan duplikasi justru menambah kompleksitas, hasilnya bisa merugikan
- Ia lebih kritis terhadap Separation of Concern
- Contoh khas dalam pengembangan web adalah pemisahan CSS, HTML, dan JavaScript
- Sebagai alternatif, Grug lebih menyukai locality of behavior
- Jika kode terkait diletakkan dekat dengan objek yang berperilaku, saat melihat objek itu kita bisa langsung tahu apa yang dilakukannya
- Closure berguna untuk penggunaan yang tepat seperti abstraksi operasi koleksi
- Namun seperti garam, sistem tipe, dan generic, sedikit saja sudah cukup, terlalu banyak justru merugikan
- “callback hell” di JavaScript disebut sebagai contoh penyalahgunaan closure
Logging, konkurensi, dan optimisasi
- Logging sangat penting, terutama di lingkungan deployment cloud
- Ia menulis log pada setiap percabangan logika utama
- Jika satu request melintasi beberapa mesin, semua log diberi request ID agar bisa dikelompokkan
- Jika memungkinkan, level log diatur secara dinamis
- Jika memungkinkan, level log juga diatur per pengguna
- Library logging Java bisa rumit, tetapi investasi yang tepat pada infrastruktur logging akan memberi imbal hasil besar nantinya
- Konkurensi adalah sesuatu yang harus ditakuti
- Sebisa mungkin, gunakan model sederhana seperti request handler web tanpa state dan antrean pekerjaan jarak jauh yang independen
- optimistic concurrency dinilai bekerja baik di ranah web
- thread local variable kadang dipakai terutama saat menulis kode framework
- Struktur data konkurensi seperti ConcurrentHashMap di Java pun tetap harus digunakan dengan hati-hati
- Optimisasi sebaiknya dimulai hanya ketika ada profil performa yang nyata
- Ia setuju dengan “premature optimization is the root of all evil”
- Bottleneck yang sebenarnya bisa berbeda dari perkiraan
- Jangan hanya melihat CPU; pemanggilan jaringan bisa setara dengan jutaan siklus CPU, jadi sebisa mungkin harus dikurangi
API, parsing, dan visitor pattern
- API yang baik membuat pengembang tidak perlu banyak berpikir
- API yang buruk muncul ketika dirancang berdasarkan detail implementasi internal atau model domain, atau ketika terlalu abstrak
- Untuk kasus sederhana, ia menyukai API yang sederhana, dan untuk kasus kompleks, API yang lebih kompleks, yaitu pendekatan berlapis
- Untuk API berorientasi objek, ia menilai lebih baik menempelkan fungsi pada objek sasarannya
- Ia memberi contoh buruk di Java: untuk memfilter list harus mengubahnya menjadi Stream lalu mengumpulkannya kembali menjadi List
- Menurutnya, operasi umum seperti
filter()seharusnya ada di list dan mengembalikan list
- Parser recursive descent dianggap sebagai cara yang menyenangkan dan indah
- Parser generator dikritik karena sulit dipahami dan sulit di-debug
- Ia mengklaim parser produksi nyata hampir selalu menggunakan pendekatan recursive descent
- Ia merekomendasikan Crafting Interpreters karya Bob Nystrom
- Bisa dibaca gratis secara online, tetapi ia juga merekomendasikan membeli bukunya
- Namun, visitor pattern disebut sebagai jebakan
- Penilaiannya terhadap Visitor pattern singkat saja: “buruk”
Frontend, microservice, dan tren
- Microservice berbahaya karena “menambahkan pemanggilan jaringan ke masalah paling sulit, yaitu membagi sistem dengan benar”
- Dalam pengembangan frontend, kompleksitas terasa sangat kuat
- Ia mengkritik situasi ketika bahkan penyimpanan form sederhana atau situs brosur memakai library SPA, GraphQL JSON API, dan backend HTTP
- Ia menggambarkan bahwa memisahkan frontend dan backend menciptakan dua tempat tinggal bagi kompleksitas
- Grug menjelaskan bahwa ia membuat htmx dan hyperscript untuk menurunkan kompleksitas
- Ia lebih menyukai mempertahankan HTML yang sederhana dan tidak banyak memakai JavaScript
- Ia mengakui React bisa lebih baik untuk pekerjaan dan jenis aplikasi tertentu
- Dalam pengembangan ada banyak tren, terutama di frontend
- Ia mengatakan backend sudah menjadi lebih membosankan, dan banyak ide buruk sudah pernah dicoba
- Ia menyarankan agar pendekatan baru yang terdengar revolusioner dihadapi dengan hati-hati, seperti menaburkan sebutir garam
- Banyak ide sudah pernah dicoba setidaknya sekali, dan ide buruk daur ulang bisa membuang waktu
Ketakutan dan sindrom penipu
- Adalah hal yang baik ketika pengembang senior secara terbuka berkata, “ini terlalu kompleks”
- Pengembang sering sulit mengakui bahwa mereka tidak mengerti karena Fear Of Looking Dumb(FOLD)
- Jika senior lebih dulu mengakuinya, junior juga bisa mengatakan bahwa sesuatu itu kompleks dan belum mereka pahami
- FOLD adalah salah satu sumber utama yang membuat kompleksitas makin berkuasa, terutama bagi pengembang muda
- Menurutnya, selera humor dan sikap mengingat kegagalan masa lalu bisa membantu
- Sindrom penipu juga umum dalam pengembangan
- Grug mengatakan dirinya berada di antara perasaan menguasai segalanya dan perasaan tidak tahu apa yang sedang dilakukan
- Meski ada pencapaian open source dari htmx dan _hyperscript, ia masih takut membuat kesalahan dan gagal
- Jika semua orang merasakan sindrom penipu, mungkin lebih baik menerimanya dengan anggapan bahwa tidak ada yang benar-benar palsu
Rekomendasi bacaan dan kesimpulan
- Rekomendasi bacaan
- Kesimpulan terakhirnya sederhana
- Kompleksitas itu sangat, sangat buruk
1 komentar
Komentar Hacker News
Jika Profesor Carson membaca komentar ini, saya ingin menyampaikan terima kasih yang tulus atas semua kontribusi yang telah Anda berikan selama ini
Saat kuliah saya tidak tahu mengapa kami mempelajari HTMX, atau mengapa Anda begitu antusias tentangnya, tetapi setelah beberapa tahun berlalu sekarang saya mengerti. Semuanya tentang HTML over the wire
Saat bekerja sebagai Staff Ruby on Rails Engineer, saya melihat karya Anda di Hotwire, dan menyenangkan juga melihat Anda sesekali muncul di Hacker News atau berbincang dengan para developer Hotwire di GitHub. Anda sangat dihormati dan diapresiasi sebagai sosok yang menerangi komunitas pemrograman
Saya setuju dengan ungkapan “debugger yang bagus sama berharganya dengan batu berkilau, bahkan sebenarnya lebih berharga dari itu”
Saya pernah berada di startup kecil maupun tim Big Tech ‘elite’, tetapi hampir selalu hanya saya yang memakai debugger di tim. Di dunia nyata, setidaknya di ranah teknologi web, sepertinya kebanyakan orang melakukan debugging dengan pernyataan
printBerhenti di baris kode yang menarik saat pengujian berjalan lalu melihat call stack yang membawa eksekusi sampai ke sana jauh lebih mudah daripada menjalankan kode maju di dalam kepala. Untuk grug muda, teknik ini adalah semacam kekuatan super kecil, jadi kalau bisa, layak meluangkan waktu agar teknik ini bisa berjalan di codebase sendiri
Mereka mengatakan bahwa mereka tidak banyak menggunakan debugger selain untuk stack trace atau melihat nilai satu-dua variabel, karena mudah terjebak dalam detail struktur data dan alur kontrol yang kompleks, dan mereka menganggap memasukkan pernyataan output serta kode self-checking di titik-titik penting lebih produktif.
Saya juga pada umumnya setuju. Untuk hampir semua pekerjaan yang saya lakukan, siklus hipotesis-log-jalankan membawa saya ke jawaban jauh lebih cepat. Bukan berarti saya berusaha menjalankan kode di kepala; saya sudah punya model kerja tentang bagaimana kode berjalan, tahu output seperti apa yang seharusnya muncul jika model itu benar, dan dari output yang salah saya bisa cepat mengintuisi keadaan sebenarnya
[0] The unreasonable effectiveness of print debugging (349 points, 354 comments) April 2021 https://news.ycombinator.com/item?id=26925570
Dalam arsitektur microservices mesh, menjalankan sesuatu dengan benar secara lokal saja sulit, dan lingkungan pengujian juga sering tidak disiapkan agar bisa dipasangi debugger step-through. Pada akhirnya yang tersisa hanya debugging dengan
print, dan kalau sistem logging itu sendiri bermasalah atau program mati sebelum log di-flush, itu pun tidak bisa dipakaiTidak lama kemudian pasti akan dibutuhkan. Tetapi saya terkejut ketika dalam kolaborasi pun melihat orang-orang yang tidak tahu cara memakai debugger itu sendiri. Saat saya bilang “pasang breakpoint di situ”, “sekarang masuk ke dalam fungsi dan lihat status variabelnya”, “yang itu dilewati saja”, setiap kali yang kembali hanya tatapan kosong
Secara ketat, saya bukan menambahkan lalu menghapus pernyataan
print, melainkan menambahkan kode logging yang akan tetap dibiarkan. Untuk antarmuka utama, biasanya saya mulai dengan mencatat masuk/keluar fungsi dan nilai parameter pada level INFO, lalu saat memakai sistem dan melihat bagian yang perlu diperiksa lebih lanjut, saya menambahkan log yang lebih rinciSaya juga cukup memperhatikan format log. Saat bekerja dengan sistem terdistribusi, sangat berguna untuk membuat prefiks tiap baris log benar-benar konsisten. Dengan memasukkan ID node, pid, dan timestamp semuanya dalam lebar tetap, lalu mengunduh dan mengurutkan log seluruh klaster, perilaku beberapa node terlihat dalam satu file dalam bentuk yang saling tersisip
Namun begitu debugger masuk jauh ke dalam library atau framework yang saya pakai, saya langsung tersesat dan tidak menyukainya. Framework atau library itu adalah benda yang dibangun dari puluhan ribu jam kerja orang, sehingga terasa jauh melampaui level saya
Ada banyak kalimat seperti permata di sini, tetapi kalimat tentang microservices ini yang paling saya suka: “grug bertanya-tanya kenapa orang-orang pintar mengambil masalah tersulit, yaitu membagi sistem dengan benar, lalu malah menambahkan panggilan jaringan ke dalamnya”
Mereka dengan senang hati memecah aplikasi web sepele yang hanya punya sekitar lima form menjadi “microservices”, membuatnya berbagi database yang sama, menambahkan lapisan manajemen API, antrean untuk pekerjaan batch besar (megabyte), sistem notifikasi email, sampai platform observabilitas buatan sendiri, lalu mengubah form web sederhana menjadi SPA sambil berkata “ini lebih mudah”
Sekarang saya paham bahwa “arsitektur” dan “pattern” adalah program penciptaan lapangan kerja untuk developer yang tidak berguna. Kalau tidak begitu, mereka mungkin sudah berdiri di jalan memegang papan bertuliskan “beri saya sandwich, saya akan menulis JavaScript”
Bagi orang-orang seperti itu, kalau tidak diekspos sebagai panggilan API, itu hanya gumpalan kode buram yang tidak bisa mereka pahami atau gunakan ulang
Beberapa package bisa bekerja sama secara internal, tetapi hanya mengekspos API kecil ke sisa codebase. Fakta bahwa itu jaringan memaksa modul-modul untuk hanya bertukar data, bukan callback atau perilaku, dan menekan mereka agar mengembangkan antarmuka dengan cara yang kompatibel ke belakang. Karena itu, modul yang berbeda bisa di-“hot reload” pada waktu yang berbeda tanpa meledak
Rasanya sebagian besar manfaat itu bisa didapat tanpa hop jaringan sungguhan, tetapi saya belum melihat upaya serius untuk itu
Tanpa orkestrator seperti K8S, aplikasi itu tidak bisa dijalankan, dan karena sulit dipasang serta dioperasikan, ini menguntungkan penjualan cloud terkelola. Ia membuat orang memakai lebih banyak bandwidth jaringan dan CPU, dan keduanya dikenai biaya
Ia membuat berbagi dan mempertahankan state yang kompleks atau besar di dalam aplikasi menjadi sulit, sehingga layanan database terkelola atau event queue dipakai sebagai pengganti. Kalau monolith, cukup pakai queue atau channel, tetapi di microservices orang jadi menginginkan monster raksasa seperti Kafka
Menjalankannya secara lokal juga jadi sulit, sehingga perlu lingkungan pengembangan cloud, dan mungkin harus punya beberapa lingkungan pengembangan serta pengujian. Ketergantungan pada karakteristik tertentu seperti cara networking cloud tertentu juga meningkat, sehingga vendor lock-in cloud makin besar
Entah masih ingat masa ketika cloud dijual sebagai cara menurunkan biaya IT. Itu lucu, dan sejak 2000-an saya sudah tahu itu omong kosong; pada akhirnya saya yakin semua biayanya justru akan membesar
Karena cara itu lebih mudah dikelola, dan lebih dekat ke cara organisasi pengembangan daripada keputusan teknis. Kalau alternatifnya monorepo, secara pribadi saya menganggap itu lebih buruk
“Kalau harus memilih antara kompleksitas dan duel 1 lawan 1 melawan tyrannosaurus, grug memilih tyrannosaurus. Setidaknya grug bisa melihat tyrannosaurus”
Saya memikirkan kalimat ini setidaknya sekali seminggu
grug yang ini sedang 1 lawan 1 dengan tyrannosaurus transparan, dan dia terkutuk
Salah satu nilai tulisan ini adalah bahwa orang yang mampu melakukan hal yang lebih canggih dan kompleks berdasarkan pengalaman justru sengaja menghindarinya
Tentu ada waktu dan tempat ketika kecanggihan dan abstraksi yang lebih tinggi dibutuhkan. Namun filosofi grug mengatakan bahwa melakukan hal semacam itu sendiri tidak punya nilai intrinsik, dan itu terdengar seperti nasihat yang cukup masuk akal
Saya juga melihat bantuan AI lebih efektif pada kode yang konsisten, biasa-biasa saja, dan berpusat pada data. Tergantung situasinya, tentu saja
Developer pemula menulis kode sederhana, developer menengah menulis kode kompleks, dan developer ahli kembali menulis kode sederhana
Kuliah apa yang bisa diasumsikan pernah didengar orang tentu berbeda-beda menurut konteks
Salah satu ironi dalam pengembangan perangkat lunak modern adalah kita memperkenalkan kompleksitas karena berpikir “pada akhirnya ini akan menghemat waktu”
Kadang itu benar dan memang menghemat waktu, tetapi tidak selalu begitu, dan mungkin juga tidak sering
DRY kadang berujung pada abstraksi yang tergesa-gesa. Begitu kita berpikir “pola ini juga akan dipakai di tempat lain, jadi bagian yang sama harus diekstrak”, iblis kompleksitas masuk
Kita ingin menangkap sebanyak mungkin bug pada saat kompilasi, tetapi untuk itu compiler harus tahu lebih banyak tentang apa yang sebenarnya ingin kita lakukan, dan kita akhirnya membuat tipe-tipe rumit yang menggerogoti pemahaman
Demi menghindari boilerplate, kita membuat macro atau DSL yang rumit, tetapi karena hukum abstraksi bocor, saat tiba momen kita harus tahu implementasi sebenarnya, kepala rasanya mau pecah
Yang sulit adalah semua contoh ini kadang memang ide yang bagus. Menurut saya, mampu menilai kapan kompleksitas harus diperkenalkan agar justru menjadi penyederhanaan adalah tanda software engineer yang baik
Logika bisnis idealnya didefinisikan di satu tempat, tetapi hal-hal lain boleh diduplikasi bila perlu, dan itu sendiri bukan hal buruk
Untuk mengendalikan DRY, saya juga menekankan “aturan 3”. Kode copy/paste masih oke sampai tiga kali; setelah itu barulah abstraksi layak dipertimbangkan. Tentu tidak ada aturan praktis yang cocok untuk semua kasus, dan intuisi itu sulit diajarkan
Biasanya saya membuat web app dengan framework bergaya Sinatra seperti Flask, dan menulis fungsi yang merespons pola URL. Satu “layar” bisa terdiri dari satu atau lebih fungsi yang bekerja bersama dan template HTML terkait
Jika aplikasi menyiapkan hal-hal seperti koneksi database, lokasi file, header dan footer HTML, selebihnya hampir tidak ada coupling antar-layar. Jika butuh layar baru, kita bisa menyalin layar yang ada lalu mengubahnya, atau meminta LLM membuat layar atau endpoint; kalau hasilnya buruk, buat ulang saja
Di tempat kerja lama, kami sedang membuat framework bernama Themis untuk membuat set pelatihan ML, menggunakan microservices, React, Docker, dan sebagainya. Kebutuhan sebenarnya adalah terus menambahkan tugas baru dan cepat membuat layar yang sederhana tetapi sangat dioptimalkan untuk tiap tugas. Kalau harus membuat 20.000 penilaian, satu klik tiap kali saja sudah berat; kalau empat klik tiap kali, menjadi 80.000 klik dan orang akan menyerah
Dengan struktur waktu itu, kami harus menulis endpoint API untuk aplikasi JAXB dan komponen dalam aplikasi React monolitik, lalu menunggu TypeScript, Docker, dan javac berjalan selama 20 menit. Kalau beruntung, aplikasi bisa boot; kalau tidak, mulai dari awal lagi
Saya menulis kritik terhadap Themis dan merancang Nemesis dengan tujuan pengembangan tugas baru yang cepat, tetapi di tempat kerja lama itu adalah jalan yang tidak dipilih. Meski begitu, sejak saat itu Nemesis dan saya telah memproses jutaan instance tugas
Artinya bukan menyuruh mengekstrak helper yang hanya dipakai di satu tempat. Meski logika menumpuk dalam satu fungsi, kelas, file, dan sebagainya, selama tidak disalin, itu tetap DRY
Abstraksi tergesa-gesa memang benar-benar ada. Fakta bahwa kelas-kelas CS umumnya mengajarkan orang melakukan hal seperti itu juga tidak membantu. Beri seorang junior database MySQL, dan hal pertama yang mungkin ia lakukan adalah mencoba mengabstraksi MySQL
Entah kompleks atau sederhana, kalau hasilnya tidak menambah nilai, itu tidak penting. Fokuslah lebih dulu pada menambahkan nilai yang lebih besar daripada yang dikurangi, baru khawatirkan kompleksitas setelah itu
Salah satu cara favorit saya menggunakan LLM adalah memasukkan esai ini, lalu memintanya berkomentar tentang isu yang sedang saya tangani dengan persona grug-brained developer. Bagus untuk meredakan stres
Apakah cukup memulai prompt dengan sesuatu seperti “Bertindaklah seperti Grug Brained Developer dalam esai ini”?
“Kompleksitas sangat buruk” memang benar
Selama bertahun-tahun saya berkecimpung dalam rekayasa perangkat lunak, gagasan ini adalah salah satu dari sedikit prinsip yang terus terbukti benar dalam segala situasi. Sebagian masalah memang secara inheren kompleks, tetapi bahkan dalam kasus seperti itu, jauh lebih baik meluangkan waktu untuk sampai pada solusi yang paling sederhana
Pekerjaan saya yang paling efektif muncul setelah meragukan pendekatan sebelumnya dan menyederhanakannya secara drastis. Mungkin ada sedikit fleksibilitas potensial yang hilang, tetapi kebanyakan fleksibilitas yang saya kira dibutuhkan ternyata sebenarnya tidak diperlukan sejauh itu
Misalnya, sejak muncul LLM yang cukup bagus dan dapat bertindak secara agentic, saya menghindari tipe TypeScript yang terlalu rumit, rapuh, dan sulit di-debug; sebagai gantinya saya menulis kode seperti spesifikasi lalu meminta LLM menghasilkan kode statis berdasarkan itu
Dependensi ESLint proyek terus rusak setelah pembaruan versi, banyak aturannya tidak cukup canggih untuk menghindari false positive, dan menjaganya tetap berjalan baik dengan TypeScript serta VSCode juga rumit. Beralih ke Biome.js membuatnya lebih sederhana dan cukup efektif, tetapi belakangan mulai muncul bug. Meski begitu, saya sadar bahwa linting itu bagus kalau ada, bukan sesuatu yang harus dirawat dengan menghabiskan waktu berlebihan, jadi saya menghapusnya dari rantai build tool, dan di VSCode pun tidak perlu selalu dinyalakan. Cukup sesekali menjalankan Biome untuk memeriksa gaya kode dan format
Saat membuat tool migrasi data kustom untuk proyek, saya menilai bahwa migrasi maju memang diperlukan, tetapi migrasi balik tidak sepadan dengan waktu dan kompleksitas untuk diimplementasikan. Jika perlu rollback database yang berisi data, cukup pulihkan backup; jika tidak ada data atau bukan DB produksi, mulai dari keadaan bersih dengan skrip inisialisasi berversi
Saya juga tidak begitu paham bagaimana contoh ketiga lebih sederhana. Dengan cara berpikir matematis, ruang bijektif bisa dibuat dengan mudah. Meniru migrasi balik lewat cara lain mungkin justru lebih sulit. Tentu ini bergantung pada detail, jadi bukan aturan umum
Rich yang cerdas mengatakan complect berarti mengikat sesuatu bersama, dan saya setuju dengan itu. Rich mengatakan kompleksitas itu buruk, tetapi saya tidak setuju. Mengikat sesuatu bersama itu perlu. Jika tidak saling terhubung, masalah tidak bisa dipecahkan
Sulit percaya bahwa tulisan ini berasal dari tahun 2022
Rasanya saya bisa dengan yakin mengatakan bahwa saya membacanya 10 tahun lalu dan saat itu pun sudah menjadi klasik
Sedih tapi benar: saran karier ideal adalah belajar mengatakan “yes”, lalu jika gagal, belajar menyalahkan grug lain
Saat pertama kali masuk dunia korporat, saya pikir ini tidak benar dan masalahnya hanya kurangnya komunikasi di tim teknis. Saya salah, dan grug benar