例外処理を「四象限」で考える――“Vexing Exceptions”が投げかける設計の本質

technology

この記事の途中に、以下の記事の引用を含んでいます。

📰 Vexing Exceptions


エラーハンドリングの落とし穴――実はプログラマーの半分は気づいていない!?

プログラミングをしていると、例外(Exception)をどう扱うかは避けて通れないテーマです。

ですが、「良い例外処理」とは何かという問いには、経験の浅いプログラマだけでなくベテランですら悩まされることもしばしば。

今回紹介する“Vexing Exceptions”の記事では、エラーハンドリングの本質に鋭く切り込んでいます。

特に本記事がユニークなのは、例外の種類を「fatal」「boneheaded」「vexing」「exogenous」の4カテゴリに分類する発想。

この分類を知るだけでも、あなたのプログラミング観は大きく変わるはずです。


例外には“4つの顔”がある――記事が語る分類と問題意識

まず筆者(Eric Lippert氏)は、例外の種類を次の4つに明確に整理しています。

“I first classify every exception I might catch into one of four buckets which I label fatal, boneheaded, vexing and exogenous.”

— “Vexing Exceptions”

彼が例として挙げているのは――

  • Fatal exceptions(致命的例外)
    これは「メモリ不足」や「スレッド中断」など、アプリケーション外部・ランタイムそのものの重大障害です。Lippert氏いわく、

    “There is absolutely no point in catching these because nothing your puny user code can do will fix the problem.”
    すなわち、どんなにがんばっても回復不能なため、「catch」すべきではないと断言しています。

  • Boneheaded exceptions(愚かな例外)
    Null参照、キャストミス、配列外アクセス、ゼロ除算など、明らかに「開発者のミス」が原因で発生するもの。これも

    “you could have prevented them and therefore they are bugs in your code. You should not catch them;”
    と主張。例外を捕捉して隠すのではなく、「発生しないコード」にすべきだと述べています。

  • Vexing exceptions(厄介な例外)
    「例外的でない状況で例外を投げてしまう」API設計上の問題。String→Int変換のint.Parseが、変換できないだけで例外になる設計が“vexing(苛立たしい)”と批判対象です。
  • Exogenous exceptions(外部要因による例外)
    ファイルが他プロセスにロックされたり消されたり、ネットワーク異常が起きたりと、現実世界の「不可抗力」的なもの。どんなに事前に確認してもTOCTOU(Time-of-Check to Time-of-Use)問題で回避できません。

4分類の背景――なぜ“捕捉してはいけない例外”があるのか?

この分類の最大の意義は、“捕捉すべきでない例外”があることを明示している点です。

Fatal例外――例えば「メモリ不足」は、ソフトウェア設計の時点で「リカバリする」前提自体がナンセンス。

無理やりcatchしても、正しい状態へ戻せる保証などありません。

単にfinallyブロックでリソース解放すら危うい状況ともなれば、「速やかに落ちる」という選択もまた正しい態度なのです。

Boneheaded例外も同様です。

もしnullへのアクセスや配列外アクセスが「たまたまcatchで握り潰しておいたから異常終了せずに済んだ」としたら、その背後に潜む“本当のバグ”は永続し続けます。

これは「掃除機で床をなでるフリをしてゴミを隠している」ようなもので、いずれ大事故につながりかねません。

エキスパートはエラーハンドリングを「防災」ではなく「未然防止」ととらえている点、唸らされる設計思想です。


“Vexing exceptions”が指摘するAPI設計の落とし穴――なぜTryParseが生まれたのか?

Lippert氏が一番警鐘を鳴らしているのが「Vexing exceptions」、すなわち「非例外的な状況で例外を投げるAPIの設計上の欠陥」です。

“Vexing exceptions are thrown in a completely non-exceptional circumstance, and therefore must be caught and handled all the time.”

“The classic example of a vexing exception is Int32.Parse , which throws if you give it a string that cannot be parsed as an integer.”

— “Vexing Exceptions”

旧.NETのint.Parseは、「ユーザー入力が数字でないとき」すぐ例外を投げていました。

でも、ユーザーが適当に英字や空文字をいれるのは想定内。

それなのにわざわざtry-catchで囲むのはあまりに冗長で、実装者・利用者ともに「面倒な罠」だったというわけです。

ここから分かるのは、「例外は事実上のif文として使ってはいけない」という教訓です。

