Naikkan `if`, turunkan `for`
(matklad.github.io)- Intinya adalah dua aturan penataan kode: tarik percabangan kondisi ke sisi pemanggil, dan dorong pemrosesan berulang ke sisi operasi batch
- Jika
ifdinaikkan, prasyarat menjadi lebih mudah dipaksakan lewat tipe atau assert, dan alur kontrol yang kompleks dapat dikumpulkan di satu tempat sehingga kondisi duplikat dan cabang mati lebih mudah ditemukan - Struktur yang membuat
enumlalu segeramatchlagi mengulang kondisi yang sama dalam bentuk percabangan, struktur data, dan percabangan ulang, sehingga ada ruang untuk disederhanakan menjadi pemanggilanfoo(x)danbar(y) - Jika
forditurunkan, operasi batch menjadi unit dasar alih-alih iterasi per objek, sehingga biaya awal bisa diamortisasi, urutan pemrosesan bisa diubah, dan vektorisasi serta optimasi struct-of-array menjadi mungkin - Jika kondisi dikeluarkan dari loop, percabangan di hot loop bisa dikurangi, dan ini juga terhubung ke struktur seperti TigerBeetle yang mengamortisasi biaya pengambilan keputusan di control plane ke pemrosesan batch di data plane
Menaikkan if
- Kondisi
ifdi dalam fungsi pertama-tama layak diperiksa apakah bisa dipindahkan ke pemanggil- Contohnya, fungsi yang menerima
Walrussecara langsung dianggap lebih baik daripada fungsi yang menerimaOption<Walrus>lalu mengembalikan tanpa melakukan apa pun saat nilainyaNone - Daripada memeriksa prasyarat di dalam fungsi lalu “tidak melakukan apa-apa”, lebih jelas bila pemanggil yang memeriksanya dan memaksakan kebenarannya lewat tipe atau assert
- Contohnya, fungsi yang menerima
- Refactoring yang memindahkan pemeriksaan prasyarat ke atas dapat merambat sepanjang jalur pemanggilan, dan pada akhirnya bisa mengurangi jumlah pemeriksaan secara keseluruhan
- Alur kontrol dan
ifmeningkatkan kompleksitas dan mudah menjadi sumber bug- Jika
ifdinaikkan, logika percabangan yang kompleks terkumpul dalam satu fungsi, sedangkan pekerjaan aktual turun ke subrutin yang linear - Alur kontrol yang kompleks lebih mudah dipakai untuk menemukan duplikasi dan kondisi mati saat berada dalam satu fungsi di satu layar dibanding tersebar di seluruh berkas
- Jika
- Refactoring “dissolving enum” adalah cara menghilangkan pola yang mengubah percabangan menjadi struktur data lalu bercabang lagi
- Jika
f()membuatE::Foo(x)atauE::Bar(y)berdasarkan kondisi, lalug(e)kembali memanggilfoo(x)ataubar(y)lewatmatch, maka kondisi yang sama diulang dalam beberapa bentuk - Jika kondisi itu ditarik ke
main(), bentuknya menjadi lebih sederhana:if condition { foo(x) } else { bar(y) }
- Jika
Menurunkan for
- Dari sudut pandang data-oriented, program dan hot path biasanya menangani banyak objek, jadi batch objek diperkenalkan dan operasi batch dijadikan kasus dasar
- Versi skalar untuk objek tunggal menjadi kasus khusus dari operasi batch
for walrus in walruses { frobnicate(walrus) }dianggap bentuk yang kurang baik dibandingfrobnicate_batch(walruses)
- Keuntungan utamanya adalah performa
- Jika seluruh batch ditangani sekaligus, biaya awal bisa diamortisasi
- Urutan pemrosesan bisa diubah dengan lebih leluasa, dan pendekatan seperti vektorisasi atau struct-of-array yang memproses satu field dari semua entitas lebih dulu juga menjadi mungkin
- Sebagai contoh ekstrem, artikel ini menautkan Vectorized Interpreters Talk
- Contoh menarik lainnya adalah perkalian polinomial berbasis FFT
- Pendekatan yang mengevaluasi polinomial di banyak titik secara bersamaan bisa lebih cepat daripada melakukan evaluasi titik per titik berulang kali
- Aturan
ifdanforbisa diterapkan bersama- Bentuk yang baik adalah menjalankan
for walrus in walrusesper cabang di luarif condition - Bentuk yang buruk adalah mengevaluasi
if conditionsetiap kali di dalam loopfor - Ini bisa menghindari evaluasi ulang kondisi, menghilangkan percabangan dari hot loop, dan memungkinkan vektorisasi
- Bentuk yang baik adalah menjalankan
- Pola ini berlaku baik di level mikro maupun makro
- Arsitektur TigerBeetle memproses batch objek secara bersamaan di data plane untuk mengamortisasi biaya pengambilan keputusan di control plane
- Motivasi utama nasihat tentang
foradalah performa, tetapi cara kerja berbasis koleksi seperti pada jQuery juga bisa membantu dari sisi daya ekspresi
1 komentar
Pendapat Hacker News
Awalnya saya terkejut tulisan ini mendapat penolakan sebesar ini, tetapi setelah teringat bahwa ini adalah saran desain berorientasi data, saya jadi paham
Seperti banyak orang di forum ini, saya juga kebanyakan membuat aplikasi web untuk pekerjaan, dan dalam konteks seperti itu saran ini bisa terlihat seperti omong kosong
Jika dalam pekerjaan sehari-hari Anda tidak perlu memikirkan instruction cache, heuristik bahwa saran ini secara umum boleh diabaikan tampaknya tepat
Kalau ingin mendapat gambaran kapan saran ini menjadi penting, ada baiknya mencari “Typical C++ Bullshit” dari Mike Acton; tulisan ini terasa seperti merangkum isinya dengan cara yang lebih mudah dipahami
Saya cukup bersimpati dengan kegelisahan Casey Muratori, tetapi sebagian besar software untuk bisnis perlu mengoptimalkan kemudahan perubahan dan kebenaran dibanding performa, yaitu “pemrograman seiring waktu”
Developer sering mencoba membuat logic bisnis yang kompleks menjadi DRY dengan memecahnya ke metode-metode private kecil di dalam class, dan “naikkan if ke atas” membantu agar logic percabangan tidak tersebar di banyak metode
“Turunkan for ke bawah” juga penting. Banyak alur pemanggilan pada akhirnya berujung pada query database yang mahal, dan sering kali loop di level atas membuat pemanggilan DB berkali-kali di level bawah
Iterasi itu sering bisa digantikan dengan klausa
whereataujoindi SQL, dan untuk agregasi atau filtering pun lebih baik didorong lebih dekat ke DAO daripada mengambil banyak objek lalu mengiterasinya, karena lebih mudah dioptimalkan di dekat DBNamun, seperti semua prinsip desain, jangan menerapkannya sebagai dogma; nilailah secara sadar
Sayang sekali jika kemudahan perubahan dan performa dipandang sebagai hal yang saling bertentangan. Sejauh ini saya belum melihat bukti meyakinkan bahwa keduanya benar-benar berbenturan
Salah satu aspek terpenting dalam performa adalah membuatnya kecil. Kode kecil, struktur data kecil, dan jumlah instruksi eksekusi yang sedikit adalah intinya; “memikirkan instruction cache” pun pada dasarnya berarti menulis kode kecil
Semakin kecil kode, semakin sedikit ruang bagi bug untuk masuk, semakin mudah dipahami sekaligus, dan semakin mudah memastikan cakupan pengujian, sehingga menguntungkan kebenaran. Kemudahan perubahan juga diuntungkan karena semakin kecil kode, semakin kecil pula perubahannya
Tentu ada optimasi yang membuat kode menjadi rumit seperti paralelisasi, caching, dan optimasi level rendah, tetapi itu hanya sebagian dari optimasi performa, dan programmer yang benar-benar peduli pada performa tidak akan melakukan hal seperti itu tanpa profiling dan analisis
if/fordanfor/ifmungkin tidak jauh berbeda secara fungsional, tetapi salah satunya bisa lebih cepat; jadi jika bisa memakai yang benar tanpa biaya, tidak ada alasan sengaja memilih yang lebih lambatProgram yang sederhana memiliki call stack pendek dan menghindari abstraksi yang rumit sekaligus mahal, sehingga cenderung memakai lebih sedikit memori dan berjalan lebih cepat
Mengejar performa tingkat tertinggi bisa membuat program menjadi rumit, tetapi kesederhanaan sejati biasanya menghasilkan performa yang masuk akal
“Menurunkan for ke bawah” terutama adalah saran berorientasi data, tetapi “menaikkan if ke atas” lebih dekat ke arah membuat program menjadi lebih sederhana. Tepatnya, ini soal meningkatkan lokalitas source code, dan intinya adalah mengumpulkan logic percabangan di satu tempat: https://loup-vaillant.fr/articles/source-of-readability
Terutama “turunkan loop ke bawah” sangat kuat di aplikasi CRUD. Jika pembuatan dan pembaruan dilakukan secara batch sebisa mungkin, sering kali waktu yang dihemat jauh lebih besar daripada bottleneck CPU
Perbedaan antara
items.map(insertToDb/postToServer)daninsertToDb/postToServer(items)hampir selalu berbeda beberapa order of magnitudeSaya pernah melihat optimasi seperti ini memangkas waktu kerja dari hitungan detik atau menit menjadi milidetik, dan sering kali API juga menjadi lebih rapi serta log lebih mudah dibaca
Seiring bertambahnya pengalaman, saya merasa terlalu banyak programmer memperhatikan “kode cantik” pada unit kecil, tetapi tidak cukup memperhatikan desain codebase secara keseluruhan
Fungsi yang ringkas dan dipoles dengan baik memang bagus, tetapi tulisan seperti ini bisa berubah menjadi perdebatan bike-shedding yang tidak produktif di PR atau diskusi
Apakah sebuah fungsi agak berantakan, di mana menaruh
ifdanfor, apakah memakaimapdanfilter, tidak terlalu saya persoalkan selama nama fungsinya tepat, antarmuka dan tipenya ekspresif, tujuannya jelas, terdokumentasi, dan tidak memakai efek samping secara berlebihanJika memikirkan di mana memvalidasi input saat ada nilai masukan, menurut heuristik ini validasi harus dilakukan di bagian paling atas tempat input diterima
Ini membantu memahami kode, dan dari sudut pandang pembuktian juga diperlukan secara teknis karena prasyarat harus dipropagasikan ke atas
Nasihat pertama jelas bukan perdebatan sepele, sedangkan yang kedua agak lebih ambigu
Mereka berusaha mencegah junior menembak kakinya sendiri, tetapi sering kali belum punya cukup kepekaan terhadap biaya kompleksitas dan bagaimana kode berubah dalam jangka panjang
Sebaliknya, setelah melakukannya lebih dari 20 tahun, kebanyakan orang menilai kesederhanaan lebih tinggi daripada sebagian besar unsur teknis
Pertanyaan seperti “seberapa awal harus bercabang” dan “apa yang sebenarnya harus dilakukan kode ini” cenderung memberikan jawaban paling bernilai dalam jangka panjang
Pertanyaan tentang abstraksi dan enkapsulasi justru mudah mengarah ke jenis perdebatan yang disebutkan tadi, dan alasan masalah keamanan paling sering muncul di kode orang-orang yang hanya melihat “gambaran besar” adalah karena mereka tidak benar-benar memahami apa yang dilakukan codebase yang katanya dirancang dengan baik itu
Dalam software sensitif performa yang cocok dengan desain berorientasi data, hal-hal unit kecil seperti ini perlu lebih diperhatikan. Sebab optimisasi compiler bekerja dengan cara seperti itu
Maka aturan mainnya berubah. Makna pernyataan menjadi penting, kode yang menjelaskan dirinya sendiri menjadi lebih bermakna, komentar kode tersisa hanya untuk penalaran, dan “dokumentasi” lebih dekat ke spesifikasi dan panduan pengguna
Paling-paling kita bisa mengelompokkan fungsi-fungsi, lalu setelah itu harus mengurus sendiri
Kekurangan dari “menaikkan if ke atas” adalah prasyarat dan pascakondisi tidak terlihat langsung di dalam definisi fungsi, sehingga harus diperiksa di setiap titik pemanggilan
Dalam proyek besar yang melibatkan banyak orang, fungsi seperti ini bisa digunakan kembali di luar konteks yang dimaksud dan berujung pada bug
Ini bisa diselesaikan dengan framework kontrak, tetapi kondisi harus ditulis dua kali, di kontrak dan di kode; tipe dependen pun sama
Cara menandai area kode yang termasuk konteks tertentu, lalu mendefinisikan fungsi yang hanya bisa dipanggil di dalam konteks itu, menarik untuk dipikirkan
Di Python, misalnya, decorator seperti
@requires_context("VALIDATED_XY")dan context managervalidated_xydapat membuat fungsi hanya bisa dipanggil di area yang sudah divalidasiRuntime tidak mengetahui makna konteks itu, tetapi dengan tooling dan pengujian, kita bisa merancang agar konteks yang diinginkan hanya dibangun saat kondisi tertentu terpenuhi
Dalam bahasa seperti Haskell, ini bisa dipaksakan di level tipe dengan sesuatu seperti identity monad, dan meski bukan pemaksaan level tipe, ini bisa menjadi cara menarik untuk melindungi area kode “unsafe”
Jadi prasyarat tetap terlihat langsung pada definisi fungsi, hanya saja bukan sebagai pernyataan
if, melainkan sebagai bagian dari type signatureIni adalah pola yang umum di Rust seperti yang digunakan dalam tulisan itu, dan berbeda dari pemeriksaan
if, prasyarat yang ketat ini diperiksa pada waktu kompilasi, bukan runtime, sehingga jika tidak dipenuhi program tidak akan terkompilasiMisalnya menerima
do_something(position: ValidatedPosition), lalu memvalidasiPositionbiasa dan mengubahnya menjadiValidatedPositionsebelum meneruskannyaDalam praktiknya validasi mungkin akan dienkapsulasi di dalam konstruktor
ValidatedPosition, tetapi intinya adalah jika mencoba meneruskanPositionapa adanya, mypy akan memberi tahu bahwa tipe yang diberikan salahPemeriksaan tipe Python tidak sekomprehensif Rust, tetapi semakin berguna ketika ingin memastikan data yang diteruskan sudah diproses dengan semestinya
Jika sebuah fungsi memiliki prasyarat, tentu saja boleh melakukan assertion di awal fungsi. Misalnya sistem tipe Java mengizinkan null, jadi fungsi yang membutuhkan objek harus melempar exception jika menerima null
Setiap titik pemanggilan bertanggung jawab memanggil fungsi hanya ketika prasyarat benar. Ini wajar menurut definisi prasyarat
Memanggil fungsi dengan melanggar prasyarat adalah bug di sisi pemanggil. Di dalam fungsi mungkin diperlukan kode untuk memeriksanya agar mencegah perilaku tak terdefinisi, tetapi assertion seperti ini harus dibedakan dari alur kontrol program yang sebenarnya, dan tulisan itu membahas yang terakhir
fn frobnicate(walrus: Walrus)ini, jika Anda meneruskan sesuatu yang bukanWalrusyang dimiliki, program tidak akan terkompilasiSekalipun generik, argumen harus memenuhi trait bound, dan bergantung pada bagaimana argumen digunakan di dalam fungsi, compiler akan menuntut bound yang diperlukan pada definisi fungsi
publicdanprivatesampai taraf tertentu adalah untuk menandai kode agar sesuai dengan konteks tertentuAtau mungkin diperlukan semantik yang lebih spesifik yang melintasi area yang ditangani oleh
public,private, danprotecteddi ekosistem .NETContoh pertama buruk bukan karena
ifdanfor, melainkan karena alasan lainSecara umum, jika ada “kontainer” untuk sesuatu, lebih baik menulis fungsi pada “objek” level domain di dalamnya, bukan pada kontainernya
Saat memakai agent di Clojure, kita tidak menulis fungsi khusus agent, melainkan fungsi untuk objek yang bisa ditampung oleh agent
Di Elixir juga, fungsi domain inti bekerja pada struktur data domain internal, bukan pada PID, dan bila perlu pemanggilan GenServer didelegasikan ke sana
Dengan begitu kode menjadi lebih fleksibel, dan kita bisa memisahkan dengan lebih rapi domain inti “melakukan frobnicate pada Walrus” dari kepentingan aplikasi “Walrus mungkin ada atau mungkin tidak ada; jika ada, lakukan frobnicate”
Validasi sejak awal memang baik, tetapi penting juga agar fungsi mengeluarkan error validasi yang jelas, alih-alih tiba-tiba meledak dengan error aneh
Haskell menyelesaikan masalah ini dengan newtype, yaitu menyediakan “kontainer transparan yang menyertifikasi bahwa validasi yang semestinya sudah dilakukan”
Saran yang benar-benar ingin saya tekankan kepada orang-orang adalah memilih “sad if”. Hampir selalu lebih mudah dibaca dan dipelihara bila kode memeriksa kondisi-kondisi yang salah satu per satu di bagian atas lalu langsung memperbaiki atau menghentikannya, daripada menumpuk jalur normal dalam beberapa lapis nesting
Manusia secara alami ingin berfokus pada kasus yang diharapkan, tetapi kode justru sebaiknya lebih dulu berfokus pada kasus-kasus pengecualian. Setiap
ifmenambah beban mental, dan jika kita harus mengambil informasi dari sistem eksternal atau keluar lebih awal karena error, beban itu bisa segera dilepas ketika deteksi dan penanganannya berada berdekatanwalrus.frobnicate(), rasanya itu lebih dekat ke soal selera sintaksis daripada inti yang ingin disampaikan penulisSaya tidak sampai mengatakan ini saran buruk, tetapi juga belum tentu saran yang baik
Fakta bahwa bahasa yang dipilih adalah Rust cukup banyak menjelaskan. Sistem tipe yang kuat mencegah banyak defensive programming yang dibutuhkan di bahasa lain
Jika seorang programmer C tidak memeriksa validitas pointer yang diteruskan ke fungsi lalu menyebabkan dereference NULL, saya tidak ingin satu tim dengan orang itu
Jadi setidaknya sebagian
ifjelas harus berada di bagian bawah, dan sebaiknya error bisa dipropagasikan ke atas dengan baikUntuk
forsaya tidak sekuat itu pendapatnya, tetapi di C, karena argumen array mengalami decay menjadi pointer, saya pikir iterasi juga seharusnya berada di atas, bukan di bawah. Di fungsi tempat array dibuat, panjangnya bisa dipercaya, tetapi di fungsi yang menerimanya sebagai argumen, tidak demikianDokumentasilah yang harus menggantikan sistem tipe yang kuat, bukan pemeriksaan runtime
Kode yang penuh pemeriksaan NULL dan langkah defensif lain jauh lebih sulit dibaca
Bisa dibilang pemeriksaan yang lebih defensif diperlukan di batas library, dan tulisan ini tepat mengatakan agar pemeriksaan seperti itu didorong ke atas
Kode yang kritis untuk keamanan bisa berbeda, tetapi dalam kebanyakan kasus, dereference NULL yang tidak disengaja cukup ditangkap lewat testing, sanitizer, dan fuzzing
Untuk memeriksa nilai pointer yang tidak valid, kita harus memeriksa keseluruhan begitu banyak kemungkinan nilai yang salah, dan jika hanya memeriksa NULL, pada dasarnya kita tidak benar-benar memeriksa nilai yang tidak valid
Jika prasyarat fungsi adalah “parameter
ptidak boleh NULL”, boleh saja diperiksa. Tetapi jika prasyaratnya adalah “pharus berupa pointer yang valid”, semoga beruntung menemukan kondisi assertion yang tepatBelakangan saya banyak memakai T-SQL, dan parameter atau variabel tidak bisa dideklarasikan sebagai
NOT NULLJadi sebaiknya NULL diperiksa sedini mungkin secara masuk akal, biasanya di bagian paling atas stored procedure
Jika tidak, NULL bisa merambat secara tak terduga jauh ke dalam lapisan pemanggilan dan menimbulkan masalah yang kurang jelas
Untungnya data tabel bisa dideklarasikan sebagai
NOT NULL, jadi bug seperti ini biasanya tidak merusak data, tetapi jauh lebih mudah jika ditangkap lebih awalNamun jika ada logika yang menulis ke DB bergantung pada suatu nilai parameter dan nilai itu ternyata NULL secara tak terduga, nilai yang salah bisa tertulis atau nilai yang diperlukan malah tidak tertulis sama sekali, yang pada praktiknya bisa merusak data
Jadi defensive programming adalah jawabannya
Tanpa konteks yang tepat, saran ini cukup aneh, dan mungkin bahkan buruk
Loop dan pernyataan
ifsama-sama operasi control flow, jadi sebagian argumen dalam tulisan ini tidak terlalu masuk akalArgumen terkuat tampaknya adalah performa, tetapi performa biasanya seharusnya menjadi hal yang dipikirkan paling akhir, apalagi jika ini berupa saran heuristik
Sayangnya penulis sudah menjadikannya slogan, dan saya berharap ini tidak menyebar
Jika kita bisa menulis kode yang diperbaiki seperti contoh penulis, kondisinya adalah konstanta selama loop berjalan. Selama bukan kondisi mahal yang dievaluasi setiap kali, branch prediction akan menangani sebagian besarnya
Jika kondisinya berupa ekspresi boolean dari nilai const, compiler juga mungkin bisa mengetahuinya
Saya kira seluruh tulisan akan berlanjut ke contoh seperti ini: membagi
walrusberdasarkan kondisi lalu memanggilfrobnicate_batch(fwalrus)dantransmogrify_batch(twalrus)Namun kenyataannya contoh yang dipakai adalah membungkus seluruh loop menjadi dua cabang dengan satu kondisi
Mengejutkan bahwa programmer dengan latar belakang software engineering sering salah soal ini
Saya mulai memrogram dari bidang sains, dan di sana memikirkan hal-hal seperti ini benar-benar mutlak diperlukan
Salah menentukan urutan loop saja bisa membuat simulasi memakan waktu 1 minggu alih-alih 1 jam
Berkat latar belakang itu, saya secara naluriah melakukan optimasi kecil dengan mengatur urutan
fordanifsecara tepat, dan kode yang tidak melakukannya terasa seperti salahSaya tidak yakin aturan umum seperti ini benar-benar bisa diterapkan pada kode nyata
Aturan semacam ini sering terlihat seperti dogma yang salah tempat, dan meskipun artikel blog memulainya sebagai heuristik, programmer muda tidak selalu menerimanya seperti itu
Beberapa minggu lalu YouTube terus merekomendasikan video berjudul “I'm a never-nester”, yang tampaknya berargumen agar jangan pernah menumpuk
if, dan itu terasa cukup konyolSeperti contoh dalam tulisan itu, kode dengan
if conditiondi dalamfordiberi label “buruk”, tetapi pada sebagian besar kode nyata yang ditulis seperti itu,conditionbergantung padawalrussehinggaiftidak bisa dinaikkanJika memang bisa dinaikkan, fakta bahwa kondisi yang sama dievaluasi ulang di setiap iterasi loop akan terlalu jelas, sehingga programmer cenderung menghindarinya secara alami
Namun ketika junior atau mahasiswa membaca saran yang terdengar seperti dogma, mereka bisa saja berusaha mengikutinya secara kaku dan justru menghasilkan kode yang lebih buruk
match (condition_a, condition_b)tidak lebih baik daripadaifbertumpukifsebaiknya “dinaikkan”Tujuan kode itu adalah melakukan operasi tertentu pada walrus bergantung pada kondisi, tetapi sebenarnya
ifsedang dipakai sebagai pengganti polimorfisme dan sistem tipeMengapa walrus harus memiliki dua fungsi yang perlu dipanggil dalam skenario berbeda? Mengapa tidak satu fungsi dan dua tipe saja, lalu meneruskan tipe yang tepat?
Bahkan dengan struktur saat ini, di bagian atas kita bisa memilih fungsi penangan berdasarkan kondisi, lalu di loop di bawah cukup memanggil fungsi yang sudah dipilih setiap kali
Keputusan sebaiknya dibuat sedini mungkin, supaya tidak perlu disebar ke seluruh kode. Bagian dalam kode melakukan pekerjaan yang sama setiap kali tanpa percabangan, dan keluarannya hanya berbeda lewat grafik komposisi
Tentu ini bukan gagasan baru, dan 15 tahun lalu pun sudah merupakan ide lama: https://www.youtube.com/watch?v=4F72VULWFvc
walrus, dan membantu membuat fakta itu terlihat eksplisitJika “turunkan for” diterapkan lagi di sini, bentuknya menjadi memanggil
frobnicate_batch(walruses)atautransmogrify_batch(walruses)di dalamif conditionAda orang yang merumuskan dengan kata-kata masalah yang sudah beberapa kali saya temui tetapi belum bisa saya ungkapkan dengan baik, dan itu tampak seperti model yang bagus untuk disimpan di kepala
Pada saat yang sama, kritik seperti ini juga bernilai. Semoga developer junior yang dogmatis membacanya juga dan menilai dengan sedikit lebih bernuansa
Programmer terutama tidak boleh menyalin hal semacam ini lalu menerapkannya secara mekanis
Menerapkan heuristik dan praktik terbaik secara buta bisa jadi bukan ide bagus, dan pada akhirnya semuanya “tergantung situasi”
Heuristik seperti ini biasanya mengandung secuil kebijaksanaan, tetapi umumnya juga menciptakan sesuatu yang kemudian harus dilepaskan dari dogma lagi pada programmer pemula
Selalu ada banyak kasus yang menjadi lebih buruk ketika diikuti terlalu kaku, dan “ketahui kapan harus tidak mengikuti saran ini” pada dasarnya adalah bagian sulit utamanya
Menurut saya aturan-aturan seperti ini berguna untuk dimainkan. Cobalah menerimanya, dorong sampai batasnya, lalu balikkan selama sehari atau setahun, dan lihat sejauh mana ia berjalan
Mempelajari batasannya juga merupakan satu bahan lagi yang menumpuk di atas perkamen yang terus ditulisi ulang
Sebagian koan itu memang seharusnya saling bertentangan