Aturan Pemrograman Rob Pike (1989)
(users.ece.utexas.edu)- Artikel tentang 5 aturan pemrograman Rob Pike pada tahun 1989
- Aturan 1: Jangan berasumsi di mana program akan menghabiskan sebagian besar waktunya; bottleneck bisa muncul secara tak terduga. Hindari hack demi kecepatan sampai bottleneck itu terbukti.
- Aturan 2: Selalu ukur sebelum melakukan tuning demi kecepatan. Optimalkan hanya jika sebagian kode benar-benar memberi dampak signifikan pada sisanya.
- Aturan 3: Algoritma yang rumit itu lambat saat n kecil. Dan biasanya memang begitu. Gunakan algoritma rumit hanya jika n sering besar, dan bahkan saat itu pun terapkan dulu Aturan 2.
- Aturan 4: Algoritma dan struktur data yang sederhana lebih diinginkan. Keduanya lebih kecil kemungkinannya mengandung bug dibanding yang rumit dan lebih mudah diimplementasikan.
- Aturan 5: Struktur data yang tepat sangat menentukan dalam pemrograman. Jika data tersusun dengan baik, algoritmanya akan menjadi jelas dengan sendirinya.
- Aturan 1 dan 2 Pike mencerminkan pepatah Tony Hoare, "premature optimization is the root of all evil".
- Ken Thompson mengungkapkan kembali Aturan 3 dan 4 Pike sebagai "when in doubt, use brute force".
- Aturan 3 dan 4 mewujudkan filosofi desain KISS (Keep It Simple, Stupid).
- Aturan 5 sejalan dengan pernyataan Fred Brooks dalam 'The Mythical Man-Month', yang sering diringkas sebagai "Show me your flowchart and conceal your tables, and I shall continue to be mystified. Show me your tables, and I won't usually need your flowchart; it'll be obvious."
1 komentar
Komentar Hacker News
Saya sepenuhnya setuju dengan ungkapan “data yang berkuasa”
Karena itu wawancara LeetCode selalu terasa aneh. Biasanya fokusnya pada algoritma, padahal dalam praktiknya sejak awal sering kali tidak boleh didekati seperti itu, dan struktur data seharusnya lebih menjadi pusatnya
Tentu, kalau sama sekali tidak tahu algoritma, kita bisa saja tidak menyadari situasi pengecualian atau saat tertentu ketika perlu bergantung pada algoritma tertentu karena alasan tertentu. Meski begitu, algoritma bisa diajarkan relatif singkat, sementara orang tampaknya lebih sulit mendapatkan intuisi tentang struktur data apa yang harus dipakai
Pada saat itu suasananya berubah menjadi “oh, benar-benar ada engineer senior yang masuk,” masalah teknis dibicarakan dengan lebih terbuka, dan sikap untuk membuktikan “apakah bisa coding” juga berkurang
Sebaliknya, tim yang paling sulit dalam membuat perubahan yang baik, mencapai milestone, dan berkolaborasi adalah tim yang tidak punya orang yang benar-benar menata struktur data dan arsitektur kode dengan baik. Tampaknya makin banyak orang terbiasa berpikir bahwa framework akan menangani semuanya, dan kalau tidak berhasil, plugin atau middleware buatan orang yang lebih pintar akan menyelesaikannya
Engineer yang menghindari struktur data ibarat menembak kakinya sendiri, menyerahkan salah satu alat paling berguna, sehingga batasannya terlihat dalam pekerjaan sehari-hari
Misalnya, kode bisa banyak dipakai untuk mencari jalur terpanjang pada graf berarah asiklik (DAG) berbobot, tetapi intinya adalah menyadari bahwa masalah tersebut bisa direpresentasikan sebagai DAG berbobot. Kalau tidak melihat itu, masih bisa diselesaikan, tetapi solusinya menjadi jauh lebih lambat dan rumit
Pewawancara tidak akan lebih dulu memberi tahu bahwa harus memakai priority queue, adjacency matrix, atau trie. Jika mentok, mereka bisa memberi petunjuk, tetapi pengarahan yang berlebihan sulit dilihat sebagai sinyal perekrutan yang kuat
Terkait ungkapan “algoritma keren itu lambat saat n kecil, dan n biasanya kecil,” yang saya rasakan dalam proyek baru-baru ini adalah n besar bisa jauh lebih besar daripada yang dibayangkan
Mudah untuk berpikir “harus melakukan 100 ribu operasi, jadi ini pasti perlu dioptimalkan,” tetapi komputer itu cepat, dan 100 ribu perkalian biasanya bisa terlalu cepat sehingga tidak perlu dipikirkan terlalu dalam
Bukan berarti jangan berpikir sama sekali, tetapi sering kali mengejutkan betapa gilanya kecepatan hardware modern
Saya pernah melihat gangguan produksi akibat kode yang tidak sengaja menjadi kuadratik, dan meskipun 99% pengguna selalu memakai n kecil, sebagian pengguna sering menemui n besar dan mengalami aplikasi yang sangat lambat
Dalam kebanyakan kasus, meski sedikit lebih lambat pada kasus umum dan implementasinya sedikit lebih rumit, saya ingin memilih algoritma yang lebih baik daripada waktu kuadratik. Jalur lambat yang umum akan dioptimalkan, tetapi jalur lambat yang jarang terjadi bisa tidak pernah disentuh langsung oleh developer lalu meledak di produksi
Tentu, jika algoritmanya terlalu rumit, bisa saja memilih implementasi waktu kuadratik yang sederhana, tetapi default-nya saya berusaha menetapkan di bawah kuadratik jika memungkinkan. Saya juga pernah menulis tentang ini: https://kevincox.ca/2023/05/09/less-than-quadratic/
Jadi ini mungkin lebih cocok 40 tahun lalu, ketika CPU tidak secepat itu dibanding memori, dan branch misprediction belum terlalu dipedulikan di hardware konsumen
Setiap kali wawancara, hiring manager menginginkannya, tetapi pemula LeetCode yang belum pernah mengalami luka produksi menolak keputusan itu
https://www.frankmcsherry.org/assets/COST.pdf
Melihat peningkatan performa komputer selama 20 tahun terakhir, menaikkan patokan itu menjadi 100 ribu terasa cukup masuk akal
Aforisme terkenal “optimisasi prematur adalah akar dari segala kejahatan” sebenarnya berasal dari Donald Knuth, bukan Tony Hoare, dan sering dipakai tanpa konteks seolah-olah menentang optimisasi secara umum
Kalimat lengkapnya adalah: “Kita sebaiknya melupakan efisiensi kecil, katakanlah dalam 97% kasus. Optimisasi prematur adalah akar dari segala kejahatan. Namun kita tidak boleh melewatkan peluang pada 3% yang penting”
Intinya adalah luangkan waktu untuk mengoptimalkan bagian yang memang berdampak
Tampaknya mungkin Tony yang mengatakannya lebih dulu dan Knuth yang memoles lalu menerbitkannya. Selalu baik menyertakan kutipan panjang yang memberi konteks yang diperlukan
Pemrograman saat itu sangat berbeda dari sekarang. “Optimisasi prematur” pada masa itu bukan “pakai saja pustaka populer yang skalabel”, melainkan lebih dekat ke “pakai algoritma manipulasi bit yang tidak bisa dipahami dan hanya berjalan di perangkat keras ini”
Makna itu sudah terkandung dalam ungkapan “optimisasi prematur adalah akar dari segala kejahatan”; aforismenya bukan “optimisasi adalah akar dari segala kejahatan”
Dalam wawancara struktur data dan algoritma di perusahaan, saya tak terhitung sering melihat developer frontend mengatakan bahwa bubble sort adalah pilihan terbaik. Tidak perlu sampai menurunkannya di tempat; cukup mengetahui beberapa opsi dan bisa menyebutkan pilihan yang baik untuk masalahnya
Jika seseorang hidup terlalu ekstrem dengan “jangan melakukan optimisasi prematur” sampai bahkan tidak tahu cara yang efisien, bagaimana ia bisa tahu bagian mana yang penting?
Pernyataan “struktur data adalah inti” menjadi dua kali lebih penting dalam database
Orang yang memakai DB sekadar sebagai tempat penyimpanan bit bodoh atau pantulan 1:1 dari definisi objek sering terkejut ketika DB menganggapnya personal dan merusak performa
Kalau saya harus melihat lagi skema DB yang dihasilkan ORM, rasanya pertemuan kembali itu terlalu cepat
Masalahnya adalah sebagian, atau banyak, developer tidak tahu SQL, dan juga tidak punya pengetahuan DB yang diperlukan untuk memakai ORM
ORM adalah abstraksi yang cukup bocor, sehingga kita perlu tahu apa yang ada di bawahnya. Jika itu dipahami, dengan kebanyakan ORM pun kita bisa membuat skema yang layak
Untuk mengorganisasi struktur data dengan baik dan mempertahankannya ketika desain berubah, data dan kode harus dipisahkan pada tingkat organisasi
Desain skema DB, use case, dan pemetaan di antaranya harus dipisahkan dari implementasi lainnya, dan kelompok ini juga harus menulis pemeriksaan integritas, dan sebagainya. Jika struktur organisasi tidak memisahkan data dan kode, sulit memisahkan kode dan data
Aturan tambahan saya adalah bahwa pemborosan performa kecil, jika menumpuk, pada akhirnya membuat program lambat meskipun masing-masing tampak sepele
Jika tidak berdampak pada kompleksitas, keterbacaan, kemudahan pemeliharaan, atau biaya implementasi, performa jangan dibiarkan begitu saja. Jika kondisi lain hampir sama, memilih opsi yang lebih lambat dari dua pilihan bukanlah hal yang baik
Selain itu, jika kita berasumsi n kecil, hampir apa pun bisa berjalan. Namun jika memakai kode yang berjalan baik untuk n di bawah 100 tetapi rusak di atas 10000, misalnya O(n²), maka sebaiknya tetapkan saja batasnya. Jika asumsi n kecil terpatahkan, lebih baik gagal dengan error besar daripada tagihan AWS meledak atau program macet
Banyak dari pedoman ini pada akhirnya bermuara pada strategi untuk mencegah overengineering
Menurut pengalaman saya, optimisasi prematur adalah salah satu jebakan paling mahal. Jika kita menghindari masalah potensial terlalu dini, asumsi itu tidak tervalidasi, dan tim berikutnya harus membuat solusi mahal untuk mengatasi kompleksitas yang tidak perlu
Pendekatan yang saya pelajari adalah ini: optimisasi bergantung pada estimasi, dan estimasi di tahap awal sering salah
Saya juga menyadari bahwa agar orang tidak membuat kode yang terlalu rumit, mengelola ego dan memahami psikologi ternyata cukup penting
Overproduction biasanya dianggap sebagai pemborosan terburuk, karena bukan hanya membuat sesuatu yang tidak dibutuhkan, tetapi juga menghabiskan upaya yang sebenarnya bisa dipakai untuk hal yang benar-benar dibutuhkan. Overengineering juga serupa
Misalnya, meski pengguna baru 100 orang, kita memulai dengan arsitektur microservices karena suatu hari jika menjadi 1 juta pengguna, mendesain ulang monolith akan sulit
Jadi yang perlu ditangani lebih dulu adalah mengapa seiring waktu kode menjadi kurang lentur
Secara umum ini aturan yang bagus, tetapi dalam praktiknya aturan nomor 1 tidak berlaku begitu saja
Saat memulai, kita perlu hipotesis tentang apa yang akan menjadi bottleneck. Tidak selalu mungkin sekadar mengimplementasikan XYZ lalu mengukur apa yang lambat dan memperbaikinya. Karena X, Y, dan Z saling terhubung, kadang X dan Z harus dibuat dengan cara tertentu agar Y bisa cepat, dan kadang kita sudah tahu bahwa Y akan menjadi bottleneck
Bahkan setelah nanti mengukur dan mengetahui apa yang lambat, kita tetap harus bertaruh pada pendekatan untuk membuatnya lebih cepat. Semakin terdidik taruhannya, semakin baik
Programmer yang baik memang mengukur, tetapi mereka juga bisa memprediksi apa yang akan lambat, banyak bug, dan boros memori, sehingga iterasinya lebih sedikit. Mengatakan seolah-olah perilaku performa tidak bisa diprediksi sebagai sebuah aturan berarti mengabaikan pengalaman dan keterampilan yang telah dibangun programmer yang baik
Sebab proses menaati aturan nomor 1 justru merupakan cara terbaik untuk memperoleh pengalaman dan latar empiris yang dibutuhkan bagi intuisi yang baik dalam memperkirakan bottleneck
Jika prediksi kecepatan salah, proyek akan membawa kode yang rumit tanpa perlu sepanjang masa hidupnya
Orang sering salah menilai kecepatan algoritma. Jika komputer menghabiskan 99% waktunya untuk mengambil n dari server DB, O(n) dan O(n²) sering tampak sama dalam waktu nyata
Algoritma yang ditulis dalam C kadang lebih lambat daripada kode Python yang setara, mungkin karena bytecode compiler melakukan sesuatu yang cerdas
Saya sudah sering membuat kode legacy menjadi lebih cepat, dan biasanya jauh lebih mudah daripada yang dibayangkan, serta lambat karena alasan yang tidak jelas bagi penulis aslinya. Dalam praktiknya, sering kali kode itu lambat karena codebase sudah menjadi terlalu kompleks sehingga penulis aslinya tidak lagi bisa menalarinya. Bagi saya ada contoh konkret “terlalu lambat”, jadi mudah menjalankannya sambil mengamati titik lambat dan melakukan debugging
Jika Anda membuat video game dengan banyak objek fisik dan, dari pengalaman, yakin bahwa deteksi tabrakan akan menjadi masalah besar, merancang game dan sistem berpusat pada hal itu bukanlah speed hack
Jika Anda tahu performa akan menjadi perhatian besar dalam pekerjaan tersebut, tentu saja harus diukur. Bukan untuk memastikan apakah itu perhatian, melainkan untuk memastikan seberapa baik Anda menangani perhatian tersebut
Jika membuat sistem baru untuk kebutuhan baru, sering kali rasanya tidak masalah untuk mulai saja. Buat, uji dan ukur, buang atau refactor, lalu ulangi
Ambil Rust sebagai contoh: dimulai dari bahasa rancangan awal dan compiler yang dibuat dengan OCaml, lalu diiterasi. Meski sejak awal diketahui suatu hari bisa pindah dari OCaml ke self-hosting, saya tidak yakin itu akan membuat perbedaan besar
Jika tidak ada pengguna, fungsi yang butuh beberapa jam pun masih cukup cepat dibanding fungsi yang setelah dioptimalkan menjadi milidetik. Saya tidak tahu apakah ada orang yang sudah membuktikan mampu membuat prediksi semacam itu secara akurat
Sebagai bantahan terhadap nomor 5, algoritma kompleks di atas data sederhana bisa memberi peningkatan performa besar, menghilangkan hambatan, dan justru menyederhanakan
Misalnya, jika memakai binary search pada array terurut alih-alih objek BinaryTree, merge menjadi sederhana karena cukup concat lalu sort, tidak ada pointer sehingga serialisasi mudah, dan dalam beberapa kasus serialisasi itu sendiri tidak diperlukan. Array bisa berada di disk, di memori, atau keduanya lewat mmap; bisa menangani data yang lebih besar dari RAM; dan memungkinkan cold start dengan hanya menunjuk ke file atau mapping lalu langsung berjalan. Ada juga sifat cache-oblivious
Huffman coding juga contohnya. Di universitas biasanya kita belajar algoritma berbasis tree dengan kompleksitas O(n log n), tetapi saya tidak tahu bahwa ada cara membangun Huffman tree berbasis array in-place dalam waktu linear
Tentu saja 99% waktu kita membuat backend microservice dan memakai struktur data koleksi standar. Namun jika di kantor mengerjakan big data, saya jauh lebih memilih memprosesnya di satu mesin lokal dengan disk besar daripada mengadopsi keluarga MapReduce yang sedang tren saat itu
Rob Pike mungkin akan berkata untuk terlebih dahulu melakukan profiling pada kode, lalu melihat apakah kode keren atau struktur data alternatif itu benar-benar lebih cepat
Saya pertama kali membaca tulisan ini di cat-v lebih dari 10 tahun lalu, dan itu meninggalkan pengaruh yang tidak terhapuskan pada cara saya mendekati dan memikirkan desain serta kompleksitas
http://doc.cat-v.org/bell_labs/pikestyle
Saya tidak paham bagaimana aturan asli “struktur data adalah kuncinya” bisa dipangkas menjadi “tulislah kode bodoh yang memakai objek pintar”
Ungkapan “smart objects” sangat buruk, dan aturan aslinya jauh lebih baik meski lebih panjang