Apa cara terbaik untuk menyiapkan desain & kode Anda untuk bug “tidak dikenal” dari hari pertama?

8

Saya hanya ingin tahu, apakah ada metode atau teknik praktis atau bahkan trik untuk menghindari bug yang "tidak dikenal" terutama bug jahat dan acak yang sering muncul pada menit terakhir atau setidaknya untuk menjaga hal-hal ini pada tingkat minimum. Saya bertanya ini karena ketika Anda bekerja pada platform baru atau menggunakan teknologi baru tertentu untuk pertama kalinya, bagaimana Anda membenarkan desain dan kode Anda cukup kuat? Atau hal-hal ini hanya bisa dipelajari oleh waktu dan kesalahan?

(Saya menggunakan C ++ untuk sebagian besar waktu kerja saya)

Terima kasih!

KaptenJH
sumber

Jawaban:

5

Hampir dua puluh tahun yang lalu, saya mendapatkan banyak wawasan tentang hal ini dari buku David Thielen yang sangat baik "No Bugs: Memberikan Kode Bebas Kesalahan dalam C dan C ++", yang sekarang tersedia dalam bentuk PDF gratis .

Dia mengajari saya dua ide hebat ...

Bug tidak datang entah dari mana. Kita semua programmer duduk dan menuliskannya ke dalam kode kita dengan jari kita sendiri.

"Bug" berkonotasi bahwa beberapa agensi luar memutuskan untuk menginfeksi program Anda dengan bug dan bahwa jika Anda menjalani kehidupan yang bersih, dan mengorbankan hewan berbulu kecil di kaki komputer Anda, mereka akan pergi ... Konsep ini penting karena warna pendekatan Anda untuk men-debug kode Anda. Jika Anda melihat kesalahan sebagai "bug," Anda berharap tidak ada yang ditemukan. (Kamu berharap peri yang baik datang, menaburkan debu peri, dan serangga pergi.)

Bug tidak boleh disebut bug, mereka harus disebut Massive Fuck-Ups [MFUs] ... MFU ada karena program ditulis oleh orang-orang, dan orang-orang membuat kesalahan ... Anda akan menulis MFU. Anda akan duduk dan dengan kebencian penuh dari pemikiran sebelumnya menempatkan MFU dalam kode Anda. Pikirkan tentang hal ini - Anda tahu bahwa Anda yang meletakkan bug di sana. Jadi, jika Anda duduk dengan kode, Anda akan memasukkan beberapa bug.

Karena takdir semua programmer tidak bisa dihindari untuk menulis bug, saya perlu kode defensif, termasuk hal-hal yang akan melompat, menjerit, dan mengibarkan bendera merah ketika mereka mendeteksi bug.

Setelah ditulis pada awal tahun 90-an, kekhususan dalam buku Thielen ini agak ketinggalan zaman. Misalnya, di Linux dan Mac OS X, Anda tidak perlu lagi menulis bungkus sendiri untuk operator baru C ++; Anda dapat menggunakan valgrind untuk itu.

Tetapi ada beberapa hal yang secara rutin saya lakukan untuk C / C ++ / ObjC:

  1. Ketika saya bisa, nyalakan opsi "Peringatan adalah kesalahan" dari kompiler, dan perbaiki semuanya. (Saya mengelola satu proyek lama di mana memperbaiki semuanya sekaligus akan memakan waktu berminggu-minggu, jadi saya hanya memperbaiki file setiap beberapa minggu - dan dalam beberapa tahun, saya dapat mengaktifkan opsi itu.)
  2. Gunakan alat analisis kode statis, seperti Gimpel's PC-Lint atau yang sangat bagus sekarang dibangun ke dalam Xcode Apple. Cakupan bahkan lebih baik, tetapi biayanya untuk perusahaan besar, bukan hanya manusia biasa.
  3. Gunakan alat analisis dinamis, seperti valgrind, untuk memeriksa masalah memori, kebocoran, dll.
  4. Seperti yang dikatakan Thielen (dan bab ini masih layak dibaca): Assert The World . Tentu saja, tidak ada orang selain idiot yang akan memanggil fungsi Anda dengan pointer nil - dan itu berarti seseorang, di suatu tempat, adalah idiot yang akan melakukan hal itu. Bahkan mungkin Anda dalam tiga tahun ketika apa yang Anda lakukan hari ini telah berkabut. Jadi tambahkan saja pernyataan di awal fungsi untuk memvalidasi argumen pointer - dibutuhkan tiga detik untuk mengetik, dan hilang dalam rilis executable.
  5. Di C ++, RTTI adalah teman Anda. Sekali lagi, tidak ada orang selain idiot yang akan memanggil fungsi Anda dengan penunjuk ke objek yang salah - yang berarti bahwa, pasti, beberapa orang idiot akan - dan biaya untuk bertahan terhadap hal itu dapat diabaikan. Dalam kode berbasis-C yang diturunkan dari GObject, Anda dapat melakukan hal yang sama dengan macro cast dinamis yang defensif.
  6. Unit dan tes regresi otomatis sekarang menjadi bagian penting dari repertoar saya. Pada satu proyek, mereka adalah bagian integral dari sistem build rilis, dan build tidak akan selesai kecuali mereka semua lulus.
  7. Bagian penting lainnya adalah mencatat kode di debug dan melepaskan executable yang dapat diaktifkan saat runtime oleh sesuatu seperti variabel lingkungan.
  8. Tulis tes defensif sehingga programmer yang menjalankan debug tidak dapat mengabaikannya jika gagal. Pesan runtime ke konsol dapat diabaikan. Program macet dengan tegas tidak dapat diabaikan.
  9. Saat merancang, menyediakan API publik, dan implementasi pribadi yang tidak bisa didapatkan oleh kode luar. Dengan begitu, jika Anda harus refactor, tidak ada yang bergantung pada beberapa variabel keadaan interior sihir atau sesuatu. Di kelas C ++, saya penggemar berat yang dilindungi dan pribadi untuk ini. Saya juga berpikir kelas proxy hebat, tapi jangan gunakan itu sendiri.

