Siapa yang harus membaca Exception.Message?

27

Ketika merancang pengecualian, haruskah saya menulis pesan yang harus dipahami pengguna atau pengembang? Siapa yang seharusnya menjadi pembaca pesan pengecualian?

Saya menemukan pesan pengecualian sama sekali tidak berguna dan saya selalu kesulitan menulisnya. Dengan konvensi, jenis pengecualian seharusnya sudah memberi tahu kami mengapa sesuatu tidak berfungsi dan properti khusus mungkin menambahkan lebih banyak informasi seperti nama file, indeks, kunci dll. Jadi mengapa mengulanginya dalam pesan itu sendiri? Pesan yang dibuat secara otomatis juga bisa dilakukan dan yang harus dikandungnya adalah nama pengecualian dengan daftar properti tambahan. Ini akan sama bermanfaatnya dengan teks tulisan tangan.

Bukankah lebih baik untuk tidak menulis pesan sama sekali tetapi memiliki renderer pengecualian khusus yang menangani pembuatan pesan yang bermakna mungkin dalam bahasa yang berbeda daripada hardcoding mereka dalam kode?


Saya telah ditanya apakah salah satu dari pertanyaan itu memberikan jawaban untuk pertanyaan saya:

Saya sudah membaca keduanya dan saya tidak senang dengan jawaban mereka. Mereka berbicara tentang pengguna secara umum dan fokus pada konten dari pesan itu sendiri daripada pada penerima dan ternyata setidaknya ada dua dari mereka: pengguna akhir dan pengembang. Saya tidak pernah tahu saya harus berbicara dengan yang mana saat menulis pesan pengecualian.

Saya bahkan berpikir bahwa pesan terkenal itu tidak memiliki nilai nyata sama sekali karena hanya mengulang nama jenis pengecualian dengan kata-kata yang berbeda jadi mengapa repot-repot menulisnya? Saya dapat membuatnya dengan sempurna secara otomatis.

Bagi saya pengecualian pesan tidak memiliki perbedaan pembacanya. Sebuah sempurna pengecualian akan perlu untuk menyediakan setidaknya dua versi dari pesan: satu untuk pengguna akhir dan satu untuk pengembang. Menyebutnya hanya pesan, terlalu umum. Pesan pengembang kemudian harus ditulis dalam bahasa Inggris tetapi pesan pengguna akhir mungkin perlu diterjemahkan ke bahasa lain. Tidak mungkin untuk mencapai semua ini hanya dengan satu pesan sehingga pengecualian perlu menyediakan beberapa pengidentifikasi untuk pesan pengguna akhir yang seperti yang baru saja saya katakan, mungkin tersedia dalam berbagai bahasa.

Ketika saya membaca semua pertanyaan terkait lainnya, saya mendapat kesan bahwa pesan pengecualian memang dimaksudkan untuk dibaca oleh pengguna akhir dan bukan pengembang ... satu pesan seperti memiliki kue dan memakannya juga.

t3chb0t
sumber
4
Ada dua pertanyaan lain yang diajukan di sini tentang Programer yang muncul di benak Anda. Saya tidak yakin apakah itu duplikat atau tidak, tetapi saya pikir Anda harus membacanya dan mungkin mereka akan membahas beberapa atau bahkan semua masalah Anda: Cara menulis pesan pengecualian yang baik dan Mengapa banyak pesan pengecualian tidak berisi detail berguna ?
Thomas Owens
2
@ThomasOwens Saya sudah membaca keduanya tetapi mereka tidak menangani penerima pesan pengecualian tertentu. Mereka berbicara tentang pengguna secara umum tetapi siapa dia? Apakah pengguna perangkat lunak yang sekarang memiliki gagasan tentang pemrograman atau pengembang yang harus menganalisis apa yang salah? Saya tidak pernah yakin siapa yang harus saya tangani ketika menulis pesan.
t3chb0t
2
Saya pikir jawaban yang baik di sini mungkin merujuk pada salah satu atau kedua pertanyaan itu, tetapi mereka jauh dari duplikat.
Lightness Races with Monica
1
Mengapa ini ditutup sebagai duplikat? Bahkan jika itu adalah duplikat dari pertanyaan lain itu (yang bukan), jawaban yang ini jelas memiliki jawaban yang lebih baik, itu seharusnya yang lain yang ditandai sebagai duplikat.
Ben Aaronson
2
@MichaelT Saya masih tidak benar-benar melihat banyak tumpang tindih di sana kecuali jika Anda mengandaikan bahwa pesan Pengecualian adalah untuk pengguna
Ben Aaronson