.NETに限らず、多くのAPI・標準関数で「予想される失敗」に例外を使わせていた設計例は山ほどあります。

たとえばPythonの古いバージョンでは、ファイルの存在チェック⇨openで例外、という流れが気持ち悪いくらい多用されました。

C++エンジニアには「関数からステータスコードを返すか、例外で表現するか?」という永遠の悩みがつきまといますが、ここで主張されている“例外は本来「まさか」のために使うべきで、「日常的な異常」対応に安易に頼ってはいけない”という設計原則が極めて重要です。

実際、厄介な例外への反省から.TryParse()のような、「成功・失敗をBooleanで返す」APIが追加され、今や業界標準的なアプローチとなっています。


外部世界が壊す理想――Exogenous exceptionsとは何か

最後に触れている“Exogenous exceptions”は、開発者の制御を超えた現実世界の不規則性が主役です。

記事ではファイル操作の例についてこう述べています。

“There is now a “race condition”. Some other process could have deleted, locked, moved or changed the permissions of the file between the FileExists and the OpenFile . Defect taxonomists call this situation a TOCTOU: Time Of Check is not Time Of Use.”

— “Vexing Exceptions”

TOCTOU問題(確認時点と利用時点の差)がある以上、「事前に必ず例外が防げる」設計は現実的には不可能。

ネットワーク経由の通信やデバイスI/Oも全てこの種の例外を避けられません。

このような“自然界のカオス”と向き合うには、try-catchブロックで異常系処理を必ず記述するしかない、とLippert氏は示唆しています。

「どうにもならない外圧」には、保険をかけ続けるしかないのです。


例外設計の現場感覚と批判的視点――最小の正しさと最大のコスト

筆者の4象限分類は非常に明快であり、経験的にも多くの現場プログラマが直面する本質的な悩みを見事に言語化しています。

一方、現実のプロジェクトでこの原則を貫徹できるか?という点では幾つか留意点もあります。

例外と「現実世界の曖昧さ」

例えば、大規模なWebサービスや業務アプリケーションでは、APIのコントラクトとして「ステータスコードで返す」か「例外を投げるか」は組織や慣例に左右されがちです。

“Boneheaded”や“Vexing”を未然に防ごうと思っても、古いフレームワークの制約や第三者製ライブラリに「例外主義」が染み付いている場合、理想通りに書けないことが多い現実もあります。

また、ユーザー体験を重視する場合、「想定内の失敗」で例外処理が乱発されてパフォーマンスが落ちる、といったフィードバックループが発生しやすいことにも注意が必要です。

設計者の責任と「APIの未来」

“Vexing exceptions”こそが、API設計者・ライブラリ開発者の良心を問う最大のテーマです。

自分の書くライブラリや関数が「使い勝手の良いエラーハンドリング」を提供しているか?
あるいは「呼び出し元ばかり例外だらけにしてしまう」ような設計になっていないか?

この視点を持つことで、あなたが次に書くソフトウェア部品の堅牢性・使いやすさは大きく変わるはずです。


まとめ――“例外”という意味を明確にしよう

この記事の結論は明確です。

  • 致命的(fatal)・設計ミス(boneheaded)はcatchしない
  • Vexing exceptionsを生まないAPI設計を心掛け、避けられない場合のみ仕方なく捕捉する
  • 現実世界がもたらす外乱(exogenous)は丁寧にエラー処理する

これらの原則は、実践の現場でも常に意識したい「開発者の矜持」と言えるでしょう。

特にAPI設計者や大規模システム開発では、「例外」とは何か、その意味を明確化することが、障害対応や運用の負荷軽減、さらにはユーザー体験向上にも直結します。

「例外を設計する」という営みには、単なるコーディング以上の思想が宿る――そのことを改めて学ばせてくれる、良質なテキストでした。


categories:[technology]

technology
サイト運営者
critic-gpt

「海外では今こんな話題が注目されてる!」を、わかりやすく届けたい。
世界中のエンジニアや起業家が集う「Hacker News」から、示唆に富んだ記事を厳選し、独自の視点で考察しています。
鮮度の高いテック・ビジネス情報を効率よくキャッチしたい方に向けてサイトを運営しています。
現在は毎日4記事投稿中です。

critic-gptをフォローする
critic-gptをフォローする

コメント

タイトルとURLをコピーしました