Tentu saja, apa yang akan Anda lakukan untuk bahasa atau teknologi baru akan bervariasi dalam detailnya. Tapi begitu Anda memasukkan ke dalam hati Anda anggapan bahwa serangga itu Besar-Besaran, Anda Menulis Dengan Jari Anda Sendiri, dan kode Anda terus-menerus diserang oleh pasukan idiot, dengan Anda sebagai kepala jenderal, dengan Anda sebagai kepala jenderal, saya yakin Anda Saya akan mencari teknik pertahanan yang cocok.

Bob Murphy
sumber
14

Nah, jika Anda tahu itu, maka mereka masuk ke kategori "bug yang tidak diketahui yang dikenal" (yaitu Anda tahu sesuatu tentang sifat "ini" akan terjadi). Berapapun jumlah unit test tidak akan menangkapnya, mereka hanya sangat berguna untuk kasus yang diketahui.

Cara kita menangani hal ini adalah dengan menempatkan layanan logging kesalahan pada aplikasi yang sedang berjalan, melaporkan kembali ke basis ketika itu terjadi dan menghadapinya ketika itu muncul. Kalau tidak, Anda bisa menghabiskan waktu bertahun-tahun dan masih belum menutupi apa pun ... pada titik tertentu Anda hanya menunggu untuk melihat apa yang terjadi di dunia nyata dan berkembang dengan cepat.

Sisi desain, Anda mendesain untuk pemeliharaan sebagai salah satu faktor utama.

  • Pola pembelajaran yang berhasil dan pola yang harus dihindari. Pola yang lebih konsisten dan koheseif yang Anda miliki semakin nyaman Anda sehingga kelas masalah tertentu akan atau tidak akan terjadi.
  • Buat semuanya jelas. Obsurity menyebabkan kebingungan, menyebabkan bug.
  • Konvensi penamaan yang kuat. Jika Anda menyebutkan hal-hal dengan baik dan konsisten, ada sejumlah besar manfaat ketika Anda mencoba mengubah sesuatu atau menjelaskannya ke sana ... setiap hal yang disebut Factory akan melakukan X.
  • Eja semuanya. Kami memiliki autocomplete hari ini tidak menggunakan akronim ketika kata lengkap menghilangkan kebingungan.
  • Terpisah menjadi lapisan atau abstrasi ... sehingga gaya masalah tertentu akan terjadi pada lapisan tertentu dan bukan "di suatu tempat"
  • Pisahkan kelas masalah ke dalam lapisan dan aspek. Semakin sedikit satu bagian berkaitan dengan bagian lain dari kode, semakin baik secara umum. Bahkan jika perlu sedikit lebih lama untuk menulis hal-hal serupa dua kali.

Dan kuncinya ... Temukan seorang mentor yang telah melakukan semua kesalahan sebelumnya ATAU buat beberapa kesalahan sampai Anda menemukan apa yang berhasil dan tidak berhasil.

