
Kode yang mudah di-debug adalah kode yang tidak menipu Anda. Lebih sulit untuk men-debug kode dengan perilaku tersembunyi, dengan penanganan kesalahan yang buruk, dengan ketidakpastian, terstruktur tidak memadai atau berlebihan, atau dalam proses perubahan. Dalam proyek yang cukup besar, Anda berakhir dengan kode yang tidak dapat Anda mengerti.
Jika proyek ini relatif lama, maka Anda mungkin menemukan kode yang Anda lupa sama sekali, dan jika itu bukan untuk log komit, Anda akan bersumpah bahwa baris ini tidak ditulis oleh Anda. Seiring pertumbuhan proyek, menjadi semakin sulit untuk mengingat apa yang dilakukan oleh berbagai kode. Dan situasinya diperburuk jika kode tidak melakukan apa yang tampaknya dilakukan. Dan ketika Anda perlu mengubah kode yang tidak Anda mengerti, Anda harus mengetahuinya dengan keras: debug.
Kemampuan menulis kode yang mudah di-debug dimulai dengan pemahaman bahwa Anda tidak mengingat apa pun yang ditulis sebelumnya.
Aturan 0: Kode yang baik mengandung kesalahan yang jelas.
Vendor teknologi yang banyak digunakan mengklaim bahwa "tulis kode yang jelas" berarti "tulis kode yang bersih." Masalahnya adalah bahwa tingkat βkemurnianβ sangat peka konteks. Kode murni dapat di-hardcode dalam sistem, dan kadang-kadang beberapa hack kotor ditulis sehingga mudah untuk menonaktifkannya. Terkadang kode dianggap bersih, karena semua kotoran didorong ke suatu tempat. Kode yang baik belum tentu bersih.
Kebersihan mencirikan tingkat kebanggaan (atau rasa malu) yang dialami pengembang sehubungan dengan kode ini, bukan kemudahan pemeliharaan atau perubahan. Lebih baik memberi kita kode membosankan daripada yang bersih, perubahan yang jelas: saya menemukan bahwa orang lebih bersedia untuk memodifikasi basis kode jika buah hang cukup rendah dan mudah dipetik. Kode terbaik mungkin adalah kode yang baru saja Anda lihat dan segera mengerti cara kerjanya.
- Kode yang tidak mencoba membuat masalah jelek agar terlihat bagus, atau masalah yang membosankan terlihat menarik.
- Kode di mana kesalahannya jelas dan perilakunya jelas, tidak seperti kode tanpa kesalahan yang jelas dan dengan perilaku tidak jelas.
- Kode di mana itu didokumentasikan di mana itu tidak ideal, berbeda dengan kode yang berusaha untuk kesempurnaan.
- Kode dengan perilaku yang sangat jelas sehingga pengembang mana pun dapat menemukan segudang cara berbeda untuk mengubah kode ini.
Kadang-kadang kode itu sangat jahat sehingga setiap upaya untuk membuatnya lebih bersih hanya memperburuk situasi. Menulis kode tanpa memahami konsekuensi dari tindakan mereka juga dapat dianggap sebagai ritual penerapan kode yang dikelola dengan mudah.
Saya tidak ingin mengatakan bahwa kode bersih itu buruk, tetapi terkadang keinginan untuk kebersihan lebih terlihat seperti menyapu sampah di bawah permadani. Kode debug belum tentu bersih; dan kode yang penuh dengan pemeriksaan atau penanganan kesalahan jarang terbaca.
Aturan 1: Selalu ada masalah di komputer
Komputer memiliki masalah dan program macet selama menjalankan terakhir.
Aplikasi pertama-tama harus memastikan bahwa itu dimulai dari kondisi yang diketahui, baik, aman sebelum mencoba melakukan sesuatu. Terkadang tidak ada salinan negara, karena pengguna menghapusnya atau memutakhirkan komputer. Program macet selama menjalankan terakhir dan, paradoksnya, selama menjalankan pertama juga.
Misalnya, saat membaca atau menulis status ke file, masalah berikut mungkin terjadi:
- File tidak ada.
- File tersebut rusak.
- File versi yang lebih lama, atau lebih baru.
- Perubahan terakhir ke file belum selesai.
- Sistem file berbohong kepada Anda.
Masalah-masalah ini bukanlah hal baru, basis data telah menjumpai mereka sejak zaman kuno (1970-01-01). Menggunakan sesuatu seperti SQLite akan membantu mengatasi banyak masalah serupa, tetapi jika program macet pada eksekusi terakhir, maka kode dapat bekerja dengan data yang salah dan / atau dengan cara yang salah.
Misalnya, dengan program terjadwal, sesuatu dari daftar ini akan terjadi:
- Program ini akan mulai dua kali dalam satu jam karena waktu musim panas.
- Program akan mulai dua kali karena operator lupa bahwa itu sudah berjalan.
- Program akan mulai terlambat karena kehabisan ruang disk kosong atau masalah jaringan atau cloud yang misterius.
- Program akan berjalan selama lebih dari satu jam, yang dapat menyebabkan penundaan panggilan berikutnya ke program.
- Program akan dimulai pada waktu yang salah.
- Program ini pasti akan dijalankan sesaat sebelum batas waktu, misalnya, tengah malam, akhir bulan atau tahun, dan akan gagal karena kesalahan komputasi.
Membuat perangkat lunak berkelanjutan dimulai dengan menulis perangkat lunak yang menurutnya telah jatuh sebelumnya, dan macet jika Anda tidak tahu harus berbuat apa. Hal terbaik tentang melempar pengecualian dan meninggalkan komentar dengan gaya "ini seharusnya tidak terjadi" adalah ketika hal ini pasti terjadi, Anda akan memiliki kepala mulai men-debug kode Anda.
Program ini bahkan tidak diwajibkan untuk pulih dari kegagalan, cukup untuk membiarkannya menyerah dan tidak memperburuk situasi. Pemeriksaan kecil yang menghasilkan pengecualian dapat menghemat berminggu-minggu dalam pengintaian, dan file kunci sederhana dapat menghemat waktu pemulihan dari cadangan.
Kode yang mudah di-debug adalah:
- kode yang memeriksa untuk melihat apakah semuanya baik-baik saja sebelum melakukan apa yang mereka minta;
- kode yang membuatnya mudah untuk kembali ke keadaan terkenal dan coba lagi;
- serta kode dengan tingkat keamanan yang menyebabkan kesalahan terjadi sedini mungkin.
Aturan 2: Program Anda bertarung dengan dirinya sendiri
Serangan DoS terbesar dalam sejarah Google berasal dari diri kita sendiri (karena sistem kami sangat besar). Meskipun dari waktu ke waktu seseorang mencoba menguji kekuatan kita, tetapi kita tetap bisa melukai diri kita sendiri lebih dari orang lain.
Ini berlaku untuk semua sistem kami.
Astrid Atkinson, Long Game Engineer
Program selalu macet selama menjalankan terakhir, selalu ada tidak cukup prosesor, memori, atau ruang disk. Semua pekerja memalu antrian yang kosong, semua orang mencoba mengulangi permintaan yang gagal dan lama, dan semua server pada saat yang sama berhenti selama pengumpulan sampah. Sistem tidak hanya rusak, tetapi terus-menerus mencoba untuk merusak dirinya sendiri.
Kesulitan besar dapat menyebabkan bahkan memeriksa sistem.
Menerapkan pemeriksaan operasi server bisa mudah, tetapi hanya jika tidak memproses permintaan. Jika Anda tidak memeriksa durasi waktu aktif terus-menerus, maka ada kemungkinan bahwa program tersebut jatuh di antara pemeriksaan. Bug juga dapat dipicu oleh pemeriksaan kesehatan: Saya harus menulis cek yang menyebabkan crash sistem, yang harus mereka lindungi. Dua kali, dengan perbedaan tiga bulan.
Kode penanganan kesalahan pasti akan mengarah pada penemuan lebih banyak kesalahan yang perlu diproses, banyak di antaranya muncul dari penanganan kesalahan itu sendiri. Demikian pula, optimalisasi kinerja sering menjadi penyebab kemacetan dalam sistem. Aplikasi yang bagus untuk digunakan dalam satu tab berubah menjadi masalah, diluncurkan dalam 20 salinan.
Contoh lain: seorang pekerja di sebuah pipa berjalan terlalu cepat dan mengkonsumsi memori yang tersedia sebelum bagian selanjutnya dari pipa itu mengaksesnya. Ini dapat dibandingkan dengan kemacetan lalu lintas: kemacetan terjadi karena peningkatan kecepatan, dan akibatnya, kemacetan lalu lintas tumbuh ke arah yang berlawanan. Jadi optimisasi dapat menghasilkan sistem yang jatuh di bawah beban tinggi atau berat, seringkali dalam beberapa cara misterius.
Dengan kata lain: semakin cepat sistem, semakin kuat tekanan di atasnya, dan jika Anda tidak membiarkan sistem sedikit menangkal, maka jangan kaget jika retak.
Counteraction adalah salah satu bentuk umpan balik dari sistem. Program, yang mudah di-debug, melibatkan pengguna dalam lingkaran umpan balik, memungkinkan Anda untuk melihat semua perilaku di dalam sistem, acak, disengaja, diinginkan dan tidak diinginkan. Anda dapat dengan mudah memeriksa kode tersebut, melihat dan memahami perubahan yang terjadi dengannya.
Aturan 3: Jika Anda meninggalkan sesuatu yang ambigu sekarang, Anda harus men-debugnya nanti
Dengan kata lain, seharusnya mudah bagi Anda untuk melacak variabel dalam program dan memahami apa yang terjadi. Ambil rutinitas apa pun dengan aljabar linear mimpi buruk, Anda harus berusaha menyajikan kondisi program sejelas mungkin. Ini berarti bahwa di tengah suatu program Anda tidak dapat mengubah tujuan suatu variabel, karena menggunakan satu variabel untuk dua tujuan berbeda adalah dosa yang mematikan.
Ini juga berarti bahwa Anda harus menghindari masalah semi-predikat dengan hati-hati, jangan pernah menggunakan nilai tunggal (
count
) untuk mewakili sepasang nilai (
boolean
,
count
). Penting untuk menghindari mengembalikan angka positif untuk hasil dan pada saat yang sama mengembalikan
-1
jika tidak ada yang cocok. Faktanya adalah bahwa Anda dapat dengan mudah menemukan diri Anda dalam situasi di mana Anda memerlukan sesuatu seperti "
0, but true
" (dan inilah fitur di Perl 5); atau ketika Anda membuat kode yang sulit untuk digabungkan dengan bagian lain dari sistem (
-1
untuk bagian selanjutnya dari program ini mungkin bukan kesalahan, tetapi nilai input yang valid).
Selain menggunakan satu variabel untuk dua tujuan, tidak disarankan untuk menggunakan dua variabel untuk tujuan yang sama, terutama jika itu adalah Boolean. Saya tidak bermaksud mengatakan bahwa menggunakan dua angka untuk menyimpan rentang adalah hal yang buruk, tetapi menggunakan angka Boolean untuk menunjukkan status suatu program seringkali merupakan mesin keadaan bertopeng.
Ketika suatu negara tidak lulus dari atas ke bawah, yaitu, dalam kasus siklus episodik, yang terbaik adalah memberikan keadaan dengan variabel sendiri dan menghapus logika. Jika Anda memiliki sekumpulan boolean di dalam objek, gantilah dengan variabel yang disebut
state
dan gunakan enum (atau string jika perlu di suatu tempat).
if
ekspresi akan terlihat seperti
if state == name
, bukan
if bad_name && !alternate_option
.
Bahkan jika Anda membuat mesin keadaan eksplisit, ada kemungkinan untuk membingungkan: kadang-kadang kode dapat memiliki dua mesin keadaan tersembunyi di dalamnya. Setelah saya disiksa untuk menulis proksi HTTP, sampai saya membuat setiap mesin eksplisit, melacak status koneksi dan menguraikannya secara terpisah. Saat Anda menggabungkan dua mesin keadaan menjadi satu, mungkin sulit untuk menambahkan keadaan baru atau untuk memahami persis keadaan apa yang seharusnya dimiliki sesuatu.
Ini lebih tentang membuat kode yang tidak harus di-debug daripada tentang debugging yang mudah. Jika Anda mengembangkan daftar status yang benar, akan jauh lebih mudah untuk membuang yang salah tanpa kehilangan satu atau dua.
Aturan 4: Perilaku acak adalah perilaku yang diharapkan.
Saat Anda tidak memahami apa yang dilakukan struktur data, kesenjangan pengetahuan ini diisi oleh pengguna: perilaku apa pun dari kode, disengaja atau tidak disengaja, pada akhirnya akan bergantung pada sesuatu. Banyak bahasa pemrograman populer mendukung tabel hash yang dapat diulang, dan yang dalam kebanyakan kasus mempertahankan ketertiban setelah penyisipan.
Dalam beberapa bahasa, perilaku tabel hash memenuhi harapan sebagian besar pengguna, mengulangi kunci dalam urutan mereka ditambahkan. Dalam bahasa lain, tabel hash di setiap iterasi mengembalikan kunci dalam urutan yang berbeda. Dalam hal ini, beberapa pengguna mengeluh bahwa perilakunya
tidak cukup acak.
Sayangnya, semua sumber keacakan dalam program Anda pada akhirnya akan digunakan untuk simulasi statistik, atau lebih buruk lagi - kriptografi; dan sumber penyortiran apa pun akan digunakan untuk menyortir.
Dalam database, beberapa pengidentifikasi mengandung sedikit informasi lebih banyak daripada yang lain. Dengan membuat tabel, pengembang dapat memilih antara berbagai jenis kunci utama. Pilihan yang tepat adalah UUID, atau sesuatu yang tidak bisa dibedakan darinya. Kerugian dari opsi lain adalah mereka dapat mengungkapkan informasi pemesanan dan identifikasi. Artinya, bukan hanya
a == b
, tetapi
a <= b
, dan opsi lainnya berarti kunci kenaikan otomatis.
Saat menggunakan kunci kenaikan otomatis, basis data memberikan nomor untuk setiap baris dalam tabel, menambahkan 1 saat memasukkan baris baru. Dan pengurutan tidak jelas: orang tidak tahu bagian mana dari data yang kanonik. Dengan kata lain, apakah Anda mengurutkan berdasarkan kunci atau cap waktu? Seperti halnya tabel hash, orang itu sendiri akan memilih jawaban yang tepat. Dan masalah lain adalah bahwa pengguna dapat dengan mudah memprediksi catatan tetangga dengan kunci lain.
Tetapi setiap upaya untuk mengecoh UUID akan gagal: kami telah mencoba menggunakan kode pos, nomor telepon dan alamat IP, dan setiap kali gagal total. UUID mungkin tidak membuat kode Anda lebih mudah di-debug, tetapi perilaku acak yang lebih sedikit berarti lebih sedikit masalah.
Dari tombol Anda dapat mengekstrak informasi tidak hanya tentang pemesanan. Jika dalam database Anda membuat kunci berdasarkan bidang lain, maka orang akan membuang data dan mengembalikannya dari kunci. Dan dua masalah akan muncul: ketika keadaan program disimpan di beberapa tempat, akan sangat mudah bagi salinan untuk saling tidak setuju; dan menyinkronkannya akan lebih sulit jika Anda tidak yakin yang mana yang perlu diubah atau mana yang telah diubah.
Apa pun yang Anda izinkan pengguna Anda lakukan, mereka akan melakukannya. Menulis kode yang mudah di-debug berarti memikirkan cara untuk menyalahgunakannya, serta bagaimana orang dapat berinteraksi dengannya secara umum.
Aturan 5: Debugging adalah tugas sosial, pertama-tama, tugas teknis.
Ketika sebuah proyek dibagi menjadi beberapa komponen dan sistem, mungkin akan lebih sulit untuk menemukan bug. Dengan memahami bagaimana masalah muncul, Anda dapat mengoordinasikan perubahan di berbagai bagian untuk memperbaiki perilaku. Memperbaiki bug dalam proyek-proyek besar tidak memerlukan begitu banyak menemukan mereka sebagai meyakinkan orang tentang keberadaan bug ini, atau kemungkinan sangat adanya.
Ada bug dalam perangkat lunak, karena tidak ada yang benar-benar yakin siapa yang bertanggung jawab untuk apa. Artinya, lebih sulit untuk men-debug kode ketika tidak ada yang ditulis, Anda harus bertanya tentang segala sesuatu di Slack, dan tidak ada yang menjawab sampai seorang pakar datang.
Ini dapat diperbaiki dengan perencanaan, alat, proses, dan dokumentasi.
Perencanaan adalah cara untuk menghilangkan tekanan untuk tetap berhubungan, struktur manajemen insiden. Paket ini memungkinkan Anda untuk memberi tahu pembeli, membebaskan orang-orang yang telah berhubungan terlalu lama, dan juga melacak masalah dan membuat perubahan untuk mengurangi risiko di masa depan. Alat - cara untuk mengurangi persyaratan untuk melakukan beberapa pekerjaan, sehingga menjadi lebih mudah diakses oleh pengembang lain. Proses ini adalah cara menghilangkan fungsi manajemen dari masing-masing peserta dan mentransfer ke tim.
Orang dan cara interaksi akan berubah, tetapi proses dan alat akan tetap ada saat tim bertransformasi. Bukannya yang satu lebih penting dari yang lain, tetapi yang satu dirancang untuk mendukung perubahan yang lain. Proses ini juga dapat digunakan untuk menghapus fungsi kontrol dari tim. Ini tidak selalu baik atau buruk, tetapi selalu
ada semacam proses, bahkan jika tidak dijabarkan. Dan tindakan mendokumentasikannya adalah langkah pertama untuk membiarkan orang lain mengubah proses ini.
Dokumentasi lebih dari sekadar file teks. Ini adalah cara mentransfer tanggung jawab, bagaimana Anda membawa orang ke tempat kerja, bagaimana Anda melaporkan perubahan kepada mereka yang terpengaruh oleh perubahan ini. Dokumentasi menulis membutuhkan lebih banyak empati daripada saat menulis kode, dan lebih banyak keterampilan: tidak ada flag kompiler sederhana atau jenis cek, dan Anda dapat dengan mudah menulis banyak kata tanpa mendokumentasikan apa pun.
Tanpa dokumentasi, seseorang tidak dapat mengharapkan orang lain untuk membuat keputusan berdasarkan informasi, atau bahkan setuju dengan konsekuensi dari penggunaan perangkat lunak. Tanpa dokumentasi, alat atau proses, tidak mungkin berbagi beban pemeliharaan atau setidaknya mengganti orang-orang yang sekarang memecahkan masalah.
Keinginan untuk memfasilitasi debugging tidak hanya berlaku untuk kode itu sendiri, tetapi juga untuk proses yang berhubungan dengan kode, ini membantu untuk memahami kulit siapa yang Anda butuhkan untuk memperbaiki kode.
Kode yang mudah di-debug mudah dijelaskan.
Ada pendapat bahwa jika Anda menjelaskan masalah kepada seseorang selama debugging, maka Anda sendiri yang memahaminya. Untuk ini, Anda bahkan tidak perlu orang lain, yang terpenting adalah memaksa diri Anda untuk menjelaskan situasi dari awal, untuk menjelaskan urutan pemutaran. Dan seringkali ini cukup untuk mengambil keputusan yang tepat.
Kalau saja. Terkadang, ketika kita meminta bantuan, kita tidak bertanya apa yang dibutuhkan. Ini sangat umum sehingga disebut Masalah XY: β
Bagaimana saya mendapatkan tiga huruf terakhir dari nama file? Hah? Tidak, maksudku ekspansi . β
Kami berbicara tentang masalah dalam hal solusi yang kami pahami, dan kami berbicara tentang solusi dalam hal konsekuensi yang kami takuti. Debugging adalah pemahaman yang sulit tentang konsekuensi yang tak terduga dan solusi alternatif, itu membutuhkan yang paling sulit bagi seorang programmer: untuk mengakui bahwa ia memahami sesuatu yang salah.
Ternyata ini bukan kesalahan kompiler.