4 poin oleh GN⁺ 2024-10-30 | 1 komentar | Bagikan ke WhatsApp
  • Karena kode menimbulkan biaya pemeliharaan sejak saat ditulis, struktur yang mudah dihapus atau diganti di kemudian hari sering kali lebih penting daripada reusability
  • Semakin banyak pengguna API, semakin besar biaya perubahan; semakin dalam ketergantungan pada API pihak ketiga, semakin besar codebase terguncang oleh perubahan eksternal
  • Duplikasi, boilerplate, layering, bongkahan kode besar, pemisahan modul, dan feature flag semuanya bisa menjadi alat manajemen dependensi, tergantung situasinya
  • Pemisahan yang baik bukan sekadar mengelompokkan fungsi umum, melainkan lebih mirip menyembunyikan keputusan desain yang sulit diubah atau berpeluang besar berubah dari satu sama lain
  • Kode yang baik bukan kode yang sempurna sejak awal, melainkan kode legacy yang semakin lama semakin tidak mengganggu; pada akhirnya, itu adalah kode yang mudah dihapus

Kode adalah biaya, dan penghapusan adalah penghematan biaya

  • Semua kode menciptakan biaya pemeliharaan sejak saat ditulis; reuse dapat mengurangi jumlah kode, tetapi bisa membuat kita lebih sulit mengubah pikiran nanti
  • Semakin banyak kode yang menggunakan sebuah API, semakin besar biaya penulisan ulang saat API itu berubah
  • Semakin banyak kita bergantung pada API pihak ketiga, semakin besar pula dampak yang diterima saat API tersebut berubah
  • Dalam sistem berskala besar, seiring waktu semakin sulit memahami bagaimana kode saling terkait dan bagian mana bergantung pada bagian mana
  • Jika jumlah baris kode dilihat bukan sebagai “baris yang diproduksi” melainkan sebagai “biaya yang dikeluarkan”, menghapus kode berarti menurunkan biaya pemeliharaan
  • Tujuannya bukan hanya membuat perangkat lunak yang bisa digunakan ulang, tetapi membuat perangkat lunak yang bisa dibuang

Tahap 0: Jangan menulis kode

  • Jumlah baris kode memang tidak menjelaskan segalanya, tetapi skala seperti 50, 500, 5.000, 10.000, atau 25.000 baris tetap penting
  • Monolith 1 juta baris membutuhkan lebih banyak waktu, biaya, dan usaha untuk diganti dibanding monolith 10 ribu baris
  • Semakin banyak kode, semakin sulit dihilangkan; tetapi mengurangi satu baris kode saja hampir tidak menghemat apa pun
  • Kode yang paling mudah dihapus adalah kode yang tidak pernah ditulis sejak awal

Tahap 1: Lakukan copy-paste

  • Kode yang reusable lebih mudah dibuat setelah beberapa use case benar-benar muncul, bukan dengan menebak penggunaan masa depan sejak awal
  • Mencoba copy-paste beberapa kali di codebase membantu memahami cara penggunaan yang sebenarnya
  • Begitu suatu kode dijadikan API bersama, kode itu menjadi sulit diubah
  • Kode yang memanggil fungsi tidak hanya bergantung pada perilaku yang terdokumentasi, tetapi juga pada perilaku sengaja maupun tidak sengaja yang teramati dari implementasinya
  • Menghapus kode di dalam sebuah fungsi lebih sederhana daripada menghapus fungsi itu sendiri

Tahap 2: Berhenti copy-paste

  • Kode yang sudah cukup sering berulang sudah saatnya dinaikkan menjadi fungsi
  • Ini mencakup kode utility yang sering dibutuhkan di atas standard library, seperti kode yang membuka file konfigurasi lalu mengembalikan hash table, atau kode untuk menghapus direktori
  • util lebih baik dibuat sebagai direktori daripada satu file, dengan utility berbeda ditempatkan di file berbeda
    • Satu file util akan terus membesar, dan setelah terlalu besar akan sulit dipecah
  • Kode yang kurang spesifik terhadap aplikasi atau proyek lebih mudah digunakan ulang dan lebih kecil kemungkinan berubah atau dihapus
    • Ini mencakup kode library seperti logging, API pihak ketiga, file handle, dan process
    • List, hash table, dan collection jarang dihapus bukan hanya karena interface-nya sederhana, tetapi juga karena cakupannya tidak membesar seiring waktu
  • Intinya adalah menempatkan bagian yang sulit dihapus sejauh mungkin dari bagian yang mudah dihapus