Jawaban:

46

Pesan-pesan itu untuk pengembang lain

Pesan-pesan itu diharapkan dibaca oleh pengembang untuk membantu mereka men-debug aplikasi. Ini dapat mengambil dua bentuk:

  • Debugging aktif. Anda sebenarnya menjalankan debugger saat menulis kode dan mencoba mencari tahu apa yang terjadi. Dalam konteks ini, pengecualian yang bermanfaat akan memandu Anda dengan membuatnya mudah untuk memahami apa yang salah, atau akhirnya menyarankan solusi (meskipun ini opsional).

  • Debugging pasif. Kode berjalan dalam produksi dan gagal. Pengecualian dicatat, tetapi Anda hanya mendapatkan pesan dan jejak tumpukan. Dalam konteks ini, pesan pengecualian yang membantu akan membantu Anda dengan cepat melokalisasi bug.

    Karena pesan-pesan itu sering dicatat, itu juga berarti Anda tidak boleh memasukkan informasi sensitif di sana (seperti kunci pribadi atau kata sandi, bahkan jika itu bisa berguna untuk men-debug aplikasi).

Misalnya, IOSecurityExceptionjenis pengecualian yang dilemparkan saat menulis file tidak terlalu eksplisit tentang masalahnya: apakah karena kita tidak memiliki izin untuk mengakses file? Atau mungkin kita bisa membacanya, tetapi tidak menulis? Atau mungkin file itu tidak ada dan kami tidak memiliki izin untuk membuat file di sana? Atau mungkin terkunci (mudah-mudahan, jenis pengecualian akan berbeda dalam kasus ini, tetapi dalam praktiknya, jenis kadang-kadang bisa samar). Atau mungkin Keamanan Akses Kode mencegah kita dari melakukan operasi I / O?

Sebagai gantinya:

IOSecurityException: file ditemukan tetapi izin untuk membaca isinya ditolak.

jauh lebih eksplisit. Di sini, kita segera tahu bahwa izin pada direktori diatur dengan benar (jika tidak, kita tidak akan dapat mengetahui bahwa file itu ada), tetapi izin di tingkat file bermasalah.

Ini juga berarti bahwa jika Anda tidak dapat memberikan informasi tambahan yang belum termasuk dalam jenis pengecualian, Anda dapat menyimpan pesan tersebut kosong. DivisionByZeroExceptionadalah contoh yang bagus di mana pesannya berlebihan. Di sisi lain, kenyataan bahwa sebagian besar bahasa membiarkan Anda mengeluarkan pengecualian tanpa menentukan pesannya dilakukan karena alasan yang berbeda: baik karena pesan default sudah tersedia, atau karena akan dihasilkan nanti jika diperlukan (dan generasi ini pesan terlampir di dalam tipe pengecualian, yang masuk akal, "OOPly" berbicara).

Perhatikan bahwa karena alasan teknis (seringkali kinerja), beberapa pesan pada akhirnya menjadi jauh lebih samar dari seharusnya. .NET's NullReferenceException:

Referensi objek tidak disetel ke instance objek.

adalah contoh luar biasa dari pesan yang tidak membantu. Pesan yang bermanfaat adalah:

Referensi objek producttidak disetel ke instance objek ketika dipanggil product.Price.

Pesan-pesan itu bukan untuk pengguna akhir!

Pengguna akhir tidak diharapkan melihat pesan pengecualian. Tak pernah. Meskipun beberapa pengembang akhirnya menunjukkan pesan-pesan itu kepada pengguna, ini mengarah pada pengalaman dan frustrasi pengguna yang buruk. Pesan seperti:

Referensi objek tidak disetel ke instance objek.

sama sekali tidak berarti bagi pengguna akhir, dan harus dihindari dengan cara apa pun.

