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
17
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
エラーを定義から消し去る
shinaps
April 01, 2026
More Decks by shinaps
See All by shinaps
動くコードでは不十分
shinaps
0
30
サラリーマンの小遣いで作るtoCサービス - Cloudflare Workersでスケールする開発戦略
shinaps
2
610
react-callを使ってダイヤログをいろんなとこで再利用しよう!
shinaps
2
740
CloudflareとHonoを使って飲食店のレビューができるLINEアプリを作った
shinaps
3
1.5k
Featured
See All Featured
Faster Mobile Websites
deanohume
310
32k
Taking LLMs out of the black box: A practical guide to human-in-the-loop distillation
inesmontani
PRO
3
2.3k
Exploring the Power of Turbo Streams & Action Cable | RailsConf2023
kevinliebholz
37
6.5k
How To Stay Up To Date on Web Technology
chriscoyier
790
250k
RailsConf & Balkan Ruby 2019: The Past, Present, and Future of Rails at GitHub
eileencodes
141
35k
Claude Code のすすめ
schroneko
67
230k
Optimizing for Happiness
mojombo
378
71k
Building Applications with DynamoDB
mza
96
7.2k
Stewardship and Sustainability of Urban and Community Forests
pwiseman
0
450
From π to Pie charts
rasagy
0
260
Save Time (by Creating Custom Rails Generators)
garrettdimon
PRO
32
4.3k
B2B Lead Gen: Tactics, Traps & Triumph
marketingsoph
0
200
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