Tahap 3: Tulis lebih banyak boilerplate

  • Membuat library dapat menghindari copy-paste, tetapi dalam praktiknya kita sering menulis banyak boilerplate untuk menggunakan library tersebut
  • Boilerplate mirip copy-paste karena setiap kali kita mengubah bagian yang sedikit berbeda di tempat berbeda
  • Duplikasi seperti ini adalah cara menerima verbositas sebagai harga untuk mengurangi dependensi dan mendapatkan fleksibilitas
  • Library yang membutuhkan boilerplate sering muncul saat sulit mencampur policy dan protocol, seperti network protocol, wire format, dan parsing tool
    • Protocol menangani apa yang bisa dilakukan program
    • Policy menangani apa yang harus dilakukan program
  • Kode seperti ini sering merupakan kebutuhan untuk berkomunikasi dengan komputer lain atau menangani file lain, sehingga sulit dihapus
  • Penting untuk tidak menyebarkan business logic ke dalam kode seperti ini
  • Walaupun harus menulis lebih banyak baris, lebih baik menulis baris itu di bagian yang mudah dihapus

Tahap 4: Jangan menulis boilerplate

  • Jika boilerplate sudah terlalu banyak, sudah saatnya membuat library yang membungkus library fleksibel dengan opini tentang policy, workflow, dan state
  • Membuat API yang mudah digunakan mirip dengan melibrary-kan boilerplate
  • Python HTTP client requests adalah contoh yang menyediakan interface sederhana di atas urllib3 yang lebih verbose
    • requests menangani workflow umum penggunaan HTTP dan menyembunyikan detail praktis
    • urllib3 menyediakan pipelining, connection management, dan lainnya tanpa menyembunyikan detail dari pengguna
  • Membungkus satu library dengan library lain bukan hanya soal menyembunyikan detail, tetapi juga separation of concerns
  • Sebaiknya jangan menaruh business logic di direktori util; lebih baik menumpuk library yang mudah digunakan di atas library yang sederhana untuk diimplementasikan
  • Kadang library pihak ketiga juga sebaiknya dibungkus
    • Agar seluruh proyek tidak terkunci pada pilihan tertentu, kita bisa membuat library yang sesuai dengan kode kita sendiri
  • API yang nyaman digunakan dan API yang mudah diperluas sering kali saling bertentangan
  • Layering lebih dekat pada membuat kode yang sulit dihapus menjadi nyaman digunakan tanpa mencemari business logic, daripada menulis kode yang akan dihapus nanti

Tahap 5: Tulis bongkahan kode besar

  • Walaupun kita melakukan copy-paste, refactoring, layering, dan composition, pada akhirnya kode harus melakukan sesuatu; kadang kita membutuhkan bongkahan kode besar yang menahan bagian lainnya tetap bersama
  • Business logic dapat ditandai oleh edge case tanpa akhir dan hack cepat
  • Kode game atau kode founder juga bisa dilihat sebagai jenis kode yang sama, yang mengambil jalan pintas demi menghemat banyak waktu
  • Kadang lebih mudah menghapus satu kesalahan besar daripada menghilangkan 18 kesalahan kecil yang saling terkait
  • Banyak pemrograman bersifat eksploratif, sehingga beberapa kali salah dan beriterasi bisa lebih cepat daripada mencoba benar sejak awal
  • Saat membuat game pertama, jangan mulai dengan membuat engine; sebelum menulis aplikasi, sebaiknya jangan mulai dengan membuat web framework
  • Monorepo juga merupakan trade-off serupa
    • Sulit mengetahui sejak awal bagaimana kode harus dipisah, dan satu kesalahan besar lebih mudah di-deploy daripada 20 kesalahan yang terikat kuat
  • Jika kita tahu kode itu akan segera dibuang, dihapus, atau mudah diganti, kita bisa mengambil lebih banyak jalan pintas
  • Tujuannya bukan mengulang bongkahan lumpur yang sama sepuluh kali hingga menyempurnakan kesalahan, melainkan membuat kesalahan baru setiap kali, mengambil risiko baru, dan membangun lewat iterasi
  • Proyek pada akhirnya akan gagal atau menjadi kode legacy, dan kegagalan terjadi lebih sering daripada keberhasilan
  • Menghapus semuanya sekaligus lebih mudah daripada menghapus kode sepotong demi sepotong

