Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
エラーを定義から消し去る
Search
Sponsored
·
Your Podcast. Everywhere. Effortlessly.
Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
→
shinaps
April 01, 2026
6
0
Share
エラーを定義から消し去る
shinaps
April 01, 2026
More Decks by shinaps
See All by shinaps
動くコードでは不十分
shinaps
0
22
サラリーマンの小遣いで作るtoCサービス - Cloudflare Workersでスケールする開発戦略
shinaps
2
580
react-callを使ってダイヤログをいろんなとこで再利用しよう!
shinaps
2
690
CloudflareとHonoを使って飲食店のレビューができるLINEアプリを作った
shinaps
3
1.5k
Featured
See All Featured
So, you think you're a good person
axbom
PRO
2
2k
The MySQL Ecosystem @ GitHub 2015
samlambert
251
13k
Building the Perfect Custom Keyboard
takai
2
720
Designing for humans not robots
tammielis
254
26k
SEO Brein meetup: CTRL+C is not how to scale international SEO
lindahogenes
1
2.5k
What the history of the web can teach us about the future of AI
inesmontani
PRO
1
500
Information Architects: The Missing Link in Design Systems
soysaucechin
0
850
GraphQLとの向き合い方2022年版
quramy
50
14k
Deep Space Network (abreviated)
tonyrice
0
97
Measuring Dark Social's Impact On Conversion and Attribution
stephenakadiri
1
170
Efficient Content Optimization with Google Search Console & Apps Script
katarinadahlin
PRO
1
440
The #1 spot is gone: here's how to win anyway
tamaranovitovic
2
1k
Transcript
エラーを定義から消し去る A Philosophy of Software Design 第10章から 1 / 24
目次 例外はなぜ複雑さを生むのか 例外処理コードの本質的な難しさと不必要な例外の増殖 技法1:エラーを定義から消し去る APIの意味・定義を変更して例外条件そのものを排除する 技法2:例外のマスキング 低レベルで例外を処理し、上位レベルに見せない 技法3:例外の集約 多くの例外を一箇所でまとめて処理する 技法4:クラッシュさせるだけ
回復が困難で頻度の低いエラーはアプリケーションを中止する 2 / 24
例外はなぜ複雑さを生むのか 例外処理コードの本質的な難しさと不必要な例外の増殖 3 / 24
例外処理コードの本質的な難しさ 例外処理が難しい理由 テストの困難さ 通常のコードの流れを中断する 例外が発生した時に処理を続行するか、 中止するか、どちらにしても複雑 中止する場合、部分的に変更された状態 を巻き戻す必要がある 例外処理コード自体がさらなる二次的な 例外を生む
I/Oエラーなど一部の例外はテスト環境で再現しにくい 例外処理コードが実行される頻度は非常に低い バグが長期間検出されないまま残る 4 / 24
例外処理の冗長さ:Javaの例 実際の例外処理を考慮しなくても、try-catchのボイラープレートだけで通常の操作コードより行数が多い 本質的な処理はわずか3行。残りはすべて例外処理のための定型コード 5 / 24
例外が多すぎる 不必要な例外の増殖 「検出するエラーが多いほど良い」 → 過剰に防御的なスタイル きれいに処理する方法を見つける代わりに、 例外をスローして問題を呼び出し元に丸投げ 呼び出し元も何をすべきかわからない可能性 が高い 著者自身の失敗:Tclのunsetコマンド
unset(変数の削除)で、変数が存在しない場合にエ ラーをスロー → クリーンアップで存在しない変数も削除したいケー スが頻発 → 開発者はcatch文で囲んでエラーを無視するコード を書く羽目に 例外はインターフェースの一部。不必要な例外はクラスを浅くする 6 / 24
例外の複雑さを減らすには 例外処理による複雑さの損害を減らす最善の方法は 例外を処理しなければならない箇所の数を減らすこと 7 / 24
技法1:エラーを定義から消し去る 処理すべき例外がないようにAPIを定義する 8 / 24
エラーを定義から消し去る 例外処理の複雑さを排除する最善の方法は、処理すべき例外がないようにAPIを定義すること 変更前:unset = 変数を削除する xが存在しない → エラー! 変数が存在しない場合、仕事を遂行 できない
例外を生成する「意味がある」よう に見える 変更後:unset = 変数が存在しないことを保証する xが存在しない → 作業は完了済み。OK 存在しない変数でunsetを呼ぶのは問題ない 報告すべきエラーケースが存在しない 操作のセマンティクス(意味・定義)をわずかに変更するだけで 例外条件を消し去ることができる 9 / 24
例:WindowsとUnixのファイル削除 Windows:使用中のファイルは削 除不可 プロセスが開いているファイルは削 除できない → ユーザーがプロセスを探して強 制終了 → 最悪の場合、システムを再起動
Unix:削除を遅延させてエラーを消す ファイルが開かれている状態で削除 → ファイルに削除マークを付け、削除操作は成功を返す → 既存プロセスは引き続き読み書き可能 → すべてのプロセスがファイルを閉じたらデータを解放 Unixは2種類の例外を定義から消し去った 「削除操作のエラー」と「使用中ファイルの削除による既存プロセスへの例外」 10 / 24
例:Javaのsubstringメソッド 現在のAPI 改善案 範囲外のインデックスに対処するため に、呼び出し元で5〜10行のチェック コードが必要 インデックスが負でも、beginIndex > endIndexでも動 作が明確に定義される
Pythonのリストスライスは実際にこのアプローチ バグを減らす最善の方法はソフトウェアをシンプルにすること 11 / 24
リストAPIと404エラー あるAPIの設計で起きたこと 実際の実装 要素が0件 → 404 Not Found を返す フロント側で404をキャッチして空リスト
表示に変換するコードが必要に あるべき設計 要素が0件 → 200 OK + `[]` を返す フロント側は配列の長さだけ見ればよい エラー処理が不要に 「該当データがない」はエラーではなく空集合という正当な結果 数学的にも自然な定義 12 / 24
技法2:例外のマスキング 例外的な条件を低いレベルで処理し、上位に見せない 13 / 24
例外のマスキング 例外的な条件をシステムの低いレベルで検出・処理し、上位レベルに見せない TCPのパケットロス処理 パケットがドロップ TCP層が検出・再送 クライアントはドロップを認識しない クライアントはパケットロスを意識する 必要がない NFSのサーバー障害処理 NFSサーバーがダウン
クライアントがリクエストを再発行 サーバー復帰後にシームレスに再開 アプリケーションはエラー処理コードが一切不要 マスキングはインターフェースを縮小し、機能を追加する → より深いクラスをもたらす 14 / 24
キュー処理とマスキング AIサービスでのファイル処理フロー システム側の責任 処理の流れ ユーザーがファイルをアップロード キューにジョブをディスパッチ 202 Accepted を返す ここでユーザーの手を離れる
システム側で処理を完遂 API呼び出し失敗 → リトライ 一時的な障害 → 待って再実行 ユーザーには処理完了だけを通知 やってはいけないこと 「APIエラーが発生しました」をユーザーに返す → ユーザーにはどうしようもできない TCPがパケットロスをマスキングするように キューワーカーが一時的なエラーをマスキングする 15 / 24
技法3:例外の集約 多くの例外を1箇所でまとめて処理する 16 / 24
Webサーバーの例 悪い例:個別にキャッチ 良い例:最上位で集約 大量のハンドラがすべて同じことを行う パラメータ欠落、構文エラー、権限不足… すべて単一のハンドラで処理可能 エラーメッセージはスロー側で生成 新しい機能が追加されても同じ方法で例外をスローすれば 既存のシステムに組み込める 17
/ 24
入口での集約(Zod + OpenAPI) スキーマ定義でバリデーションを一元化 スキーマなし: 各ハンドラ内で`if (!id) return 400`のような 個別チェックが散在
スキーマあり: バリデーションはスキーマ定義に一元化 ハンドラはビジネスロジックに集中 18 / 24
フロントエンドでの集約(ErrorBoundary) TanStack Query + ErrorBoundary 個別にエラーハンドリング ErrorBoundaryで集約 各コンポーネントはデータ取得に集中 エラー表示は1箇所で統一 バックエンドのonError、フロントエンドのErrorBoundary
レイヤーを問わず集約の原則は同じ 19 / 24 各コンポーネントでエラー表示コードが散在
技法4:クラッシュさせるだけ 回復が困難で頻度の低いエラーはアプリケーションを中止する 20 / 24
クラッシュさせるだけ 処理しようとする価値のない例外には、診断情報を出力してアプリケーションを中止する メモリ不足エラーの例 クラッシュが適切なケース メモリ不足 開いているファイルのI/Oエラー 内部の不整合データ構造の検出 ネットワークソケットが開けない メモリが枯渇したらアプリケーションにできる ことはほぼない
チェック漏れでnullポインタ参照するより明確に クラッシュさせた方がよい クラッシュが不適切なケース ユーザー入力のバリデーションエラー 回復がシステムの価値の一部である場合 21 / 24
集約 × クラッシュの合わせ技 HonoのonErrorハンドラ 集約の側面 あらゆる想定外エラーを1箇所でキャッチ → 各ハンドラに個別のtry-catchが不要 クラッシュの側面 回復を試みずそのリクエストを諦める
→ 500を返して終了 「どこで処理するか」は集約、「どう処理するか」はクラッシュ 技法は組み合わせて使える 22 / 24
やりすぎに注意 学生チームの失敗 ネットワーク通信モジュールですべての ネットワーク例外をマスキング エラーが発生しても、キャッチして破棄し 何事もないかのように続行 アプリケーションがメッセージの消失や サーバー障害を知る手段がない 堅牢なアプリケーションの構築が不可能に 判断の基準
例外情報がモジュール外で必要ない場合 → 定義から消すか、マスキングしてOK 例外情報がモジュール外で必要な場合 → インターフェースに複雑さを追加しても公開すべき 23 / 24
まとめ 本章から 4つの技法 例外処理はソフトウェアにおける 複雑さの最悪の源泉の一つ 例外処理コードは本質的に書くのが難しく テストも困難 不必要な例外の定義が問題をさらに 悪化させる 例外を処理しなければならない箇所の数を
減らすことが鍵 エラーを定義から消し去る APIの意味・定義を見直すのが最善 例外のマスキング 低レベルで処理して上位に見せない 例外の集約 複数の例外を1箇所でまとめて処理 クラッシュさせるだけ 回復不要なエラーは対応を中止する 例外を見つけたとき「どう処理するか」の前に 「この例外は本当に必要か」と問う 24 / 24