Aturan Pemrograman Rob Pike Masih Relevan Hingga Kini
Rob Pike menulis lima aturan pemrograman pada 1989. Aturan itu masih relevan, terutama yang sering kita abaikan.

Pada 1989, Rob Pike — yang kemudian ikut menciptakan Go, UTF-8, dan Plan 9 — menuliskan lima aturan pemrograman. Aturannya cukup singkat untuk muat di sebuah kartu indeks, tapi cukup dalam sehingga komunitas programming masih memperdebatkannya hingga 37 tahun kemudian. Kebanyakan developer pernah melihat beberapa di antaranya dikutip di blog atau konferensi. Yang benar-benar memahaminya jumlahnya lebih sedikit, dan itu sayang sekali, karena aturan ini bisa menghemat banyak usaha yang terbuang.
Aturan-aturannya kelihatan sederhana, tapi jangan tertipu. Aturan ini bukan soal sintaks atau pola arsitektur. Intinya adalah di mana programmer secara konsisten membuang waktu, dan bagaimana cara berhenti melakukannya.
Lima Aturannya
Biar saya sebutkan langsung sebelum membahasnya satu per satu:
- Kamu tidak bisa menebak di mana program akan menghabiskan waktunya. Bottleneck muncul di tempat yang mengejutkan, jadi jangan sok tahu dan menambahkan speed hack sebelum kamu membuktikan bahwa di situlah bottleneck-nya.
- Ukur dulu. Jangan melakukan tuning kecepatan sebelum kamu mengukur, dan bahkan setelah itu, jangan lakukan kecuali ada satu bagian kode yang jauh lebih berat dari bagian lainnya.
- Algoritma yang rumit justru lambat ketika n kecil, dan n biasanya kecil. Algoritma rumit punya konstanta besar. Sampai kamu tahu bahwa n kemungkinan besar akan sering besar, jangan tampil keren-kerenan.
- Algoritma rumit lebih rawan bug dibanding yang sederhana, dan jauh lebih sulit diimplementasikan. Pakai algoritma sederhana dan struktur data sederhana.
- Data itu yang menentukan. Jika kamu sudah memilih struktur data yang tepat dan mengorganisasinya dengan baik, algoritmanya hampir selalu jelas dengan sendirinya. Struktur data, bukan algoritma, adalah inti dari pemrograman.
Aturan 1 dan 2 membahas optimasi. Aturan 3 dan 4 membahas kompleksitas. Aturan 5 membahas desain. Jika digabungkan, ketiganya membentuk filosofi yang pada dasarnya soal kerendahan hati: mengakui bahwa intuisi kita soal performa sering keliru, bahwa kompleksitas punya biaya yang sering kita remehkan, dan bahwa struktur data yang baik lebih penting daripada kode yang pintar-pintaran.
Aturan 1: Kamu Tidak Tahu di Mana Bottleneck-nya
Ini aturan yang paling sering dilanggar developer, dan biasanya dengan penuh percaya diri. 'Saya tahu fungsi ini lambat karena ada nested loop.' 'Saya harus pakai hash map di sini karena lookup-nya O(1).' 'Saya akan preallocate array ini karena alokasi itu mahal.' Kedengarannya masuk akal. Tapi sering kali salah.
Saya sudah cukup sering profiling sistem production sampai punya kumpulan contoh di mana bottleneck yang disangka ternyata bukan bottleneck sebenarnya. Ada sistem di mana semua orang mengira database-nya yang bermasalah, padahal profiling menunjukkan serialisasi JSON memakan 60% waktu request. Ada data pipeline di mana perkalian matriks yang dikira 'mahal' hanya memakan 5% runtime, sementara parsing CSV memakan 70%. Ada aplikasi web di mana tim menghabiskan berbulan-bulan mengoptimasi query database, padahal bottleneck sebenarnya adalah resolusi DNS di setiap outbound HTTP request.
Otak manusia itu profiler yang buruk. Kita melebihkan bobot operasi yang secara konsep terlihat mahal (query database, network call) dan meremehkan operasi yang terlihat murah (string concatenation, parsing JSON, alokasi memori). Hardware modern membuat keadaan ini makin parah. CPU cache, branch prediction, dan out-of-order execution membuat hubungan antara kompleksitas kode dan waktu eksekusi jadi sangat tidak intuitif.
Aturan 2: Ukur Dulu, Baru Optimasi
Ini adalah turunan praktis dari Aturan 1. Jangan mengoptimasi berdasarkan intuisi. Lakukan profiling. Temukan hotspot yang sebenarnya. Baru optimasi bagian itu, dan hanya bagian itu.
Separuh kedua aturan ini — 'jangan kecuali ada satu bagian kode yang jauh lebih berat dari yang lain' — sama pentingnya, tapi jarang dikutip. Kalau profiler menunjukkan waktu eksekusi tersebar merata ke 20 fungsi, masing-masing 5%, berarti tidak ada satu bottleneck pun yang bisa dioptimasi. Membuat satu fungsi 2x lebih cepat hanya menghemat 2,5% total runtime. Itu jarang sepadan dengan tambahan kompleksitasnya. Kamu perlu pendekatan yang benar-benar berbeda, bukan sekadar mengoptimasi fungsi satu per satu.
# Profile first. Always.
# Python
python -m cProfile -s cumulative my_script.py
# Node.js
node --prof app.js
node --prof-process isolate-*.log > profile.txt
# Go
go test -bench=. -cpuprofile=cpu.prof
go tool pprof cpu.prof
# Rust
cargo flamegraph
# General (Linux)
perf record ./my_program && perf report
# The flamegraph (brendangregg.com/flamegraphs) is the most
# informative visualization. It shows you exactly where time
# is spent, in a format that makes bottlenecks visually obvious.
Aturan 3: Algoritma Rumit Itu Lambat Saat N Kecil
Ini aturan yang justru terbalik dalam pendidikan ilmu komputer. Kita diajari bahwa O(n log n) lebih baik dari O(n²), dan secara asimtotik itu benar. Tapi untuk n = 20, insertion sort O(n²) yang diimplementasikan dengan baik justru lebih cepat dari merge sort O(n log n), karena faktor konstanta, perilaku cache, dan overhead.
Contoh di dunia nyata sangat banyak. Linear search pada array terurut berisi 50 elemen lebih cepat dari binary search, karena linear search punya perilaku cache yang sempurna dan tidak ada branch misprediction. Linked list sederhana mengalahkan balanced binary tree untuk koleksi di bawah ~100 elemen, karena pointer chasing di tree merusak cache locality. Hash map punya lookup amortized O(1), tapi konstantanya cukup tinggi sehingga linear search pada array lebih cepat untuk koleksi di bawah ~30-50 elemen.
Standard library tahu hal ini. sorted() di Python menggunakan Timsort, yang beralih ke insertion sort untuk subsequence kecil. std::sort di C++ berpindah ke insertion sort di bawah ambang batas (biasanya 16-32 elemen). sort_unstable di Rust menggabungkan quicksort dan insertion sort. Algoritma 'canggih' hanya dipakai saat n benar-benar cukup besar sehingga ia memang menang.
Pelajaran yang lebih luas: kenali n-mu. Jika kamu sedang memilih antara algoritma O(n²) yang sederhana dan O(n log n) yang kompleks, tanyakan pada diri sendiri seberapa besar n-nya di praktik. Jika jumlahnya di bawah beberapa ratus, algoritma sederhana hampir pasti sudah cukup, dan lebih mudah ditulis, di-debug, serta dirawat.
Aturan 4: Sederhana Lebih Baik Daripada Pintar-Pintaran
Aturan 4 memperluas Aturan 3 melampaui soal performa. Algoritma rumit bukan hanya lebih lambat untuk n kecil, tapi juga lebih rawan bug. Red-black tree punya lebih banyak edge case dibanding array terurut. Struktur data concurrent lock-free punya mode kegagalan yang lebih halus dibanding yang diproteksi mutex. Custom memory allocator punya lebih banyak cara untuk merusak memori dibanding system allocator.
Saya pernah melihat tim menghabiskan berminggu-minggu mengimplementasikan dan men-debug custom LRU cache dengan operasi O(1), padahal bounded array sederhana dengan eviction linear bisa ditulis dalam satu sore, benar sejak percobaan pertama, dan cukup cepat untuk beban kerja mereka (yang paling banyak hanya beberapa ratus entri cache).
Biaya kompleksitas bukan hanya di implementasi awal. Biayanya ada pada setiap developer di masa depan yang harus memahami, memodifikasi, dan men-debug kode itu. Algoritma sederhana yang dipahami semua anggota tim jauh lebih berharga daripada algoritma pintar yang hanya bisa dirawat oleh penulisnya. Dan penulis aslinya, enam bulan kemudian, pada dasarnya sudah menjadi orang lain yang juga lupa bagaimana cara kerjanya.
Men-debug dua kali lebih sulit daripada menulis kodenya di awal. Maka, jika kamu menulis kode sepintar mungkin, secara definisi kamu tidak cukup pintar untuk men-debug-nya. — Brian Kernighan
Aturan 5: Data Menentukan Segalanya
Ini aturan paling penting dari Pike, dan yang paling sering terlewat oleh developer yang terlalu fokus pada algoritma dan design pattern. Klaimnya: jika struktur datamu tepat, algoritmanya akan mengikuti secara alami. Jika struktur datamu keliru, sekecil apa pun kepintaran algoritma tidak akan menyelamatkanmu.
Fred Brooks pernah mengatakan hal serupa: 'Tunjukkan flowchart-mu dan sembunyikan tabel-tabelmu, dan saya akan terus bingung. Tunjukkan tabel-tabelmu, dan saya biasanya tidak butuh flowchart-mu.' Linus Torvalds juga menggemakannya: 'Programmer yang buruk khawatir soal kode. Programmer yang baik khawatir soal struktur data dan relasinya.'
Prinsip ini terus muncul dalam praktik. Codebase yang menyimpan permission user sebagai daftar datar string permission akan menumpuk logika pengecekan yang kompleks dan rawan error, tersebar di seluruh kode. Susun ulang datanya sebagai hierarki role, dan logika pengecekannya jadi sepele. Sistem yang menyimpan event sebagai JSON blob akan membutuhkan parsing dan validasi yang rumit di setiap consumer. Susun event sebagai record bertipe dengan skema eksplisit, dan consumer-nya akan jauh lebih sederhana.
# Bad data structure → complex algorithm
class OrderSystem:
def __init__(self):
self.orders = [] # flat list of all orders
def get_user_orders(self, user_id):
return [o for o in self.orders if o.user_id == user_id] # O(n)
def get_pending_orders(self):
return [o for o in self.orders if o.status == 'pending'] # O(n)
def get_user_pending_orders(self, user_id):
return [o for o in self.orders
if o.user_id == user_id and o.status == 'pending'] # O(n)
# Better data structure → algorithms become obvious
class OrderSystem:
def __init__(self):
self.orders_by_user = {} # user_id → [orders]
self.orders_by_status = {} # status → [orders]
def get_user_orders(self, user_id):
return self.orders_by_user.get(user_id, []) # O(1)
def get_pending_orders(self):
return self.orders_by_status.get('pending', []) # O(1)
# The right data structure makes the code self-evident.
# You don't need to think about algorithms at all.
Bagaimana Go Mewujudkan Aturan Ini
Sulit membaca aturan Pike tanpa melihat filosofi desain Go dalam bentuk awalnya. Go, yang Pike ikut ciptakan 20 tahun setelah menulis aturan ini, adalah bahasa yang secara sistematis mengutamakan kesederhanaan dibanding kepintaran.
- Tidak ada generics (awalnya) — memaksa struktur data yang sederhana. (Generics ditambahkan di Go 1.18, tapi baru setelah bertahun-tahun menolaknya sampai ditemukan desain yang cukup sederhana.)
- Tidak ada operator overloading — kode berarti persis seperti yang terlihat.
- Tidak ada konversi tipe implisit — eksplisit lebih utama daripada pintar-pintaran.
- Tidak ada exception — tangani error di tempat terjadinya.
- Algoritma di standard library minimal — gunakan slice dan map, bukan struktur data yang rumit.
- Profiling bawaan (
pprof) — ukur, jangan menebak.
Go sering dikritik sebagai 'membosankan' oleh developer yang lebih suka bahasa yang lebih ekspresif. Justru di situlah poinnya. Aturan Pike adalah resep untuk kode yang membosankan — kode yang sederhana, terukur, dan dibangun di atas struktur data yang baik, bukan algoritma yang pintar-pintaran. Go adalah hasil ketika resep itu diubah menjadi sebuah bahasa.
Di Mana Aturannya Tidak Berlaku
Tidak ada aturan yang berlaku universal, dan aturan Pike punya pengecualian yang sah. Sistem yang sensitif performa (game engine, internal database, compiler) kadang butuh algoritma yang rumit karena n-nya memang besar. Kode infrastruktur yang dijalankan jutaan kali per detik membenarkan optimasi yang tidak perlu dilakukan di kode aplikasi. Dan kadang algoritma 'sederhana' punya kompleksitas O(n³) yang benar-benar tidak bisa diterima bahkan untuk n yang sedang.
Aturan ini adalah heuristik, bukan hukum. Nilainya ada pada koreksi terhadap bias yang umum: developer cenderung mengoptimasi terlalu dini, memakai algoritma yang terlalu rumit, dan terlalu banyak memikirkan kode sementara terlalu sedikit memikirkan struktur data. Aturan Pike melawan kecenderungan itu. Jika kamu berada dalam situasi langka di mana bias sebaliknya yang berlaku — di mana kamu memang butuh lebih banyak kompleksitas, bukan lebih sedikit — silakan gunakan yang canggih. Tapi ukur dulu.
Tiga puluh tujuh tahun sejak Pike menuliskannya, aturan ini tetap menjadi salah satu nasihat pemrograman terbaik yang pernah dipublikasikan. Bukan karena mengejutkan — kebanyakan developer berpengalaman yang membacanya akan berpikir 'ya, jelas.' Nilainya ada pada penyampaiannya yang cukup jelas untuk diterapkan secara konsisten. Lain kali kamu hendak memakai red-black tree, custom allocator, atau 'optimasi' yang belum kamu profiling — ingat: ukur dulu, jaga kesederhanaan, dan pastikan struktur datamu tepat.