Tahap 6: Pecah kode menjadi bagian-bagian

  • Big ball of mud adalah yang paling mudah dibuat, tetapi biaya pemeliharaannya paling besar
  • Perubahan yang tampak sederhana bisa membuat hampir seluruh codebase harus disentuh secara tambal sulam
  • Kode yang mudah dihapus secara keseluruhan menjadi sulit dihapus per bagian
  • Modul sebaiknya dipisah bukan berdasarkan fungsi umum, melainkan berdasarkan hal yang tidak dibagi dengan bagian lain dan keputusan desain yang harus disembunyikan
  • Seperti kriteria D. Parnas, kita bisa membuat daftar keputusan desain yang sulit atau berpeluang besar berubah, lalu merancang setiap modul agar menyembunyikan keputusan tersebut dari modul lain
  • Modul dibuat bukan demi reuse, melainkan demi kemungkinan perubahan
  • Single responsibility principle dapat dilihat sebagai “setiap modul harus menangani hanya satu masalah sulit”, tetapi yang lebih penting adalah “setiap masalah sulit harus ditangani hanya di satu modul”
  • Jika sebuah modul melakukan dua hal, sering kali itu karena untuk mengubah satu bagian kita juga harus mengubah bagian lainnya
  • Satu komponen buruk dengan interface sederhana bisa lebih mudah daripada dua komponen yang membutuhkan koordinasi cermat

Loose coupling dan interface umum

  • Sistem yang dapat menghapus sebagian tanpa menulis ulang bagian lain biasanya disebut loose coupling
  • Loose coupling dekat dengan keadaan di mana kita tidak perlu mengubah terlalu banyak kode saat berubah pikiran
  • Meng-hardcode variabel sekali, atau bahkan menggunakan command-line flag alih-alih variabel, dalam kondisi tertentu bisa menjadi loose coupling
  • Microsoft Windows mencapai tujuan ini dengan memisahkan API eksternal dan API internal
    • API eksternal terikat pada lifecycle program desktop
    • API internal terikat pada kernel dasar
    • Dengan menyembunyikan API, fleksibilitas bisa diperoleh tanpa merusak banyak software
  • HTTP juga memberi contoh loose coupling
    • Cache bisa ditempatkan di depan HTTP server
    • Gambar dapat dipindahkan ke CDN dan cukup mengubah link tanpa merusak browser
    • HTTP error code memberi kode unik untuk masalah umum sehingga client dapat menangani banyak error sebagai gantinya
  • Cara menangani kegagalan harus dipertimbangkan bersama saat memecah kode menjadi bagian-bagian kecil

Penanganan kegagalan dan tingkat coupling

  • Erlang/OTP menggunakan cara yang relatif unik untuk menangani kegagalan melalui supervision tree
  • Setiap process dalam sistem Erlang kurang lebih dimulai dan diawasi oleh supervisor
    • Jika process bermasalah, process tersebut dihentikan
    • Jika process berhenti, supervisor memulainya kembali
    • Supervisor dimulai oleh bootstrap process, dan jika supervisor gagal, bootstrap process memulainya kembali
  • Ide intinya adalah lebih cepat gagal dan restart daripada menangani error
  • Kegagalan sementara kadang dapat ditekan dengan cara mematikan lalu menyalakan kembali
  • Error handling dan recovery sebaiknya dilakukan di layer luar codebase; ini dikenal sebagai end-to-end principle
  • Lebih mudah menangani kegagalan di kedua ujung koneksi daripada di tengah, dan sekalipun ditangani di dalam, pemeriksaan tingkat atas tetap diperlukan
  • Error handling adalah salah satu dari banyak cara yang dapat mengikat sistem dengan kuat

