Jelajahi gagasan John Ousterhout tentang desain perangkat lunak praktis, warisan Tcl, debat Ousterhout vs Brooks, dan bagaimana kompleksitas dapat menenggelamkan produk.

John Ousterhout adalah ilmuwan komputer dan insinyur yang karyanya melintasi riset dan sistem nyata. Ia menciptakan bahasa pemrograman Tcl, membantu membentuk sistem berkas modern, dan kemudian merangkum puluhan tahun pengalaman menjadi sebuah klaim sederhana—sedikit tidak nyaman: kompleksitas adalah musuh utama perangkat lunak.
Pesan itu masih relevan karena kebanyakan tim tidak gagal karena kurang fitur atau usaha—mereka gagal karena sistem (dan organisasi) mereka menjadi sulit dipahami, sulit diubah, dan mudah rusak. Kompleksitas tidak hanya memperlambat insinyur. Ia merembes ke keputusan produk, kepercayaan roadmap, kepercayaan pelanggan, frekuensi insiden, bahkan perekrutan—karena proses onboarding bisa berubah menjadi tugas berbulan-bulan.
Pembingkaian Ousterhout bersifat praktis: ketika sebuah sistem mengumpulkan kasus khusus, pengecualian, ketergantungan tersembunyi, dan perbaikan “hanya kali ini”, biayanya tidak terbatas pada basis kode. Seluruh produk menjadi lebih mahal untuk dikembangkan. Fitur butuh lebih lama, QA menjadi lebih sulit, rilis menjadi lebih berisiko, dan tim mulai menghindari perbaikan karena menyentuh apa pun terasa berbahaya.
Ini bukan seruan untuk kemurnian akademis. Ini pengingat bahwa setiap jalan pintas memiliki cicilan—dan kompleksitas adalah utang dengan bunga tertinggi.
Agar ide ini konkret (dan tidak sekadar motivasional), kita akan melihat pesan Ousterhout melalui tiga sudut:
Ini tidak ditulis hanya untuk penggila bahasa. Jika Anda membangun produk, memimpin tim, atau membuat trade-off roadmap, Anda akan menemukan cara tindakan untuk mendeteksi kompleksitas lebih awal, mencegahnya menjadi institusional, dan memperlakukan kesederhanaan sebagai kendala kelas-satu—bukan sekadar hal yang bagus setelah peluncuran.
Kompleksitas bukanlah “banyak kode” atau “matematika sulit.” Itu adalah celah antara apa yang Anda pikir sistem akan lakukan saat Anda mengubahnya dan apa yang sebenarnya terjadi. Sebuah sistem kompleks ketika pengeditan kecil terasa berisiko—karena Anda tidak bisa memprediksi radius dampaknya.
Dalam kode yang sehat, Anda bisa menjawab: “Jika kita mengubah ini, apa lagi yang mungkin rusak?” Kompleksitas membuat pertanyaan itu mahal.
Seringkali ia bersembunyi dalam:
Tim merasakan kompleksitas sebagai pengiriman lebih lambat (lebih banyak waktu menyelidiki), lebih banyak bug (karena perilaku mengejutkan), dan sistem rapuh (perubahan membutuhkan koordinasi antar banyak orang dan layanan). Ini juga membebani onboarding: rekan baru tidak bisa membangun model mental, jadi mereka menghindari menyentuh alur inti.
Beberapa kompleksitas adalah esensial: aturan bisnis, persyaratan kepatuhan, kasus tepi di dunia nyata. Anda tidak bisa menghapusnya.
Tetapi banyak yang aksidental: API yang membingungkan, logika duplikat, flag “sementara” yang jadi permanen, dan modul yang membocorkan detail internal. Inilah kompleksitas yang pilihan desain ciptakan—dan satu-satunya jenis yang bisa Anda bayarkan secara konsisten untuk dihapus.
Tcl dimulai dengan tujuan praktis: mempermudah otomasi perangkat lunak dan memperluas aplikasi tanpa menulis ulang semuanya. John Ousterhout merancangnya agar tim bisa menambahkan "programabilitas yang cukup" ke sebuah alat—lalu menyerahkan kekuatan itu kepada pengguna, operator, QA, atau siapa pun yang perlu menulis skrip alur kerja.
Tcl mempopulerkan gagasan bahasa lem: lapisan scripting kecil dan fleksibel yang menghubungkan komponen yang ditulis di bahasa yang lebih cepat/tingkat rendah. Alih-alih membangun setiap fitur ke monolit, Anda dapat mengekspos sekumpulan perintah, lalu menggabungkannya menjadi perilaku baru.
Model itu berpengaruh karena sesuai dengan bagaimana pekerjaan sebenarnya terjadi. Orang tidak hanya membangun produk; mereka membangun sistem build, harness pengujian, alat admin, konverter data, dan otomasi sekali pakai. Lapisan scripting ringan mengubah tugas-tugas itu dari “ajukan tiket” menjadi “tulis skrip.”
Tcl membuat embedding menjadi concern utama. Anda bisa memasukkan interpreter ke dalam aplikasi, mengekspos antarmuka perintah yang bersih, dan segera mendapatkan kemampuan konfigurasi dan iterasi cepat.
Polanya muncul hari ini di sistem plugin, bahasa konfigurasi, API ekstensi, dan runtime scripting ter-embed—baik sintaks skripnya mirip Tcl atau tidak.
Ia juga memperkuat kebiasaan desain penting: pisahkan primitif stabil (kemampuan inti host app) dari komposisi yang mudah diubah (skrip). Ketika bekerja, alat berkembang lebih cepat tanpa terus-menerus mendestabilisasi inti.
Sintaks Tcl dan model “segala sesuatu adalah string” bisa terasa tidak intuitif, dan basis kode Tcl besar terkadang sulit dipahami tanpa konvensi ketat. Ketika ekosistem baru menawarkan pustaka standar lebih kaya, tooling lebih baik, dan komunitas lebih besar, banyak tim berpindah.
Itu tidak menghapus warisan Tcl: ia menormalkan gagasan bahwa extensibility dan otomasi bukanlah ekstra—mereka adalah fitur produk yang bisa sangat mengurangi kompleksitas bagi orang yang menggunakan dan memelihara sistem.
Tcl dibangun di sekitar ide yang tampak sederhana: jaga inti kecil, buat komposisi kuat, dan pertahankan skrip agar cukup terbaca sehingga orang bisa bekerja bersama tanpa terjemahan terus-menerus.
Alih-alih mengirimkan set fitur besar yang khusus, Tcl mengandalkan seperangkat primitif kompak (string, perintah, aturan evaluasi sederhana) dan mengharapkan pengguna menggabungkannya.
Filosofi itu mendorong perancang ke arah sedikit konsep yang dipakai ulang di banyak konteks. Pelajarannya untuk desain produk dan API jelas: jika Anda bisa menyelesaikan sepuluh kebutuhan dengan dua atau tiga blok bangunan konsisten, Anda mengecilkan permukaan yang harus dipelajari orang.
Jebakan utama dalam desain perangkat lunak adalah mengoptimalkan kenyamanan pembuat. Sebuah fitur bisa mudah diimplementasikan (salin opsi yang ada, tambahkan flag khusus, tambal kasus pinggir) sementara membuat produk lebih sulit dipakai.
Tcl menekankan sebaliknya: jaga model mental ketat, meski implementasinya harus bekerja lebih banyak di balik layar.
Saat meninjau proposal, tanyakan: apakah ini mengurangi jumlah konsep yang harus diingat pengguna, atau menambahkan satu pengecualian lagi?
Minimalisme hanya membantu jika primitif konsisten. Jika dua perintah terlihat mirip tetapi berperilaku berbeda di kasus tepi, pengguna akhirnya menghafal trivia. Sekelompok kecil alat dapat menjadi “tepi tajam” ketika aturan bervariasi secara halus.
Bayangkan dapur: pisau, penggorengan, dan oven yang baik memungkinkan Anda membuat banyak hidangan dengan menggabungkan teknik. Gadget yang hanya mengiris alpukat adalah fitur sekali pakai—mudah dijual, tetapi memenuhi laci. Filosofi Tcl menyarankan pisau dan penggorengan: alat umum yang bisa dikombinasikan dengan bersih, sehingga Anda tidak memerlukan gadget baru untuk setiap resep baru.
Pada 1986, Fred Brooks menulis esai dengan kesimpulan yang sengaja provokatif: tidak ada terobosan tunggal—tidak ada “peluru perak”—yang akan membuat pengembangan perangkat lunak menjadi sepuluh kali lebih cepat, lebih murah, dan lebih andal dalam satu lompatan.
Poinnya bukan bahwa kemajuan tidak mungkin. Itu bahwa perangkat lunak sudah merupakan medium di mana kita bisa melakukan hampir apa saja, dan kebebasan itu membawa beban unik: kita terus-menerus mendefinisikan hal yang sama saat kita membangunnya. Alat yang lebih baik menolong, tetapi mereka tidak menghapus bagian tersulit pekerjaan.
Brooks membagi kompleksitas menjadi dua ember:
Alat bisa menghancurkan kompleksitas aksidental. Pikirkan apa yang kita dapatkan dari bahasa tingkat tinggi, version control, CI, container, database terkelola, dan IDE yang baik. Tapi Brooks berargumen bahwa kompleksitas esensial mendominasi, dan ia tidak menghilang hanya karena tooling meningkat.
Bahkan dengan platform modern, tim masih menghabiskan sebagian besar energi menegosiasikan kebutuhan, mengintegrasikan sistem, menangani pengecualian, dan menjaga konsistensi perilaku sepanjang waktu. Permukaan mungkin berubah (API cloud bukannya driver perangkat), tetapi tantangan inti tetap: menerjemahkan kebutuhan manusia menjadi perilaku yang tepat dan terpelihara.
Ini menyiapkan ketegangan yang dimanfaatkan Ousterhout: jika kompleksitas esensial tidak bisa dihapus, apakah desain disiplin bisa mengurangi seberapa banyak darinya bocor ke kode—dan ke kepala pengembang sehari-hari?
Orang kadang menggambarkan “Ousterhout vs Brooks” sebagai pertarungan antara optimisme dan realisme. Lebih berguna membacanya sebagai dua insinyur berpengalaman yang menggambarkan bagian berbeda dari masalah yang sama.
“No Silver Bullet” Brooks berargumen tidak ada terobosan tunggal yang menghapus bagian tersulit perangkat lunak. Ousterhout tidak benar-benar membantah itu.
Dorongan baliknya lebih sempit dan praktis: tim sering memperlakukan kompleksitas sebagai sesuatu yang tak terhindarkan padahal banyak di antaranya adalah self-inflicted.
Dalam pandangan Ousterhout, desain yang baik bisa mengurangi kompleksitas secara signifikan—bukan dengan membuat perangkat lunak “mudah”, tetapi dengan membuatnya kurang membingungkan untuk diubah. Itu klaim besar, dan penting karena kebingunganlah yang mengubah pekerjaan sehari-hari menjadi pekerjaan lambat.
Brooks menekankan kesulitan esensial: perangkat lunak harus memodelkan realitas yang berantakan, kebutuhan yang berubah, dan kasus tepi di luar basis kode. Bahkan dengan alat hebat dan orang pintar, Anda tidak bisa menghapusnya. Anda hanya bisa mengelolanya.
Mereka tumpang tindih lebih banyak daripada yang disangka:
Daripada bertanya “Siapa yang benar?”, tanyakan: Kompleksitas mana yang bisa kita kendalikan kuartal ini?
Tim tidak bisa mengendalikan perubahan pasar atau kesulitan inti domain. Tetapi mereka bisa mengendalikan apakah fitur baru menambah kasus khusus, apakah API memaksa pemanggil mengingat aturan tersembunyi, dan apakah modul menyembunyikan atau membocorkan kompleksitas.
Itulah tengah jalan yang dapat ditindaklanjuti: terima kompleksitas esensial, dan pilih secara tanpa ampun jenis aksidental yang diciptakan.
Sebuah modul mendalam adalah komponen yang melakukan banyak hal, sambil mengekspos antarmuka kecil yang mudah dipahami. “Kedalaman” adalah jumlah kompleksitas yang modul itu ambil dari meja Anda: pemanggil tidak perlu tahu detail berantakan, dan antarmuka tidak memaksakan mereka.
Sebuah modul dangkal adalah kebalikan: ia mungkin membungkus sedikit logika, tetapi mendorong kompleksitas ke luar—melalui banyak parameter, flag khusus, urutan panggilan yang wajib, atau aturan “Anda harus ingat…”.
Bayangkan restoran. Modul mendalam adalah dapur: Anda memesan “pasta” dari menu sederhana dan tidak peduli soal pilihan pemasok, waktu merebus, atau plating.
Modul dangkal adalah “dapur” yang memberikan bahan mentah dengan lembar instruksi 12 langkah dan meminta Anda membawa penggorengan sendiri. Pekerjaan tetap terjadi—tetapi dipindahkan ke pelanggan.
Lapisan tambahan bisa hebat jika mereka membuat banyak keputusan menjadi satu pilihan jelas.
Contoh: lapisan penyimpanan yang mengekspos save(order) dan menangani retry, serialisasi, dan pengindeksan secara internal adalah mendalam.
Lapisan menyakiti ketika mereka sebagian besar mengganti nama hal atau menambah opsi. Jika abstraksi baru memperkenalkan lebih banyak konfigurasi daripada yang dihapus—misalnya save(order, format, retries, timeout, mode, legacyMode)—maka kemungkinan ia dangkal. Kode mungkin terlihat “terorganisir”, tetapi beban kognitif muncul di setiap call site.
useCache, skipValidation, force, legacy.Modul mendalam tidak hanya "mengkapsulasi kode." Mereka mengenkapsulasi keputusan.
API yang "baik" bukan hanya yang mampu melakukan banyak hal. Ia adalah yang orang bisa pegang dalam kepala saat mereka bekerja.
Lensa desain Ousterhout mendorong Anda menilai API dari usaha mental yang dituntut: berapa banyak aturan yang harus diingat, berapa banyak pengecualian yang harus diprediksi, dan seberapa mudah melakukan kesalahan.
API ramah manusia cenderung kecil, konsisten, dan sulit disalahgunakan.
Kecil bukan berarti kurang bertenaga—itu berarti permukaan terkonsentrasi menjadi beberapa konsep yang bisa dikomposisi. Konsisten berarti pola yang sama bekerja di seluruh sistem (parameter, penanganan error, penamaan, tipe return). Sulit disalahgunakan berarti API membimbing Anda ke jalur aman: invariant jelas, validasi di batas, dan pemeriksaan tipe atau runtime yang gagal lebih awal.
Setiap flag, mode, atau konfigurasi “jaga-jaga” menjadi pajak bagi semua pengguna. Bahkan jika hanya 5% pemanggil yang butuh, 100% pemanggil kini harus tahu opsi itu ada, bertanya-tanya apakah mereka perlu, dan menafsirkan perilaku ketika opsi itu berinteraksi dengan opsi lain.
Inilah cara API mengakumulasi kompleksitas tersembunyi: bukan pada satu panggilan, tetapi pada kombinatoriknya.
Default adalah kebaikan: mereka memungkinkan sebagian besar pemanggil menghilangkan keputusan dan tetap mendapatkan perilaku wajar. Konvensi (satu cara yang jelas untuk melakukannya) mengurangi percabangan di kepala pengguna. Penamaan melakukan kerja nyata juga: pilih kata kerja dan kata benda yang sesuai dengan niat pengguna, dan pertahankan operasi serupa diberi nama serupa.
Satu pengingat lagi: API internal sama pentingnya dengan yang publik. Sebagian besar kompleksitas produk hidup di belakang layar—batas layanan, pustaka bersama, dan modul "helper". Perlakukan antarmuka itu seperti produk, dengan tinjauan dan disiplin versioning (lihat juga /blog/deep-modules).
Kompleksitas jarang tiba sebagai satu “keputusan buruk.” Ia menumpuk melalui tambalan kecil yang terlihat wajar—terutama ketika tim sedang ditekan tenggat dan tujuan langsung adalah mengirim.
Salah satu perangkap adalah feature flag di mana-mana. Flag berguna untuk rollout aman, tetapi ketika bertahan, setiap flag menggandakan jumlah perilaku yang mungkin. Insinyur berhenti memikirkan “sistem” dan mulai memikirkan “sistem, kecuali ketika flag A hidup dan pengguna di segmen B.”
Lainnya adalah logika kasus khusus: "Pelanggan enterprise butuh X", "Kecuali di region Y", "Kecuali akun lebih tua dari 90 hari". Pengecualian ini sering menyebar ke seluruh basis kode, dan setelah beberapa bulan tak ada yang tahu mana yang masih diperlukan.
Ketiga adalah abstraksi bocor. API yang memaksa pemanggil memahami detail internal (timing, format penyimpanan, aturan caching) mendorong kompleksitas ke luar. Alih-alih satu modul memikul beban, setiap pemanggil belajar kekhususan.
Pemrograman taktis mengoptimalkan untuk minggu ini: perbaikan cepat, perubahan minimal, "patch saja".
Pemrograman strategis mengoptimalkan untuk tahun depan: redesign kecil yang mencegah kelas bug yang sama dan mengurangi pekerjaan di masa depan.
Bahaya adalah "bunga pemeliharaan." Workaround cepat terasa murah sekarang, tetapi Anda membayarnya dengan bunga: onboarding lebih lambat, rilis rapuh, dan pengembangan yang digerakkan rasa takut di mana tak ada yang mau menyentuh kode lama.
Tambahkan prompt ringan di code review: "Apakah ini menambah kasus khusus baru?" "Bisakah API menyembunyikan detail ini?" "Kompleksitas apa yang kita tinggalkan?"
Simpan catatan keputusan singkat untuk trade-off yang tidak sepele (beberapa butir saja). Dan sediakan anggaran refactor kecil setiap sprint supaya perbaikan strategis tidak diperlakukan sebagai tugas ekstrakurikuler.
Kompleksitas tidak tetap terkurung di engineering. Ia merembes ke jadwal, keandalan, dan cara pelanggan mengalami produk Anda.
Saat sistem sulit dipahami, setiap perubahan membutuhkan waktu lebih lama. Time-to-market melorot karena setiap rilis butuh lebih banyak koordinasi, lebih banyak pengujian regresi, dan lebih banyak siklus “hanya untuk aman” dalam review.
Keandalan juga menderita. Sistem kompleks menciptakan interaksi yang tak bisa diprediksi sepenuhnya, sehingga bug muncul sebagai kasus tepi: checkout gagal hanya saat kupon, keranjang tersimpan, dan aturan pajak regional bertemu dengan cara tertentu. Itu insiden yang paling sulit direproduksi dan paling lambat diperbaiki.
Onboarding menjadi beban tersembunyi. Rekan baru tidak bisa membangun model mental yang berguna, sehingga mereka menghindari area berisiko, menyalin pola yang tidak mereka mengerti, dan tanpa sengaja menambah kompleksitas.
Pelanggan tidak peduli apakah perilaku disebabkan oleh "kasus khusus" di kode. Mereka mengalaminya sebagai inkonsistensi: pengaturan yang tidak berlaku di mana-mana, alur yang berubah tergantung bagaimana Anda tiba, fitur yang bekerja "sebagian besar waktu." Kepercayaan turun, churn naik, dan adopsi mandek.
Tim dukungan membayar untuk kompleksitas lewat tiket yang lebih panjang dan pertukaran lebih banyak untuk mengumpulkan konteks. Operasi membayar lewat lebih banyak alert, lebih banyak runbook, dan deployment yang lebih hati-hati. Setiap pengecualian menjadi sesuatu untuk dipantau, didokumentasi, dan dijelaskan.
Bayangkan permintaan untuk “satu aturan notifikasi lagi.” Menambahnya terasa cepat, tetapi memperkenalkan cabang perilaku baru, lebih banyak copy UI, lebih banyak kasus uji, dan lebih banyak cara pengguna bisa salah konfigurasi.
Bandingkan dengan menyederhanakan alur notifikasi yang ada: lebih sedikit tipe aturan, default yang lebih jelas, dan perilaku konsisten di web dan mobile. Anda mungkin mengirimkan lebih sedikit tombol konfigurasi, tetapi Anda mengurangi kejutan—membuat produk lebih mudah dipakai, lebih mudah didukung, dan lebih cepat berkembang.
Perlakukan kompleksitas seperti performa atau keamanan: sesuatu yang Anda rencanakan, ukur, dan lindungi. Jika Anda baru menyadari kompleksitas saat pengiriman melambat, Anda sudah membayar bunga.
Di samping cakupan fitur, definisikan berapa banyak kompleksitas baru yang boleh diperkenalkan rilis. Anggaran bisa sederhana: "tidak ada konsep baru bersih kecuali kita menghapus satu", atau "setiap integrasi baru harus menggantikan jalur lama".
Buat trade-off eksplisit saat perencanaan: jika sebuah fitur butuh tiga mode konfigurasi baru dan dua pengecualian, itu harus “berbiaya” lebih banyak daripada fitur yang sesuai konsep yang ada.
Anda tidak perlu angka sempurna—hanya sinyal yang bergerak ke arah yang benar:
Lacak ini per rilis, dan kaitkan ke keputusan: "Kita menambahkan dua opsi publik; apa yang kita hapus atau sederhanakan untuk kompensasi?"
Prototipe sering dinilai dari "Bisakah kita membangunnya?" Sebaliknya, gunakan untuk menjawab: "Apakah ini terasa sederhana untuk dipakai dan sulit disalahgunakan?"
Minta seseorang yang tidak akrab dengan fitur mencoba tugas realistis dengan prototipe. Ukur waktu-untuk-sukses, pertanyaan yang diajukan, dan di mana mereka membuat asumsi salah. Itu hotspot kompleksitas.
Di sinilah workflow build modern bisa mengurangi kompleksitas aksidental—jika mereka menjaga iterasi ketat dan mempermudah rollback. Misalnya, ketika tim menggunakan platform vibecoding seperti Koder.ai untuk membuat sketsa tool internal atau alur baru lewat chat, fitur seperti planning mode (untuk memperjelas niat sebelum generasi) dan snapshots/rollback (untuk membatalkan perubahan berisiko dengan cepat) bisa membuat eksperimen awal terasa lebih aman—tanpa berkomitmen pada set abstraksi setengah jadi. Jika prototipe lulus, Anda masih bisa mengekspor kode sumber dan menerapkan disiplin “modul mendalam” dan desain API yang dijelaskan di atas.
Jadikan pekerjaan “pembersihan kompleksitas” periodik (kuartalan atau setiap rilis besar), dan definisikan apa arti “selesai”:
Tujuannya bukan sekadar kode yang lebih bersih secara abstrak—tetapi lebih sedikit konsep, lebih sedikit pengecualian, dan perubahan yang lebih aman.
Berikut beberapa langkah yang menerjemahkan ide Ousterhout “kompleksitas adalah musuh” menjadi kebiasaan tim mingguan.
Pilih satu subsistem yang sering menimbulkan kebingungan (masalah onboarding, bug berulang, banyak pertanyaan “bagaimana ini bekerja?”).
Tindak lanjut internal yang bisa Anda jalankan: sebuah “complexity review” dalam perencanaan (/blog/complexity-review) dan pemeriksaan cepat apakah tooling Anda mengurangi kompleksitas aksidental alih-alih menambah lapisan (/pricing).
Pertanyaan singkat: satu potongan kompleksitas apa yang akan Anda hapus terlebih dahulu jika Anda hanya boleh menghapus satu kasus khusus minggu ini?
Kompleksitas adalah celah antara apa yang Anda harapkan terjadi saat Anda mengubah sistem dan apa yang sebenarnya terjadi.
Anda merasakannya ketika pengeditan kecil tampak berisiko karena Anda tidak bisa memprediksi radius dampaknya (tes, layanan, konfigurasi, pelanggan, atau kasus tepi yang mungkin Anda pecahkan).
Cari sinyal bahwa penalaran mahal:
Kompleksitas esensial berasal dari domain (peraturan, kasus tepi dunia nyata, aturan bisnis inti). Anda tidak bisa menghapusnya—hanya memodelkannya dengan baik.
Kompleksitas aksidental adalah yang dibuat sendiri (abstraksi bocor, logika terduplikasi, terlalu banyak mode/flag, API yang tidak jelas). Ini bagian yang bisa tim kurangi secara konsisten melalui desain dan penyederhanaan.
Sebuah modul mendalam melakukan banyak hal sambil mengekspos antarmuka yang kecil dan stabil. Modul itu “menyerap” detail berantakan (retries, format, urutan, invariant) sehingga pemanggil tidak perlu tahu.
Tes praktis: jika kebanyakan pemanggil bisa menggunakan modul dengan benar tanpa mengetahui aturan internalnya, itu mendalam; jika pemanggil harus menghafal aturan dan urutan, itu dangkal.
Gejala umum:
legacy, skipValidation, force, mode).Preferensi API yang baik:
Sebelum menambahkan “satu opsi lagi”, tanyakan apakah Anda bisa merancang ulang antarmuka sehingga kebanyakan pemanggil tidak perlu memikirkan pilihan itu sama sekali.
Gunakan flag untuk rollout terkendali, lalu perlakukan mereka sebagai hutang dengan tanggal akhir:
Flag yang bertahan lama menggandakan jumlah “sistem” yang harus dipikirkan insinyur.
Jadikan kompleksitas eksplisit pada perencanaan, bukan hanya saat code review:
Tujuannya memaksa trade-off menjadi terbuka sebelum kompleksitas menjadi terinstitusionalisasi.
Pemrograman taktis mengoptimalkan untuk minggu ini: perbaikan cepat, perubahan minimal, "kirim sekarang".
Pemrograman strategis mengoptimalkan untuk setahun ke depan: desain ulang kecil yang menghilangkan kelas bug berulang dan mengurangi kerja di masa depan.
Heuristik berguna: jika perbaikan membutuhkan pengetahuan pemanggil ("ingat panggil X dulu" atau "set flag ini hanya di prod"), kemungkinan Anda membutuhkan perubahan yang lebih strategis untuk menyembunyikan kompleksitas itu di dalam modul.
Pelajaran tahan lama Tcl adalah kekuatan seperangkat primitif kecil ditambah komposisi yang kuat—seringkali sebagai lapisan "lem" (glue) yang tertanam.
Padanan modern termasuk:
Tujuan desain sama: jaga inti sederhana dan stabil, biarkan perubahan terjadi lewat antarmuka yang bersih.
Modul dangkal sering terlihat terorganisir tetapi memindahkan kompleksitas ke luar ke setiap pemanggil.