Robin Vessey
sumber
1
Saya suka "desain untuk pemeliharaan". Terutama yang baik adalah cepat berbalik ketika masalah yang akhirnya muncul. Itu berarti pengujian unit yang baik dan komprehensif, strategi penyebaran yang baik, dan proses pelacakan bug / QA yang baik.
Dean Harding
4

Semua hal di atas adalah poin yang bagus. Tetapi ada sesuatu yang tidak disebutkan. Anda perlu membuat modul dan fungsi Anda menjadi paranoid. Rentang menguji semua parameter fungsi. Hati-hati dengan string dengan awal atau akhir yang kosong atau yang terlalu pendek atau terlalu panjang. Waspadalah terhadap boolean yang benar-benar tidak palsu. Dalam bahasa yang tidak diketik seperti PHP, perhatikan jenis variabel yang tidak terduga. Watch out for NULL.

Kode paranoid ini sering dikodekan sebagai pernyataan yang dapat dinonaktifkan pada build produksi untuk mempercepat. Tapi itu pasti akan mencegah bug panik menit terakhir.

Andy Canfield
sumber
Bagaimana bisa boolean tidak benar atau salah?
Zhehao Mao
@Zhehao Mao: Jika Boolean adalah kolom dalam database, itu bisa Benar, Salah, atau NULL.
Mike Sherrill 'Cat Recall'
Ketika saya masih seorang GI, kami memiliki pepatah. "Ketika semua orang benar - benar keluar untuk menjemputmu, paranoia baik, berpikiran sehat." Beberapa pihak berwenang menyebut pemrograman defensif ini .
Mike Sherrill 'Cat Recall'
Oh begitu. Keanehan SQL.
Zhehao Mao
3

Rob benar dalam mengatakan bahwa unit test tidak akan menyelamatkan Anda dari bug yang tidak diketahui TETAPI unit test akan membantu Anda dari memperkenalkan bug saat Anda memperbaiki bug yang tidak dikenal dan akan menyelamatkan Anda dari pengenalan kembali bug lama secara tidak sengaja. TDD juga akan memaksa Anda untuk merancang perangkat lunak Anda sejak awal agar dapat diuji dan yang memiliki nilai berkelanjutan positif yang sangat besar.

mcottle
sumber
Aspek unit test ini tampaknya yang paling disalahpahami: Anda tidak membuktikan kebenaran kode Anda dengan unittests, Anda memalsukan kebenaran perubahan berikut dengan itu. Tetapi setiap kali bug dalam kode tidak ditemukan ditemukan, seseorang menangis 'lihat, tes Anda tidak berharga, mereka tidak menemukan bug ini'
keppla
Kemudian Anda menambahkan tes untuk mereproduksi cacat. Perbaiki cacat & Anda akan selalu menguji kesalahan itu setiap kali Anda menjalankan suite pengujian Anda ...
mcottle
Itulah yang akan saya lakukan, tapi itu sering mengarah pada 'yeah, sekarang sudah terlambat, bug sudah terjadi'. Fakta bahwa bug tidak diperkenalkan lagi akan sering diawasi
keppla
Ini benar, tetapi pada saat itu mereka telah bermigrasi ke yang tidak diketahui :) :)
Robin Vessey
2

Hindari status / "efek samping" jika memungkinkan. Meskipun komputer bersifat deterministik, dan memberikan output yang sama untuk input yang sama, tinjauan umum tentang input selalu tidak lengkap. Sayangnya, sebagian besar waktu kita tidak menyadari betapa tidak lengkapnya itu.

Ketika berbicara tentang aplikasi web, seluruh database, permintaan saat ini, sesi pengguna, pustaka pihak ke-3 yang diinstal, dan banyak lagi bagian input. Ketika berbicara tentang utas, itu lebih buruk lagi: seluruh sistem operasi Anda, dengan semua proses lainnya yang dikelola oleh penjadwal yang sama adalah 'bagian dari input'.

Bug disebabkan oleh salah menilai cara input ditangani atau dengan salah menilai input. Yang terakhir adalah, dalam pengalaman saya, yang sulit: Anda hanya dapat mengamati mereka 'hidup', seringkali, Anda tidak memiliki input lagi.

Ketika mempelajari Teknologi, Infrastruktur baru, dll., Sebaiknya praktik itu mendapatkan gambaran umum, komponen apa yang berkontribusi pada input dan kemudian mencoba untuk menghindari sebanyak mungkin dari mereka .

keppla
sumber
+1: Efek samping seringkali dapat dihindari dengan menerapkan prinsip-prinsip SOLID dan menciptakan metode atom. Selain itu, kode tersebut harus dilindungi oleh pernyataan.
Falcon
0