IMAP, file system, SQL, middleware

  • IMAP menyakitkan dalam hal error handling karena hampir setiap operasi berbentuk pengecualian dengan opsi dan penanganan tersendiri
  • Dalam IMAP, error dapat muncul di tengah hasil operasi lain
  • Alih-alih UUID, dibuat token unik untuk mengidentifikasi setiap pesan, dan token ini pun dapat berubah di tengah hasil operasi
  • Banyak operasi IMAP tidak atomic
  • Butuh lebih dari 25 tahun hingga muncul cara untuk memindahkan email dari satu folder ke folder lain secara andal
  • Ada juga encoding UTF-7 khusus dan encoding base64 yang unik
  • File system dan database menjadi pembanding yang lebih baik untuk remote storage
    • File system memiliki kumpulan operasi tetap dan banyak objek
    • SQL terlihat seperti interface yang lebih luas daripada file system, tetapi mengikuti pola banyak operasi atas set dan banyak row
  • Database tidak selalu bisa saling dipertukarkan, tetapi lebih mudah menemukan sesuatu yang bekerja dengan SQL daripada query language buatan sendiri
  • Finagle dari Twitter menggunakan API umum untuk service sehingga timeout handling, retry mechanism, dan authentication check dapat mudah ditambahkan ke kode client maupun server
  • Contoh bagus loose coupling sering kali juga merupakan contoh uniform interface
  • Codebase yang sehat tidak harus termodularisasi sempurna, tetapi harus memiliki jarak yang cukup di antara bagian-bagian yang bergerak
  • Kode yang loosely coupled tidak selalu mudah dihapus, tetapi jauh lebih mudah diganti dan diubah

Tahap 7: Terus menulis kode

  • Jika kita bisa menulis kode baru tanpa berurusan dengan kode lama, jauh lebih mudah bereksperimen dengan ide baru
  • Intinya bukan microservices atau monolith, melainkan selama kita mencari tahu apa yang dilakukan sistem, kita harus bisa menaruh satu atau dua eksperimen di atasnya
  • Feature flag adalah cara untuk memungkinkan kita berubah pikiran nanti
  • Feature flag tidak hanya untuk eksperimen fitur, tetapi juga memungkinkan perubahan di-deploy tanpa redeploy software
  • Google Chrome menemukan bahwa bagian tersulit dalam siklus rilis reguler adalah waktu yang dibutuhkan untuk menggabungkan feature branch yang sudah berjalan lama
  • Jika kode baru bisa dinyalakan dan dimatikan tanpa recompilation, perubahan besar dapat dipecah menjadi merge kecil dan tidak perlu memengaruhi kode yang sudah ada
  • Jika fitur baru muncul lebih awal di codebase yang sama, dampak pengembangan fitur jangka panjang terhadap bagian lain menjadi lebih jelas
  • Feature flag bukan sekadar command-line switch, melainkan cara memisahkan rilis fitur dari branch merge dan deployment kode
  • Saat deployment software baru bisa memakan waktu berjam-jam, berhari-hari, atau berminggu-minggu, kemampuan untuk berubah pikiran saat runtime menjadi semakin penting

Kode yang baik adalah kode legacy yang tidak mengganggu

  • Yang lebih penting daripada fakta bahwa kita beriterasi adalah memiliki feedback loop
  • Intinya adalah mengisolasi komponen untuk perubahan, bukan membuat modul demi reuse
  • Menanggapi perubahan mencakup bukan hanya pengembangan fitur baru, tetapi juga penghapusan fitur lama
  • Menulis kode yang extensible berarti berharap pilihan awal tetap benar tiga bulan kemudian
  • Kode yang deletable berangkat dari asumsi sebaliknya
  • Layering, isolasi, interface umum, dan composition adalah cara membuat software yang bisa berubah seiring waktu, bukan sekadar software yang baik itu sendiri
  • Tidak semuanya perlu dibuang, tetapi sebagian harus dihapus
  • Kode yang baik bukan kode yang benar sejak awal, melainkan kode legacy yang tidak mengganggu
  • Kode yang baik adalah kode yang mudah dihapus