Skenario kasus terburuk adalah mencoba global / menangkap yang melempar pengecualian kepada pengguna dan keluar dari aplikasi. Aplikasi yang peduli dengan penggunanya:

  • Menangani pengecualian di tempat pertama. Sebagian besar dapat ditangani tanpa mengganggu pengguna. Jaringan mati? Mengapa tidak menunggu beberapa detik dan coba lagi?

  • Mencegah pengguna dari mengarahkan aplikasi ke kasus luar biasa. Jika Anda meminta pengguna untuk memasukkan dua angka dan membagi yang pertama dengan yang kedua, mengapa Anda membiarkan pengguna untuk memasukkan nol dalam kasus kedua untuk menyalahkannya beberapa detik kemudian? Bagaimana dengan menyorot kotak teks berwarna merah (dengan tip alat yang membantu mengatakan bahwa angka harus berbeda dari nol) dan menonaktifkan tombol validasi hingga bidang tetap merah?

  • Mengundang pengguna untuk melakukan tindakan dalam formulir yang bukan merupakan kesalahan. Tidak ada izin yang cukup untuk mengakses file? Mengapa tidak meminta pengguna untuk memberikan izin administratif, atau memilih file yang berbeda?

  • Jika tidak ada yang berfungsi, menunjukkan kesalahan, ramah kesalahan yang secara khusus ditulis untuk mengurangi frustrasi pengguna, membantu pengguna untuk memahami apa yang salah dan akhirnya menyelesaikan masalah, dan juga membantunya mencegah kesalahan di masa depan (bila berlaku).

    Dalam pertanyaan Anda, Anda menyarankan untuk memiliki dua pesan sebagai pengecualian: satu pesan teknis untuk pengembang, dan satu pesan untuk pengguna akhir. Meskipun ini adalah saran yang valid dalam beberapa kasus kecil, sebagian besar pengecualian diproduksi pada tingkat di mana tidak mungkin untuk menghasilkan pesan yang bermakna bagi pengguna. Ambil DivisionByZeroExceptiondan bayangkan bahwa kita tidak dapat mencegah pengecualian terjadi dan tidak bisa mengatasinya sendiri. Ketika pembagian terjadi, apakah kerangka kerja (karena kerangka kerja, dan bukan kode bisnis, yang melempar pengecualian) tahu apa yang akan menjadi pesan yang bermanfaat bagi pengguna? Benar-benar tidak:

    Pembagian dengan nol terjadi. [BAIK]

    Sebagai gantinya, seseorang dapat membiarkannya membuang pengecualian, dan kemudian menangkapnya di tingkat yang lebih tinggi di mana kami mengetahui konteks bisnis dan dapat bertindak sesuai untuk benar-benar membantu pengguna akhir, sehingga menunjukkan sesuatu seperti:

    Bidang D13 tidak dapat memiliki nilai yang identik dengan yang ada di bidang E6, karena pengurangan nilai-nilai tersebut digunakan sebagai pembagi. [BAIK]

    atau mungkin:

    Nilai yang dilaporkan oleh layanan ATP tidak konsisten dengan data lokal. Ini mungkin disebabkan oleh data lokal yang tidak sinkron. Apakah Anda ingin menyinkronkan informasi pengiriman dan coba lagi? [Ya Tidak]

Pesan-pesan itu bukan untuk diuraikan

Pesan pengecualian juga tidak diharapkan untuk diuraikan atau digunakan secara terprogram. Jika Anda berpikir bahwa informasi tambahan dapat dibutuhkan oleh penelepon, sertakan dalam perkecualian berdampingan dengan pesan. Ini penting, karena pesan dapat berubah tanpa pemberitahuan. Ketik adalah bagian dari antarmuka, tetapi pesannya tidak: jangan pernah bergantung padanya untuk penanganan pengecualian.

Bayangkan pesan pengecualian:

Menghubungkan ke caching memutuskan batas waktu setelah menunggu 500 ms. Tingkatkan batas waktu atau periksa pemantauan kinerja untuk mengidentifikasi penurunan kinerja server. Waktu tunggu rata-rata untuk server caching adalah 6 ms. selama sebulan terakhir, 4 ms. selama seminggu terakhir dan 377 ms. selama satu jam terakhir.

Anda ingin mengekstraksi nilai "500", "6", "4" dan "377". Pikirkan sedikit tentang pendekatan yang akan Anda gunakan untuk melakukan parsing, dan baru kemudian melanjutkan membaca.

Anda punya ide? Besar.

Sekarang, pengembang asli menemukan kesalahan ketik:

Connecting to the caching sever timed out after waiting [...]

seharusnya:

                            ↓
Connecting to the caching server timed out after waiting [...]

Selain itu, pengembang menganggap bahwa bulan / minggu / satu jam tidak terlalu relevan, jadi ia juga melakukan perubahan tambahan:

Waktu tunggu rata-rata untuk server caching adalah 6 ms. selama sebulan terakhir, 5 ms. selama 24 jam terakhir dan 377 ms. selama satu jam terakhir.

Apa yang terjadi dengan parsing Anda?

Alih-alih mem-parsing, Anda bisa menggunakan properti pengecualian (yang dapat berisi apa pun yang diinginkan seseorang, segera setelah data dapat diserialisasi):

{
    message: "Connecting to the caching [...]",
    properties: {
        "timeout": 500,
        "statistics": [
            { "timespan": 1, "unit": "month", "average-timeout": 6 },
            { "timespan": 7, "unit": "day", "average-timeout": 4 },
            { "timespan": 1, "unit": "hour", "average-timeout": 377 },
        ]
    }
}

Seberapa mudah menggunakan data ini sekarang?

Kadang-kadang (seperti dalam .NET), pesan tersebut bahkan dapat diterjemahkan ke dalam bahasa pengguna (IMHO, menerjemahkan pesan-pesan itu benar-benar salah, karena setiap pengembang diharapkan dapat membaca dalam bahasa Inggris). Mem-parsing pesan semacam itu hampir mustahil.

Arseni Mourzenko
sumber
2
Poin bagus tentang pengguna akhir. Saya akan menambahkan bahwa pengguna akhir tidak boleh melihat pesan pengecualian untuk alasan keamanan juga. Mengetahui sifat kesalahan yang tepat untuk pengguna yang bukan orang awam dapat menyajikan exploit. Pengguna akhir hanya mengetahui kesalahan dalam konteks apa yang dia lakukan.
Neil
1
@ Neil: itu agak terlalu teoritis. Dalam praktiknya, manfaat menyembunyikan log dari pengguna jauh lebih besar daripada kompleksitas pencatatan online. Tidak termasuk aspek ramah pengguna. Bayangkan Anda menginstal Visual Studio pada Windows VM baru Anda. Tiba-tiba, instalasi gagal. Apakah Anda lebih suka pergi ke log dan mendiagnosis masalahnya sendiri (dengan bantuan Stack Exchange dan blog), atau menunggu sampai Microsoft memecahkan masalah dan menerbitkan perbaikan terbaru?
Arseni Mourzenko
4
Saya pikir semua orang sadar bahwa pesan "referensi objek ..." tidak membantu. Namun pertanyaan yang relevan adalah kode mesin apa yang Anda inginkan agar dihasilkan jitter yang menghasilkan pesan yang Anda inginkan? Jika Anda melakukan latihan ini, Anda dengan cepat menemukan bahwa pesan itu tidak dapat dengan mudah dihasilkan tanpa biaya kinerja yang sangat besar yang dibebankan pada semua kasus yang tidak biasa . Manfaat membuat pekerjaan pengembang yang ceroboh menjadi sedikit lebih mudah tidak dibayar oleh kinerja yang dicapai oleh pengguna akhir.
Eric Lippert
7
Mengenai pengecualian keamanan: semakin sedikit informasi dalam pengecualian semakin baik. Anekdot favorit saya adalah bahwa dalam versi pra-beta .NET 1.0 Anda bisa mendapatkan pesan pengecualian seperti "Anda tidak memiliki izin untuk menentukan nama file C: \ foo.txt". Hebat, terima kasih atas pesan pengecualian terinci itu!
Eric Lippert
2
Saya tidak setuju dengan tidak pernah menunjukkan kepada pengguna akhir pesan kesalahan terperinci. Ini telah terjadi kepada saya banyak kali bahwa saya mendapatkan pesan error samar mencegah saya dari mulai permainan saya / software, tetapi ketika saya google itu saya mencari tahu ada sebuah solusi mudah. Tanpa pesan kesalahan itu tidak akan ada cara untuk menemukan solusinya. Mungkin lebih baik menyembunyikan detail di bawah tombol "detail lebih lanjut"?
BlueRaja - Danny Pflughoeft
2

Jawabannya tergantung sepenuhnya pada pengecualian.

