Jika Tidak Akan Melakukan Apa Pun, Setidaknya Lakukan “Tidak Melakukan Apa Pun” dengan Benar
(devblogs.microsoft.com)- Meski karena platform tertentu atau status penghentian fungsi nyata tidak bisa disediakan, agar aplikasi yang sudah ada tidak rusak, perilaku “tidak melakukan apa pun” harus dirancang di dalam kontrak API yang terdokumentasi
- Di lingkungan seperti Xbox yang tidak memiliki infrastruktur pencetakan, respons sukses dengan 0 printer lebih aman bagi aplikasi yang ditulis dengan asumsi PC dibanding
NotSupportedException - Pendekatan ini disebut API inert, yaitu desain yang mempertahankan permukaan API dan spesifikasinya, tetapi tidak melakukan pekerjaan yang benar-benar berguna
- Jika pada API widget yang akan dipensiunkan
CreateWidgetberhasil tetapi mengembalikannullptr, pemanggil akan menghadapi keadaan yang kontradiktif, sehingga jalur kegagalanERROR_CANCELLEDyang sudah terdokumentasi lebih konsisten - API inert yang benar hanya menghilangkan fungsinya, sambil memakai jalur kegagalan yang memang sudah diantisipasi kode lama sehingga pengecualian, handle yang salah, dan crash dapat dikurangi
Kontrak API tetap ada meski fungsinya tidak ada
- Ada kalanya API harus dibuat tidak melakukan apa-apa, tetapi ketidakaktifannya pun harus sesuai dengan kode lama dan perilaku yang sudah didokumentasikan
- Ketidakaktifan yang dirancang dengan buruk dapat menimbulkan pengecualian, nilai balik yang kontradiktif, dan status yang tidak valid, yang berujung pada crash aplikasi atau error pengguna yang tidak perlu
Cara menonaktifkan API pencetakan di Xbox
- Windows memiliki infrastruktur pencetakan yang luas, tetapi Xbox tidak memiliki infrastruktur tersebut
- Jika di Xbox aplikasi mencoba mencetak lalu fungsi cetak melempar
NotSupportedException, masalahnya bisa membesar- Aplikasi yang dipasang di Xbox kemungkinan besar sebelumnya diuji terutama di PC
- Di PC, pencetakan selalu tersedia, sehingga pengecualian yang tidak tertangani dapat berujung pada crash aplikasi
- Sekalipun pengecualian ditangkap, pengguna bisa melihat pesan kesalahan seperti “berikan incident code kepada tim dukungan”
- Desain yang lebih baik adalah fungsi cetak tetap berhasil tetapi melaporkan bahwa tidak ada printer yang terpasang
- Aplikasi menampilkan UI pemilihan printer dan menunjukkan daftar kosong
- Pengguna melihat bahwa tidak ada printer dan dapat membatalkan permintaan cetak
- Untuk aplikasi yang mencoba membantu memasang printer, fungsi instalasi dapat langsung kembali sambil memberikan kode hasil yang berarti pengguna membatalkan tindakan
Syarat API inert
- Perilaku “tidak melakukan apa pun” seperti ini disebut inert
- API inert berarti permukaan API tetap ada dan berjalan sesuai spesifikasi, tetapi pada praktiknya tidak melakukan pekerjaan yang berguna
- Intinya adalah tidak melakukan apa pun dengan cara yang tetap konsisten dengan dokumentasi dan paling kecil kemungkinannya menimbulkan masalah pada kode yang sudah ada
- Pada API pencetakan, fungsi untuk memeriksa apakah pencetakan memang tersedia juga bisa ditambahkan
- Aplikasi dapat memakai fungsi ini untuk menyembunyikan tombol Print pada sistem yang sama sekali tidak mendukung pencetakan
- Aplikasi yang sekadar mengasumsikan pencetakan tersedia pun akan berperilaku seperti pada “sistem tanpa printer dan upaya pemasangan yang tidak berdampak”
Desain yang harus dihindari pada API widget yang akan dipensiunkan
- API yang akan dipensiunkan memiliki fungsi untuk membuat handle widget, fungsi yang menerima handle widget, dan fungsi untuk menutup handle widget
- Usulan awalnya adalah
CreateWidgetmengembalikanS_OKsambil menetapkan*widget = nullptr- Pemanggilan berhasil, tetapi statusnya menjadi tidak memperoleh handle yang valid
- Di antara kode uji yang nyata, ada kode yang menentukan keberhasilan bukan dari nilai balik, melainkan dari apakah handle bernilai
null
- Desain
EnableWidgetyang mengembalikan “handle tidak valid” juga membingungkan pemanggil- Aplikasi meneruskan handle yang diperoleh setelah
CreateWidgetberhasil keEnableWidget - API merespons bahwa handle tersebut tidak valid
- Pemanggil menghadapi keadaan kontradiktif: “saya meminta widget dan mendapatkannya, tetapi ketika saya tunjukkan lagi, katanya itu bukan widget”
- Aplikasi meneruskan handle yang diperoleh setelah
Desain yang diperbaiki dengan jalur kegagalan yang terdokumentasi
- Dokumentasi yang ada sudah memiliki nilai balik
ERROR_CANCELLEDyang berarti pengguna membatalkan pembuatan widget - Karena aplikasi memang sudah harus menangani kemungkinan widget tidak dibuat akibat kondisi eksternal, pembuatan widget bisa dibuat selalu gagal sebagai pembatalan oleh pengguna
- Alur desain yang diperbaiki sederhana
CreateWidgetmembiarkan*widget = nullptrdan mengembalikanHRESULT_FROM_WIN32(ERROR_CANCELLED)- Karena pembuatan widget selalu gagal, tidak ada handle widget yang valid
GetWidgetAliases,EnableWidget, danClosemengembalikanE_HANDLEyang berarti handle tidak valid
- Dengan cara ini, karena tidak ada widget, tidak perlu membuat alias dummy
- Karena tidak ada widget yang valid, juga tidak ada keadaan di mana aplikasi dapat secara normal meminta alias
Patokan saat membawa API desktop ke lingkungan lain
- Di desktop, API pencetakan selalu ada, dan fungsi untuk “mengambil daftar printer” didokumentasikan agar tidak melempar pengecualian
- Saat API ini dibawa ke Xbox agar aplikasi desktop yang sudah ada tetap bisa berjalan, perilaku inert yang tepat adalah mengembalikan secara jujur fakta bahwa “Xbox tidak memiliki printer”
- Melempar pengecualian sebagai jawaban atas pertanyaan “ada berapa printer?” tidak sesuai dengan kontrak API yang terdokumentasi
- Prinsip yang sama juga berlaku saat menghentikan API yang sudah ada
- Meski tidak melakukan pekerjaan yang berguna, perilaku API tetap harus konsisten dengan kontraknya
- Penting untuk memakai nilai balik dan status yang memang sudah bisa ditangani oleh kode yang ada
2 komentar
Pendapat di Lobste.rs
Terlepas dari leluconnya, saya tidak setuju dengan pemrograman yang terlalu defensif dan pengalaman pengguna seperti ini. Akibatnya, perangkat lunak tidak menjalankan tugasnya tanpa alasan yang jelas, dan tidak ada cara untuk mengetahui mengapa. Aplikasi seharusnya menangkap error dan, jika memungkinkan, membuat pesan yang ramah pengguna, atau setidaknya menampilkan pesan error aslinya kepada pengguna. Jika ini pekerjaan latar belakang, harus ada log error
Saya mengakui bahwa tulisan ini ditulis dari sudut pandang pengembang API, bukan pengembang aplikasi. Jadi error API harus didokumentasikan, dan harus menyediakan pesan error yang bisa ditindaklanjuti oleh pihak pemanggil
Saya juga tidak suka ketika tombol disembunyikan di UI hanya karena tidak ada hak akses. Jika ruang memungkinkan, menurut saya lebih baik tombolnya tetap ditampilkan namun dinonaktifkan, lalu ketika pengguna mengarahkan mouse ke sana, tampilkan pesan yang menjelaskan bagaimana cara mengaktifkannya
Secara umum, lebih baik menuntut ketepatan. Namun jika pengguna yang sudah ada berjumlah 1 miliar, sangat bijak untuk sebisa mungkin tidak merusaknya, dan dari sudut pandang pengguna hal itu memang menciptakan nilai nyata di tingkat sistem karena semuanya tetap berjalan. Pada akhirnya sikap yang tepat adalah fail fast, tapi jangan sampai membuat banyak hal ikut gagal
Ini lebih dekat ke stabilitas ABI daripada API. Di Windows, perangkat lunak yang dibangun 15 tahun lalu pun harus tetap berjalan di OS baru sebisa mungkin. Karena signature fungsi tidak bisa diubah, Anda harus melakukan kebohongan putih agar API yang sudah tidak lagi bermakna tetap bisa berjalan
Misalnya, API masih berpura-pura bahwa Active Desktop itu ada. Alternatifnya adalah merusak banyak perangkat lunak lawas yang sudah telanjur ada
Jadinya kita tidak tahu apakah fitur itu memang sudah hilang, atau hanya tersembunyi di layar lain di tengah jalan
Tetapi kalau aplikasinya tidak melakukan itu, orang yang memakai aplikasi tersebut akan menyalahkan Windows. Mereka menyalahkan Windows, bukan aplikasinya, dan hal yang sama terjadi bahkan saat aplikasinya crash
Itulah sebabnya Microsoft membuat jalan memutar seperti ini. Jauh lebih mudah membiarkan pekerjaan cetak menghilang begitu saja, sehingga pengguna akan berhenti sejenak lalu menerima, “oh benar, memang tidak ada printer”
ifuntuk mengecek null. Setiap kali melihatnya, saya teringat tulisan iniJadi saya menginstruksikan agen agar tidak mengulang-ulang pemeriksaan null, melainkan menggunakan fungsi yang aman dan memeriksa sekali saat deklarasi bahwa nilainya tidak akan pernah null
Pendapat di Hacker News
Saya mempelajarinya sebagai cara menelan error, dan menganggapnya praktik yang buruk.
Selain tidak menyelesaikan masalah nyata bahwa output tidak bisa dilakukan di Xbox, cara ini juga menyembunyikan seberapa rusaknya perangkat lunak, sehingga penemuan bug dan pengujian menjadi jauh lebih sulit.
panicdi Go bagus karena bukan dibuat untuk disalahgunakan atau dipulihkan saat runtime, melainkan sebagai alat yang membunyikan sirene keras pada saat pengujian bahwa programmer telah melakukan kesalahan.Jika para aktor di dalam sistem secara aktif menyembunyikan cacat atau error mereka sendiri, mencari dan memperbaiki penyebabnya menjadi sulit secara eksponensial.
Dalam sistem B2B, microservices, dan berorientasi API yang saya tangani, permintaan di luar spesifikasi memang seharusnya gagal dengan cepat.
Namun Windows telah sangat berusaha keras demi kompatibilitas mundur, bahkan ada kasus mempertahankan perangkat lunak terkenal yang rusak seperti SimCity sampai melakukan patch memori: https://arstechnica.com/gadgets/2022/10/windows-95-went-the-...
Di lingkungan desktop, ketika pengguna tidak bisa memperbaiki kode sistem, dalam banyak kasus pengalaman pengguna akhir lebih penting daripada ketepatan.
Mengapa BSOD terjadi tidak penting; Microsoft akhirnya belajar bahwa perangkat lunak atau perangkat keras yang buruk akan merusak reputasi Microsoft.
Secara pribadi saya lebih menyukai desain yang gagal cepat atau berhenti pada input atau kondisi yang salah, tetapi saya juga memahami nilai pendekatan seperti ini di lingkungan tersebut.
Yang penting adalah konteks, bukan menerapkan salah satu sisi secara dogmatis.
Misalnya, pendekatan Chen tidak boleh dibawa ke desain reaktor nuklir atau high-frequency trading, tetapi bisa menjadi pendekatan yang bagus untuk game engine.
Masalah apakah suatu komponen runtime harus dibiarkan “nonaktif secara tidak berbahaya” adalah keputusan desain yang mendahului apakah implementasi akan memunculkan
panicatau tidak.Dalam kasus ini, menentukan makna “mencetak dari Xbox” adalah tugas Microsoft.
Itu bisa didefinisikan sebagai error, atau secara teoretis bisa didefinisikan sebagai fitur yang mungkin didukung nanti tetapi saat ini belum memiliki cara nyata untuk dijalankan.
Jika sebuah “game” tahu cara mencetak, seperti mencolokkan printer USB atau memakai port ekspansi pada game Gameboy yang mendukung GB Printer, maka secara teori Xbox pun bisa mencetak.
Game Xbox yang mencoba mencetak belum tentu merupakan error secara hakiki; mendefinisikannya sebagai error pun hanyalah pilihan dengan trade-off seperti portabilitas aplikasi.
Bisa saja menampilkan layar pilihan tanpa printer yang dapat dipilih seperti di Xbox, membuat API berbohong bahwa output sudah dicetak, atau benar-benar mendukung pencetakan.
Baru setelah makna “mencetak dari Xbox” didefinisikan sebagai error, persoalan apakah error itu tidak dilempar dan malah ditelan menjadi masalah implementasi atau debugging.
Ada dua penanganan yang mungkin: karena mesin tidak akan pernah punya printer, keluarkan error; atau karena mesin tidak akan pernah punya printer, kembalikan daftar printer kosong.
Keduanya valid, tetapi hanya satu yang tidak merusak user space.
“Membunyikan sirene keras” bagus untuk tester atau developer, tetapi tidak begitu bagus untuk pengguna akhir.
Dari sudut pandang pengguna akhir, menelan error lebih baik daripada aplikasi mati.
Tidak ada cara mengetahui apa yang gagal: perangkat lunak di komputer saya, perangkat keras, kabel Ethernet atau Wi-Fi, switch, router, modem kabel, kabel internet sampai ke rumah, ISP, atau sistem jarak jauh yang hendak diakses—sama sekali tidak ada perbaikan.
Reaksi negatif memang bisa diduga, tetapi pekerjaan kotor seperti inilah alasan mengapa dokumen Word ’97 atau game yang dikompilasi untuk MS-DOS 30 tahun lalu terbuka seperti yang diharapkan saat diklik.
Kompatibilitas mundur selalu messy, dan pilihannya hanya melakukannya secara tidak sempurna atau tidak melakukannya sama sekali.
Pada akhirnya saya memakai GOG, dan GOG pun pada akhirnya memvirtualisasikan lingkungannya.
Misalnya, saya rasa tidak ada orang yang akan berhasil menginstal Sim City orisinal di Windows 11.
Formatnya bergantung pada driver printer yang terpasang.
Sebaliknya, file LaTeX dari 35 tahun lalu masih berjalan baik sampai sekarang.
Word tidak bisa menjamin format tidak berubah bahkan saat dibuka kembali di komputer yang sama dan versi yang sama, dan itulah yang membuat saya belajar TeX.
Untuk game juga, Microsoft merusak banyak sekali game ketika menghentikan GFWL.
Dark Souls orisinal tidak bisa dimainkan sampai dirilis ulang di Steam, jadi harus memakai versi bajakan.
Dialog “tidak didukung” akan muncul, dan dosbox bisa diunduh, tetapi itu bukan “persis seperti yang diharapkan”.
Hampir setiap kali saya menyerah atau membelinya lagi di GOG, yang pada dasarnya berarti saya membayar untuk pekerjaan merapikan masalah kompatibilitas.
Tidak ada yang lebih membuat frustrasi daripada UI perangkat yang mengisyaratkan bahwa sesuatu seolah-olah benar-benar ada, tetapi lalu mengatakan saat ini tidak ada
Pada akhirnya kita harus meluangkan waktu dulu baru tahu bahwa perangkat-perangkat itu tidak didukung, dan layar itu hanyalah mockup yang dibuat seseorang
Jika aplikasi tidak mengantisipasi kondisi output tidak didukung, lalu output melempar exception, aplikasi mungkin akan langsung mati
Aplikasi yang mengantisipasi output tidak didukung seharusnya terlebih dahulu memeriksa dukungan output lewat API eksplisit, dan jika tidak didukung, tidak menampilkan UI output
Tulisan ini bukan tentang aplikasi seperti itu, melainkan tentang bagaimana menangani aplikasi yang tidak memeriksa terlebih dahulu
Bug-nya ada di aplikasi, tetapi platform yang baik seharusnya berusaha semaksimal mungkin membuat aplikasi buruk tetap berjalan, alih-alih membiarkannya mati begitu saja
Peran Microsoft adalah memastikan aplikasi tidak mati hanya karena penyedia perangkat lunak tidak mempertimbangkan printer
Perilaku saat aplikasi hendak mencetak lalu meminta pengguna memilih printer dan menampilkan daftar kosong—sepertinya Microsoft belum belajar selama 30 tahun: https://www.reddit.com/r/hacking/comments/djvzd/windows_nt_l...
Arah konkret yang diusulkan tulisan itu, yakni bahwa komponenlah yang harus menderita sebelum pengguna menderita, memang benar, tetapi framing-nya sangat mengganggu
Ungkapan seperti “ada kalanya API harus dibuat tidak melakukan apa-apa”, “fungsi output melempar
NotSupportedExceptionitu salah” sama sekali bukan nasihat umumIni adalah hack untuk mendukung klien yang buruk, dan kadang memang diperlukan, tetapi bukan nasihat yang normal atau bisa digeneralisasi
Dalam ungkapan seperti ini tampak jejak penderitaan sebagai developer Microsoft yang sudah terinternalisasi
Jika klien tidak berubah tetapi API merusaknya, yang buruk bukan kliennya, melainkan API-nya
Ini sepenuhnya normal dan bisa digeneralisasi untuk setiap platform yang diadopsi luas dan menganggap serius kompatibilitas mundur
Windows adalah contoh utama, tetapi hal yang sama juga terjadi pada ABI kernel Linux, glibc, platform web, Java, dan lainnya
Anda tidak bisa memutar balik waktu untuk mengompilasi ulang atau menulis ulang klien
Jika klien sudah memeriksa
NotSupportedException, Anda harus mengembalikannya, tetapi jika tidak, diperlukan pendekatan lainIni Windows
Anda tidak boleh merusak aplikasi yang diandalkan pengguna
Jika ada aplikasi klien yang “buruk” atau berperilaku salah, Anda harus memakai semua workaround, shim, dan hack kompatibilitas yang diperlukan agar ia berjalan seperti yang diharapkan
Hal yang sama berlaku meski harus memasukkan kode manajemen memori khusus agar SimCity berjalan
Pelanggan Anda bergantung pada klien itu, dan jika berhenti karena perubahan yang Anda buat, mereka akan menyalahkan Anda
Di sini semua orang terpaku pada “tidak melakukan apa-apa” dan “penanganan error”, tetapi sepertinya tulisan itu kurang berhasil menjelaskan bahwa maksudnya bukan menelan semua masalah lalu bernyanyi kumbaya
Konteks sebenarnya adalah emulasi dan kompatibilitas untuk perangkat lunak yang tidak akan berubah, dan ini sangat berbeda dari kebanyakan situasi
Microsoft sendiri juga sering mengaburkan batas di antara keduanya, sehingga makin membingungkan, tetapi yang dibahas di sini bukan penanganan error secara umum
Emulasi pada dasarnya adalah berbohong
Tidak ada cara nyata untuk memasang printer ke Xbox, Anda menjalankan game Windows di Linux, dan iPhone palsu bisa jadi sebenarnya server ARM di AWS
Jika Anda akan berbohong, Anda harus melakukannya dengan meyakinkan, agar aplikasi lama tetap berjalan
Ketika Microsoft mengatakan aplikasi Windows lama bisa di-porting ke Xbox, aplikasi Windows memiliki janji bahwa ia bisa mencetak, jadi Xbox harus berbohong agar hal itu tampak mungkin
Sebaliknya, jika program melakukan use-after-free dan Anda diam-diam memutar jalan seolah tidak ada bug, itu bukan penanganan yang benar
Karena tidak ada yang pernah berjanji bahwa use-after-free lama pun akan kompatibel sampai tingkat bug di platform baru
Itu hanya asumsi karena tafsir seperti itu mendukung posisi yang Anda inginkan
Nyatanya isi tulisan itu tetap berlaku jika besok saya menulis program untuk Xbox dan mencoba mencetak
Tidak ada bagian yang membedakan bahwa perilaku ini hanya berlaku untuk perangkat lunak lama yang tidak lagi dipelihara
Saya sekaligus suka dan tidak suka pendekatan ini
Secara naluriah saya tidak suka menangani masalah lewat kepatuhan jahat
Di sisi lain, jika tujuannya adalah membuat lebih banyak pengguna bisa menjalankan lebih banyak perangkat lunak di platform, meskipun pencetakan rusak, saya sepenuhnya setuju bahwa ini keputusan yang baik
Alur komentar ini terasa aneh
Jelas ada orang yang membenci apa pun yang dilakukan Microsoft
Inti blog itu adalah membuat proses membawa aplikasi (UWP) ke XBOX tanpa friksi
Tanpa kompilasi ulang, tanpa
ifdefyang kotor, tanpa pekerjaan tambahan, aplikasi “langsung berjalan”, lalu setelah itu developer bisa menyesuaikan aplikasi untuk platform XBOXYang penting adalah developer bisa membawa aplikasi ke XBOX tanpa upaya apa pun, dan ini baik bagi platform maupun developer
Bagi pengguna, daftar printer yang kosong menjadi sinyal jelas bahwa mereka tidak bisa mencetak
Yang terpenting, aplikasi tidak mati atau menampilkan error terburuk seperti “Terjadi masalah. Coba lagi nanti”
Pesan itu bukan pengalaman pengguna yang lebih baik
Developer bisa mengompilasi ulang aplikasi dan memasukkan pesan yang lebih deskriptif, tetapi kalau begitu kita kembali ke titik awal
Misalnya, tuduhan bahwa error ditelan sama sekali bukan inti masalah
Poin utama yang terlewat oleh pihak yang mengkritik tulisan ini adalah bahwa di sini tidak ada situasi pengecualian yang mengharuskan pelemparan exception
Tidak ada printer yang terhubung ke perangkat ini, dan karena dalam kasus ini memang demikian secara definisi, aplikasi harus menangani situasi itu dengan rapi, bukan mati
Laptop tidak akan melempar exception hanya karena tidak bisa mengakses printer
Setelah menambahkan printer di pengaturan lalu berpindah ke tab “Color Profiles” selama instalasi, pada timing tertentu gnome-control-center akan mati
Jika dilihat lebih dekat, ia mencoba menghitung printer yang tersedia; ia tahu seharusnya ada printer, tetapi informasinya belum ada, sehingga ia langsung mati
Untungnya, cukup tunggu beberapa detik sampai GNOME selesai menginstal printer lalu buka kembali pengaturan
Meski begitu, untuk aplikasi inti yang membantu pengguna akhir dari berbagai latar belakang memakai desktop dengan benar, di dunia ideal aplikasi itu seharusnya sama sekali tidak pernah mati
Memikirkan kondisi batas itu sulit, dan membayangkan bahwa penanganan kondisi batas itu sendiri juga memiliki kondisi batas lain lebih sulit lagi
Benar atau salah, setidaknya penulis berpikir lebih dalam daripada sekadar “ini aplikasi buruk, jadi harus ditulis ulang agar mendukung Wayland”
Dulu, browser dianggap memiliki strategi yang bagus karena berusaha sebaik mungkin menampilkan halaman meski kode HTML mengandung kesalahan
Pendekatannya adalah menebak maksud pembuatnya semaksimal mungkin lalu lanjut, dengan anggapan bahwa kesalahan itu buruk dan pengguna tidak menginginkan kesalahan
Saya kira kita sudah belajar dari pengalaman itu, tapi rupanya tidak
Upaya XHTML untuk menggantikannya dengan validasi ketat gagal, dan sebagai gantinya HTML5 yang berhasil justru kembali mendefinisikan secara eksplisit bagaimana setiap urutan yang salah sebisa mungkin harus ditafsirkan, agar browser setidaknya berperilaku konsisten
Jika browser awal langsung menampilkan kesalahan begitu menemui tag yang belum pernah dilihat, fitur browser baru akan mati lemas sejak lahir
HellodanWorld!, kita bisa melihat apakah berkat kebijaksanaan yang sudah kita peroleh sekarang akan muncul kesalahanNamun jika masih dirender dengan baik, itu berarti masih banyak yang perlu dipelajari, atau bisa juga berarti bahwa masuk akal untuk menangani jenis kesalahan tertentu dengan anggun dan mati pada situasi yang benar-benar pengecualian
Rasanya tidak akan banyak yang dirindukan jika pindah waktu ke tahun 1999