1 komentar

 
GN⁺ 2024-10-30
Pendapat di Hacker News
  • Ungkapan yang saya suka adalah kesederhanaan itu ketangguhan
    Mirip dengan hukum perubahan berkelanjutan Lehman, artinya semakin rendah kompleksitas sistem, semakin mudah sistem itu diubah
    Menurut saya, daripada menulis kode yang dapat diperluas untuk berjaga-jaga terhadap masa depan, lebih baik mempersiapkan masa depan dengan kode yang intuitif
    Misalnya, lakukan abstraksi hanya ketika benar-benar diperlukan, izinkan duplikasi sederhana, mulai dengan monolit di awal, dan dahulukan scaling vertikal sebelum scaling horizontal
    Saya pernah membuat beberapa sistem 0→1, dan pola yang sama semuanya mengarah ke sini
    https://en.m.wikipedia.org/wiki/Lehman%27s_laws_of_software_...

    • Benar, tetapi saat menerapkan prinsip kesederhanaan itu ketangguhan, kita juga harus memahami kompleksitas intrinsik
      Kode tidak menjadi tangguh hanya karena tidak menangani kasus tepi, meskipun terlihat lebih sederhana
    • Aturan yang saya ikuti begini: yang pertama tulis saja, yang kedua salin, yang ketiga pertimbangkan refactoring
    • Saya setuju, tetapi saya tidak yakin ungkapan simple is robust cukup intuitif
      Perdebatan terbuka tentang apa itu “kesederhanaan” dan bagaimana menerapkannya pada sistem, dan ini juga pertanyaan yang cukup rumit sampai-sampai dibahas oleh Rich Hickey
      Mungkin “yang bodoh itu tangguh” atau “yang lugas itu tangguh” bisa lebih tepat menangkap maksudnya
    • Sangat setuju. Terlalu banyak sampah dalam software muncul karena mencoba menyelesaikan masalah imajiner
      Cukup tulis kode yang melakukan apa yang diperlukan. Jangan membuat masalah skalabilitas khayalan, jangan membuat abstraksi cerdik agar terlihat pintar, tulis sebagai monolit lalu taruh di VM dan bisa langsung masuk produksi
      Kalau ada masalah, selesaikan saat itu, dan sebaiknya setelah arus kas sudah positif
      Mengapa startup “AirBnb untuk anjing” dengan 0 pengguna mengkhawatirkan C100K? Apakah AWS membujuk Anda membayar serverless demi keuntungan Anda, atau demi mengeruk uang?
    • Kompleksitas logika bisnis tidak akan hilang hanya karena kita menginginkannya. Jika ia besar dan saling terkait, kodenya juga akan begitu
  • Artikel terkait:
    Write code that is easy to delete, not easy to extend (2016) - https://news.ycombinator.com/item?id=24989351 - Nov 2020 (30 comments)
    Write code that is easy to delete, not easy to extend (2016) - https://news.ycombinator.com/item?id=23914486 - July 2020 (109 comments)
    Write code that is easy to delete, not easy to extend - https://news.ycombinator.com/item?id=18761739 - Dec 2018 (2 comments)
    Write code that is easy to delete, not easy to extend - https://news.ycombinator.com/item?id=11093733 - Feb 2016 (133 comments)

  • Jika merangkum singkat kesalahan yang saya buat saat masih muda, sekarang saya justru percaya pada desain untuk dihapus
    Dulu saya berpikir bisa membuat karya seni yang hebat, yang mengantisipasi semua situasi dan memenuhi semua kebutuhan. Namun tidak ada orang yang bisa memprediksi kebutuhan masa depan sebaik itu
    Suatu hari nanti, apa yang saya buat akan menjadi “benda bodoh itu” bagi seseorang, dan betapapun bangganya saya sekarang, bisa jadi sah-sah saja bagi mereka untuk menghancurkannya sepenuhnya
    Jadi lebih baik mencurahkan tenaga untuk membuatnya mudah dihilangkan. Ini sering mengurangi coupling, tetapi yang penting adalah bahwa ini berbeda dari decoupling ala developer muda penuh semangat yang ingin memisahkan semuanya menjadi framework yang bisa dikonfigurasi secara meta
    Terkadang coupling yang kuat tetapi mudah dipahami justru lebih baik
    https://news.ycombinator.com/item?id=41219130

    • Anda bisa membuatnya mudah dihilangkan, tetapi orang lain bisa saja dengan senang hati membuat abstraksi dan logika, menanamkannya ke inti proyek, lalu pada akhirnya membuatnya membeku sampai hampir mustahil dihapus
      Misalnya muncul hal-hal seperti CommonExcelFileParser, CommonExcelFileParserUtilities, HasExcelParseStatus, ProductImportExcelParser, ProductImportExcelParserView, ProductImportExcelParserResultHandler, lalu semuanya menjadi fondasi bagi kode di sekitarnya
      Ini mirip dengan memulai proyek frontend dengan React atau Angular, lalu berpindah ke yang lain menjadi tugas Sisifus
      Dalam praktiknya, orang-orang akhirnya membuat seluruh platform, dan meskipun ada pilihan yang akan menimbulkan masalah di masa depan, karena coupling, refactoring menjadi jauh lebih sulit dibanding codebase yang kurang diabstraksikan
      Orang-orang tampaknya lebih menyukai hal seperti ini daripada menerapkan KISS dan YAGNI serta membuat kode yang mudah dihapus, jadi saya tidak tahu harus bagaimana ketika itu terjadi
    • Tetap saja, tergantung konteks. Kalau aplikasi bisnis, itu benar, benar berkali-kali
      Kebutuhan bisnis berubah dan bergerak, jadi jangan mencoba memprediksinya; tulislah sesuatu yang mudah diganti atau dibuang
      Framework dan library agak berbeda. Keduanya memang harus mengikuti perubahan dunia, tetapi bisa melakukannya dengan laju yang jauh lebih moderat
      Masalah terbesar adalah ketika dalam aplikasi bisnis yang sudah memakai framework seperti Rails atau Asp.Net, para developer masih ingin membuat “framework” lagi
    • Ada hal-hal yang berubah, dan ada kalanya kita memilih abstraksi yang salah
      Jika Anda tidak sedang menulis kernel Linux, jangan menulis seperti kernel Linux
  • Cukup aneh bahwa tulisan ini sama sekali tidak membahas pengujian dan observabilitas
    Pengujian juga punya biaya pemeliharaan, tetapi mengurangi risiko merusak sesuatu saat kita menghapus sesuatu
    Selain itu, jika layanan sudah diekspos ke pemanggil eksternal, kita membutuhkan baik cara yang kokoh untuk menandai sebagian pemanggilan sebagai akan dihentikan lalu menghapusnya nanti, maupun cara mengamati apakah pemanggilan itu masih terjadi dan siapa yang memanggilnya
    Baru-baru ini untuk pertama kalinya kami menghapus resolver GraphQL yang terekspos secara semi-otomatis, dan karena kami sudah punya metrik tentang seberapa sering resolver tertentu dipakai, kami mem-parsingnya untuk mendapatkan daftar resolver yang tidak bisa dihapus
    GraphQL memang sudah punya anotasi deprecated, tetapi layanan kami tidak menangani anotasi itu secara khusus
    Jadi kami menambahkan observabilitas yang menandai ketika fungsi deprecated dipanggil, menjalankannya cukup lama di production, lalu bisa menghapus kode yang terekspos ke luar dengan aman

    • Jika disederhanakan sedikit, membuat sesuatu yang mudah dihapus membuat kita tidak menciptakan bug yang tidak disengaja saat menghapusnya
      Begitu kita mulai membuatnya terlalu rumit, semuanya menjadi kekacauan yang saling terhubung, dan developer tidak lagi tahu dampak sebuah perubahan
      Tentu ada banyak cara untuk mengacaukannya. Kita bisa mengikuti prinsip “best practice” yang bodoh, atau melakukan “microservices” dengan cara yang membuat kita tidak tahu siapa yang mengonsumsi layanan apa. Tetapi kalau begitu, kita tidak membuatnya mudah dihapus
      Konsumsi eksternal adalah contoh yang bagus. Memberi konsumen peringatan yang wajar tentang penghentian layanan itu masuk akal, tetapi jika kita tidak bisa benar-benar mematikannya saat ingin, itu bukan sistem yang dirancang agar mudah dihapus
      Jika pendekatan itu tepat, silakan saja. Namun berharap pengujian dan observabilitas memberi tahu apakah sesuatu rusak kemungkinan besar tidak akan bekerja dengan baik
      Saya tidak menentang pengujian itu sendiri, tetapi sebagai pagar pengaman yang memberi tahu apakah kita merusak sesuatu dalam rantai yang panjang dan rumit, sulit menyebutnya sangat bagus. Karena sangat sulit juga memiliki cakupan pengujian yang benar-benar akan melindungi kita
    • Jika jumlah baris kode banyak, bisa diperkirakan jumlah baris pengujian juga akan muncul sampai batas tertentu secara proporsional
      Jika sebagian kode dihapus, sebagian pengujian juga bisa dihapus
      Seperti dalam tulisan itu, bisa dianggap bahwa tulisan hanya membahas kode dan dampak terkait pada pengujian sudah tersirat
      Hanya karena tulisan itu tidak menyebut pengujian, kita tidak bisa berasumsi bahwa maksudnya jangan menulis pengujian
    • Pengujian itu bagus, tetapi pemrograman tidak berakhir hanya dengan menulis pengujian. Tidak semua tulisan perlu menyebut pengujian
  • Melihat bagian ini, saya merasa judulnya tidak selalu benar: kode yang mudah dihapus sering kali juga merupakan kode yang mudah diperluas
    Karena kode itu berlapis, modular, dan mengisolasi bagian-bagian berbeda melalui abstraksi seperti interface atau kontrak tipe lain

  • Saya biasa mengatakan kepada mahasiswa fisika komputasi bahwa komputasi terbaik adalah komputasi yang tidak perlu dilakukan

  • Secara pribadi, saya membagi kode menjadi dua bagian: logika bisnis dan implementasi nyata
    Logika bisnis pada dasarnya bisa saja berulang, tetapi detail teknis tidak boleh terlalu banyak berulang
    Implementasi nyata boleh sekotor apa pun selama tidak memuat logika bisnis secara langsung dan tetap independen dari aplikasi
    Dengan begitu, ketika kita sadar ada sesuatu yang berantakan dan tidak berjalan baik, kita punya opsi untuk menghapus seluruh implementasi, alih-alih menelusuri balik spesifikasi sebenarnya dari implementasi lalu memaksakan perbaikan

  • Kalimat di paragraf pertama bahwa “masalah reuse kode adalah ia menghalangi kita berubah pikiran nanti” jelas keliru
    Secara umum, itu salah. Jika pikiran kita berubah dan kode sudah disalin-tempel di sepuluh tempat, kita harus memperbaiki sepuluh tempat
    Sebaliknya, jika ada di dalam fungsi, kita hanya perlu mengubahnya sekali. Kalaupun ternyata satu dari sepuluh pemanggilan tidak boleh berubah, saat itu kita bisa menyalin-tempel atau membuat fungsinya lebih umum
    Seperti menyeberang jalan tanpa melihat, salin-tempel hampir selalu ide buruk

    • Dalam pengalaman saya, kode salin-tempel yang buruk berakhir dengan setengah hari yang menjengkelkan untuk membayar utang teknis dan memperbaikinya
      Tetapi abstraksi yang buruk berujung pada pembayaran utang teknis selama berbulan-bulan
      Tentu jawabannya adalah “jangan membuat abstraksi yang buruk”, tetapi kita semua tahu bagaimana itu berjalan dalam tim dan kebutuhan produk yang terus berubah
    • Kode yang digunakan ulang sering kali merupakan kode yang benar di banyak tempat, sehingga untuk mengubahnya kita harus memperlambat langkah dan memisahkan titik-titik itu
      Kami punya git submodule berisi widget UI umum, dan sekarang hampir mustahil mengubah salah satunya, sehingga lebih mudah menyalin komponennya ke dalam proyek dan mengubahnya secara lokal
      Ini masalah. Kode bersama harus seminimal mungkin, dan berbagi itu sendiri membuat perubahan menjadi sulit
    • Apa yang terjadi jika dari 10 pemanggilan fungsi, 3 harus diubah dengan satu cara, 5 dengan cara lain, dan 2 sisanya tidak lagi memakai abstraksi yang sama sehingga harus ditulis ulang sepenuhnya
      Jika semuanya ada dalam satu fungsi, kebanyakan developer akan mencoba mengubah fungsi itu agar memenuhi semua 10 kasus. Padahal sejak awal itu seharusnya bukan satu fungsi
      Jauh lebih mudah memperbaiki sepuluh tempat hasil salin-tempel daripada mengurai simpul yang salah diikat yang sudah terlanjur mengikat bagian-bagian sistem
    • Penulisnya mungkin akan menjawab bahwa kode itu seharusnya dipindahkan ke modul atau fungsi
      Di permukaan, ini tampak seperti kontradiksi diri dalam topik ini, tetapi jika dibaca perlahan, ia menggunakan salin-tempel sebagai sinyal untuk mengetahui kode mana yang harus diabstraksikan, dan apa pola yang benar-benar layak diikuti
  • Aneh rasanya terus mengulang segala macam perintah tentang software, prinsip-prinsip yang nyaris seperti agama
    Di atas kertas semuanya tampak hebat dan terasa seperti akal sehat, tetapi setelah 50 tahun pun software masih sampah dalam 90% kasus
    Meski begitu, hal-hal seperti ini terus diangkat seolah-olah wawasan jenius atau peluru perak

    • Saya rasa 90% sampah itu ditulis oleh orang-orang yang tidak membaca atau menulis tulisan seperti ini
  • Ada korolari yang bagus di sini. Kode buruk bertahan lama karena jauh lebih sulit dihapus