Pertanyaan paling penting untuk ditanyakan adalah "siapa yang bisa memperbaiki masalah?" Jika pengecualian disebabkan oleh kesalahan sistem file saat Anda membuka file, mungkin masuk akal untuk memberikan pesan itu kepada pengguna akhir. Pesan tersebut mungkin berisi informasi lebih lanjut tentang cara memperbaiki kesalahan. Saya dapat memikirkan satu kasus definitif dalam pekerjaan saya di mana saya memiliki sistem plugin yang memuat DLL. Jika pengguna membuat kesalahan ketik pada baris perintah untuk memuat plugin yang salah, pesan kesalahan sistem yang mendasarinya sebenarnya berisi informasi yang berguna untuk memungkinkan mereka untuk memperbaiki masalah.

Namun, sebagian besar waktu, pengecualian yang tidak tertangkap tidak dapat diperbaiki oleh pengguna. Sebagian besar dari mereka melibatkan perubahan kode. Dalam kasus ini, konsumen pesan jelas merupakan pengembang.

Kasus pengecualian yang tertangkap lebih rumit karena Anda memiliki kesempatan untuk memproses pengecualian dengan benar dan melemparkan yang lebih ramah. Satu bahasa scripting yang saya tulis melemparkan pengecualian yang pesannya sangat jelas ditujukan untuk pengembang. Namun, ketika tumpukan dibatalkan, saya menambahkan jejak tumpukan yang dapat dibaca pengguna dari dalam bahasa scripting. Pengguna saya sangat jarang bisa membaca pesan, tetapi mereka bisa melihat jejak tumpukan di skrip mereka dan mencari tahu apa yang terjadi 95% dari waktu. Untuk 5% sisanya, ketika mereka memanggil saya, kami tidak hanya memiliki jejak stack, tetapi pesan siap pengembang untuk membantu saya mencari tahu apa yang salah.

Secara keseluruhan, saya memperlakukan pesan Pengecualian sebagai konten yang tidak dikenal. Seseorang lebih jauh ke hulu, yang tahu lebih banyak tentang implementasi, telah memberikan pengecualian pada perangkat lunak saya. Terkadang saya memiliki firasat bahwa konten yang tidak dikenal akan bermanfaat bagi pengguna akhir, terkadang tidak.

Cort Ammon - Pulihkan Monica
sumber
1
"Jika pengecualian disebabkan oleh kesalahan sistem file saat Anda membuka file, mungkin masuk akal untuk memberikan pesan itu kepada pengguna akhir": tetapi ketika Anda melempar kesalahan sistem file, Anda tidak tahu konteksnya: itu mungkin tindakan pengguna, atau sesuatu yang sama sekali tidak terkait (seperti aplikasi Anda membuat cadangan dari beberapa konfigurasi, tanpa pengguna bahkan menyadarinya). Ini menyiratkan bahwa Anda akan memiliki blok coba / tangkap yang menampilkan pesan kesalahan , yang sangat berbeda dengan menampilkan pesan pengecualian secara langsung.
Arseni Mourzenko 3-15
@ MainMa Anda mungkin tidak tahu konteksnya secara eksplisit, tetapi ada banyak petunjuk. Seperti, misalnya, jika mereka sedang dalam proses membuka file, dan pengecualiannya adalah "IOError: izin ditolak," ada kemungkinan yang wajar pengguna dapat menempatkan 2 dan 2 bersama-sama dan mencari tahu file yang dimaksud. Editor favorit saya melakukan ini - itu hanya menampilkan teks pengecualian di kotak dialog, tanpa bulu. Di sisi lain, jika saya memuat aplikasi saya dan mendapat "izin ditolak" saat memuat banyak gambar, itu jauh lebih masuk akal untuk menganggap pengguna dapat melakukan sesuatu dengan informasi tersebut.
Cort Ammon - Reinstate Monica
Berdebat tentang maksud Anda, saya belum pernah melihat nilai apa pun dalam menunjukkan kepada pengguna NullReferenceException, selain itu tindakan sekadar menampilkan pesan itu menunjukkan bahwa perangkat lunak Anda tidak memiliki petunjuk bagaimana memulihkan! Pesan pengecualian itu tidak pernah berguna bagi pengguna akhir.
Cort Ammon - Reinstate Monica