Dengan (keluar) aplikasi mogok, tolong!
Diterbitkan: 2020-02-12Pemrogram mencoba menghindari crash dalam kode mereka. Jika seseorang menggunakan aplikasi mereka, itu tidak boleh rusak atau berhenti secara tiba-tiba. Ini adalah salah satu pengukuran kualitas yang paling sederhana – jika aplikasi sering mogok, itu mungkin tidak dilakukan dengan baik.
Aplikasi mogok terjadi ketika sebuah program akan melakukan sesuatu yang tidak terdefinisi atau buruk seperti membagi nilai dengan nol atau mengakses sumber daya terbatas pada mesin. Mungkin juga dilakukan secara eksplisit oleh programmer yang menulis aplikasi. “Itu tidak akan pernah terjadi jadi saya akan melewatkannya” – itu adalah pemikiran yang cukup umum dan tidak sepenuhnya tidak masuk akal. Ada beberapa kasus yang tidak bisa terjadi, tidak pernah, sampai… terjadi.
Janji yang tidak ditepati
Salah satu kasus paling umum di mana kita tahu bahwa sesuatu tidak dapat terjadi adalah API. Kami telah menyetujui antara backend dan frontend – itulah satu-satunya respons server yang bisa Anda dapatkan untuk permintaan ini. Pengelola perpustakaan ini telah mendokumentasikan perilaku fungsi ini. Fungsi tidak dapat melakukan hal lain. Kedua cara berpikir itu benar, tetapi keduanya dapat menimbulkan masalah.
Saat Anda menggunakan perpustakaan, Anda dapat bergantung pada alat bahasa untuk membantu Anda menangani semua kemungkinan kasus. Jika bahasa yang Anda gunakan tidak memiliki pemeriksaan tipe atau analisis statis apa pun, Anda harus mengurusnya sendiri. Namun, Anda dapat memeriksanya sebelum mengirim ke lingkungan produksi sehingga tidak menjadi masalah besar. Itu bisa jadi sulit, tetapi Anda membaca changelogs sebelum memperbarui dependensi Anda dan menulis tes unit, bukan? Entah Anda menggunakan atau membuat perpustakaan, pengetikan yang lebih ketat yang dapat Anda berikan lebih baik untuk kode Anda dan pemrogram lainnya.
Komunikasi backend-frontend sedikit lebih sulit. Ini sering digabungkan secara longgar, sehingga perubahan di satu sisi dapat dilakukan dengan mudah tanpa menyadari bagaimana hal itu akan mempengaruhi sisi lain. Perubahan di backend sering kali dapat mematahkan asumsi Anda di frontend dan keduanya sering didistribusikan secara terpisah. Itu harus berakhir buruk. Kita hanya manusia dan terkadang kita tidak memahami pihak lain atau lupa memberi tahu mereka tentang perubahan kecil itu. Sekali lagi, itu bukan masalah besar dengan penyerahan jaringan yang tepat – respons decoding akan gagal dan kami tahu bagaimana menanganinya. Bahkan kode decoding terbaik pun dapat dipengaruhi oleh desain yang buruk…
Fungsi sebagian. Desain yang buruk.
“Kami akan memiliki dua variabel boolean di sini: 'isActive' dan 'canTransfer', tentu saja Anda tidak dapat mentransfer ketika tidak aktif, tetapi itu hanya detailnya." Di sini dimulai, desain buruk kami yang dapat memukul keras. Sekarang seseorang akan membuat fungsi dengan dua argumen itu dan memproses beberapa data berdasarkan itu. Solusi paling sederhana adalah… crash saja pada keadaan invalid, seharusnya tidak pernah terjadi jadi kita tidak perlu peduli. Kami bahkan terkadang peduli dan meninggalkan beberapa komentar untuk memperbaikinya nanti atau menanyakan apa yang harus terjadi, tetapi akhirnya dapat dikirimkan tanpa menyelesaikan tugas itu.
// kode semu function doTransfer(Bool isActive, Bool canTransfer) { If ( isActive dan canTransfer ) { // lakukan sesuatu untuk transfer yang tersedia } else if ( bukan isActive dan bukan canTransfer ) { // lakukan sesuatu untuk transfer tidak tersedia } else if ( isActive dan bukan canTransfer ) { // lakukan sesuatu untuk transfer tidak tersedia } else { // alias ( bukan isActive dan canTransfer ) // ada empat kemungkinan keadaan // ini seharusnya tidak terjadi, transfer seharusnya tidak tersedia saat tidak aktif menabrak() } }
Contoh ini mungkin terlihat konyol tetapi terkadang Anda mungkin terjebak dalam jebakan semacam itu yang sedikit lebih sulit dikenali dan diselesaikan daripada ini. Anda akan berakhir dengan sesuatu yang disebut fungsi parsial. Ini adalah fungsi yang didefinisikan hanya untuk beberapa kemungkinan input yang mengabaikan atau menabrak yang lain. Anda harus selalu menghindari fungsi parsial (harap dicatat bahwa dalam bahasa yang diketik secara dinamis, sebagian besar fungsi dapat diperlakukan sebagai parsial). Jika bahasa Anda tidak dapat memastikan perilaku yang tepat dengan pemeriksaan tipe dan analisis statis, mungkin akan macet setelah beberapa waktu dengan cara yang tidak terduga. Kode terus berkembang dan asumsi kemarin mungkin tidak valid hari ini.
Cepat gagal. Sering gagal.
Bagaimana Anda bisa melindungi diri sendiri? Pertahanan terbaik adalah menyerang! Ada pepatah bagus ini: “Gagal cepat. Sering gagal.” Tapi bukankah kita setuju bahwa kita harus menghindari crash aplikasi, fungsi parsial dan desain yang buruk? Erlang OTP memberi pemrogram keuntungan mistis yang akan menyembuhkan dirinya sendiri setelah keadaan tak terduga dan memperbarui saat berjalan. Mereka mampu membelinya, tetapi tidak semua orang memiliki kemewahan seperti ini. Jadi mengapa kita harus gagal dengan cepat dan sering?
Pertama-tama, untuk menemukan keadaan dan perilaku yang tidak terduga itu . Jika Anda tidak memeriksa apakah status aplikasi Anda sudah benar, hal itu dapat menyebabkan hasil yang lebih buruk daripada mogok!
Kedua, untuk membantu programmer lain berkolaborasi pada basis kode yang sama . Jika Anda sendirian dalam sebuah proyek saat ini, mungkin ada orang lain yang mengejar Anda. Anda mungkin lupa beberapa asumsi dan persyaratan. Agak umum untuk tidak membaca dokumentasi yang disediakan sampai semuanya berfungsi atau tidak mendokumentasikan metode dan tipe internal sama sekali. Dalam keadaan itu, seseorang memanggil salah satu fungsi yang tersedia dengan nilai yang tidak terduga tetapi valid. Sebagai contoh, katakanlah kita memiliki fungsi 'tunggu' yang mengambil nilai bilangan bulat dan menunggu selama beberapa detik. Bagaimana jika seseorang melewati '-17' untuk itu? Jika tidak crash segera setelah melakukan itu mungkin mengakibatkan beberapa kesalahan serius dan status tidak valid. Apakah menunggu selamanya atau tidak sama sekali?