Karena perangkat lunak Anda menjadi lebih kompleks, tidak dapat dihindari bahwa beberapa bug akan terjadi. Satu-satunya cara untuk menghindari itu sepenuhnya adalah dengan hanya mengembangkan perangkat lunak sepele - dan bahkan kemudian, Anda pasti akan membuat kesalahan gila dari waktu ke waktu.

Satu-satunya hal praktis yang dapat Anda lakukan adalah menghindari kerumitan yang tidak perlu - membuat perangkat lunak Anda sesederhana mungkin, tetapi tidak lebih sederhana dari itu.

Pada dasarnya itulah prinsip dan pola desain yang lebih spesifik - membuat segala sesederhana mungkin. Masalahnya adalah bahwa "sederhana dengan cara apa" dapat bersifat subyektif - maksud Anda desain paling sederhana mutlak untuk persyaratan saat ini, atau sederhana untuk memodifikasi untuk persyaratan di masa depan. Dan ada prinsip untuk itu juga.

Tes unit adalah contoh dari ketidakpastian ini. Di satu sisi, mereka adalah kompleksitas yang tidak perlu - kode yang harus dikembangkan dan dipelihara, tetapi yang tidak menyelesaikan pekerjaan. Di sisi lain, mereka adalah cara sederhana untuk mengotomatisasi pengujian, mengurangi jumlah pengujian manual yang jauh lebih sulit yang harus dilakukan.

Tidak peduli berapa banyak teori desain yang Anda pelajari dan berapa banyak pengalaman yang Anda dapatkan, prinsip utama (dan kadang-kadang satu-satunya panduan yang Anda miliki) adalah bertujuan untuk kesederhanaan.

Steve314
sumber
0

Tidak ada yang acak tentang bug perangkat lunak, penyebab utama bersifat deterministik sempurna, instruksi yang salah ke komputer.

Threading bug dapat bersifat non-deterministik dalam perilaku eksekusi, tetapi tidak secara acak menjadi penyebab utama.

Mereka terjadi untuk alasan yang sama persis hanya pada saat-saat yang tampaknya tidak dapat diprediksi dalam waktu, tetapi itu tidak membuat mereka acak hanya tampaknya tidak dapat diprediksi, setelah Anda mengetahui akar penyebabnya, Anda dapat secara deterministik memprediksi kapan mereka akan terjadi.

Saya mengatakan dengan jelas dan membuat perbedaan karena suatu alasan. Acak berarti satu hal, Dibuat, dilakukan, terjadi, atau dipilih tanpa metode atau keputusan sadar , yang menyiratkan ada beberapa pengambilan keputusan independen pada bagian komputer, tidak ada yang melakukan persis apa yang diperintahkan untuk dilakukan, Anda hanya tidak menyuruhnya melakukan hal yang benar dalam beberapa kasus yang sangat deterministik.

Semantik kata ada karena suatu alasan, acak tidak berarti sesuatu yang berbeda hanya karena seseorang menggunakannya dengan benar, itu selalu berarti hal yang sama. Istilah yang lebih baik adalah kesalahan logika yang tidak disengaja atau tidak jelas .

Mempertimbangkan bug secara acak hampir seperti menerima bahwa ada kekuatan lain yang tidak dapat dipahami di tempat kerja yang tidak sepenuhnya dipahami bertindak secara independen dari input Anda ke komputer, dan itu tidak terlalu ilmiah. Maksud saya, apakah para Dewa marah dan memukul permohonan Anda karena iseng?


sumber
+1 untuk poin yang valid, tetapi -1 untuk nitpicking. Jadi, +/- 0. Saya pikir sebagian besar orang yang membaca pertanyaan itu mengambil kata "acak" tidak dalam arti yang sepenuhnya harfiah (apa sebenarnya arti "acak", diambil secara ekstrem?), Tetapi lebih berarti sesuatu seperti "Saya tidak mengerti bagaimana perilaku ini bisa merangkak masuk atau mengapa perangkat lunak melakukan hal yang salah ".
CVn
@Micheal - itu sebabnya saya katakan dengan jelas dan membuat perbedaan. Tidak ada ekstrim ke acak itu berarti satu hal, Dibuat, dilakukan, terjadi, atau dipilih tanpa metode atau keputusan sadar semantik kata ada karena suatu alasan, acak tidak berarti sesuatu yang berbeda hanya karena seseorang menggunakannya dengan benar, itu selalu berarti hal yang sama. Apa yang mungkin mereka maksudkan adalah tidak disengaja , karena alasan saya menyatakan penjelasan saya dalam jawaban saya.