Bagian terpenting dari tabrakan yang disengaja adalah melakukannya dengan anggun . Jika Anda crash aplikasi Anda, Anda harus memberikan beberapa informasi untuk memungkinkan diagnosis. Ini cukup mudah ketika Anda menggunakan debugger tetapi Anda harus memiliki beberapa cara untuk melaporkan kerusakan aplikasi tanpa itu. Anda dapat menggunakan sistem logging untuk menyimpan informasi tersebut di antara peluncuran aplikasi atau melihatnya secara eksternal.
Bagian terpenting kedua dari crashing yang disengaja adalah menghindarinya di lingkungan produksi…
Jangan gagal. Pernah.
Anda akan mengirimkan kode Anda pada akhirnya. Anda tidak dapat membuatnya sempurna, seringkali terlalu mahal untuk berpikir tentang membuat jaminan kebenaran. Namun, Anda harus memastikan bahwa itu tidak akan berperilaku buruk atau crash. Bagaimana Anda bisa mencapainya karena kami sudah memutuskan untuk crash dengan cepat dan sering?
Bagian penting dari crashing yang disengaja adalah melakukannya hanya di lingkungan nonproduksi . Anda harus menggunakan pernyataan yang dihilangkan di build produksi aplikasi Anda. Ini akan membantu selama pengembangan dan memungkinkan untuk menemukan masalah tanpa mempengaruhi pengguna akhir. Namun, terkadang masih lebih baik untuk mogok untuk menghindari status aplikasi yang tidak valid. Bagaimana kita bisa mencapainya jika kita telah membuat fungsi parsial?
Jadikan status tidak terdefinisi dan tidak valid tidak mungkin untuk diwakili dan mundur ke status valid sebaliknya. Itu mungkin terdengar mudah tetapi membutuhkan banyak pemikiran dan kerja. Tidak peduli berapa banyak, selalu kurang dari mencari bug, membuat perbaikan sementara dan… mengganggu pengguna. Ini akan secara otomatis membuat beberapa fungsi parsial lebih kecil kemungkinannya untuk terjadi.
// kode semu function doTransfer(Status keadaan) { beralih ( keadaan ) { kasus State.canTransfer { // lakukan sesuatu untuk transfer yang tersedia } kasus State.cannotTransfer { // lakukan sesuatu untuk transfer tidak tersedia } kasus State.notActive { // lakukan sesuatu untuk transfer tidak tersedia } // Tidak mungkin merepresentasikan transfer yang tersedia tanpa aktif // hanya ada tiga kemungkinan keadaan } }
Bagaimana Anda bisa membuat status yang tidak valid menjadi tidak mungkin? Mari kita pilih dua dari contoh sebelumnya. Dalam kasus dua variabel boolean kami 'isActive' dan 'canTransfer' kami dapat mengubah keduanya menjadi enumerasi tunggal. Ini akan secara mendalam mewakili semua keadaan yang mungkin dan valid. Bahkan kemudian seseorang dapat mengirim variabel yang tidak ditentukan, tetapi itu jauh lebih mudah untuk ditangani. Ini akan menjadi nilai tidak valid yang tidak akan diimpor ke program kami alih-alih status tidak valid yang dilewatkan di dalam membuat segalanya lebih sulit.
Fungsi tunggu kami juga dapat ditingkatkan dengan baik dalam bahasa yang diketik dengan kuat. Kita dapat membuatnya hanya menggunakan bilangan bulat yang tidak ditandatangani pada input. Itu saja akan memperbaiki semua masalah kita karena argumen yang tidak valid akan dihapus oleh kompiler. Tetapi bagaimana jika bahasa Anda tidak memiliki tipe? Kami memiliki beberapa solusi yang mungkin. Pertama – crash saja, fungsi ini tidak terdefinisi untuk angka negatif dan kami tidak akan melakukan hal yang tidak valid atau tidak terdefinisi. Kami harus menemukan penggunaan yang tidak valid selama pengujian. Tes unit (yang harus kita lakukan) akan sangat penting di sini. Kedua – ini mungkin berisiko, tetapi tergantung pada konteksnya mungkin berguna. Kita dapat mundur ke nilai yang valid dengan menjaga pernyataan di build nonproduksi untuk memperbaiki status yang tidak valid jika memungkinkan. Ini mungkin bukan solusi yang baik untuk fungsi seperti ini, tetapi jika kita membuat nilai absolut dari integer, kita akan menghindari crash aplikasi. Bergantung pada bahasa konkret, mungkin ide yang baik untuk membuang/menaikkan beberapa kesalahan/pengecualian sebagai gantinya. Mungkin layak untuk mundur jika memungkinkan, bahkan ketika pengguna melihat kesalahan, itu adalah pengalaman yang jauh lebih baik daripada mogok.
Mari kita ambil satu contoh lagi di sini. Jika status data pengguna di aplikasi frontend Anda akan menjadi tidak valid untuk beberapa kasus, mungkin lebih baik untuk memaksa keluar dan mendapatkan data yang valid lagi dari server daripada mogok. Pengguna mungkin terpaksa melakukannya atau dapat terjebak di dalam lingkaran macet tanpa akhir. Sekali lagi – kami harus menegaskan dan mogok dalam situasi seperti itu di lingkungan nonproduksi, tetapi jangan biarkan pengguna Anda menjadi penguji eksternal.
Ringkasan
Tidak ada yang suka aplikasi mogok dan tidak stabil. Kami tidak suka membuat atau menggunakannya. Gagal cepat dengan pernyataan yang memberikan diagnosis yang berguna selama pengembangan dan tes akan menangkap banyak masalah lebih awal. Penggantian status valid dalam produksi akan membuat aplikasi Anda jauh lebih stabil. Membuat status yang tidak valid tidak terwakili akan menghapus seluruh kelas masalah. Beri diri Anda sedikit lebih banyak waktu untuk berpikir sebelum pengembangan tentang cara menghapus dan mundur pada status yang tidak valid dan sedikit lebih banyak selama menulis untuk memasukkan beberapa pernyataan. Anda dapat mulai membuat aplikasi Anda lebih baik hari ini!
Baca lebih banyak:
- Desain berdasarkan kontrak
- Tipe data aljabar