Slide 1

Slide 1 text

「どう扱うか」で設計する エラーハンドリング berlysia / noren.ts #1

Slide 2

Slide 2 text

エラーハンドリング is 何

Slide 3

Slide 3 text

Result型を返したあと、どうするか ● 例外ではなく Result を返す設計が一般化しつつある ● Result を返したあとの判断や振る舞いの話をしたい ● 振る舞いの選択は、コードに設計として現れる ● →失敗をどう扱い、どう意味づけるか

Slide 4

Slide 4 text

「エラー」とは失敗の事実であり、その表現である ● 失敗そのものの情報 ○ Error オブジェクト、またはその派生オブジェクト ○ Error を継承した独自のオブジェクト ○ Error を継承しない独自のオブジェクト ■ DomainExceptionは何にせよ独自オブジェクトだろう ● 失敗している結果そのもの ○ -1, NaN, null, undefined などの文脈に依存した失敗の表現 ○ 例外的な状態を示すための特定の値や型 ● 伝達方法 ○ throw と try-catch ○ コールバック関数の引数に渡ってくる (err, data) => {} スタイル ○ Promise の reject と then/catch, Async Function の await と try-catch ○ return ■ Result型は主にこれ

Slide 5

Slide 5 text

失敗にもいろいろある 種類 例 特徴 ランタイム null / undefined参照 実装ミス、例外 ビジネスロジック 未ログイン、在庫切れ 想定される失敗 致命的 OOM、DB接続断 自己復旧困難 どの失敗も結局ビジネスロジック上の失敗に収斂する ビジネスロジック上の「意味ある失敗」を、どう扱うか

Slide 6

Slide 6 text

失敗したとき、どう扱いたいか ● ログを取る ● スキップして続行する ● ユーザーに知らせる ● 処理をリトライする ● フォールバックする ● 呼び出し元の処理全体も失敗させる ● 後始末する 失敗がこうした振る舞いに繋がるようにすることこそが設計 エラーハンドリングは、これを思って構成する

Slide 7

Slide 7 text

振る舞いを選ぶ判断はどこからくるか ● 仕様・ドキュメント ○ 要件としての明文化、設計意図 ● 型 ○ Result、enum、インターフェース ● コード ○ if, switch, try-catch、分岐ロジック ● ランタイム ○ ログ・モニタリング・ユーザーの操作結果 判断材料が振る舞いを選ぶ場所まで伝わる設計が望ましい TypeScriptを使うならば、型にうまいこと現れていると望ましい

Slide 8

Slide 8 text

インターフェースに扱い方が現れる ● 関数のシグネチャは、その関数の「前提」や「期待値」を示す ● Result を返すことで、「失敗するかも」が明示される ● → 設計の意図が、コードの表面に出ている状態 設計は「伝わる形」になって初めて力を持つ

Slide 9

Slide 9 text

Before / After(throw vs Result)

Slide 10

Slide 10 text

意味を刻んだラベルとしての「エラー」 ● Result型がもたらす情報 ○ 失敗している可能性があること ○ 起こりうる失敗にどんな意味があるか ■ "NotFound", "OutOfStock" などの分類で、意図を明示 ● ラベルがあることで、呼び出し側が振る舞いを判断できる ○ 適切に呼び出し元に対して失敗の扱いを委ねる、責任を移譲する Result型を使っただけで全てが解決したりはしない 呼び出し側の判断に役に立つ、失敗の設計が求められる

Slide 11

Slide 11 text

意味を型で定義する:ロジック実装 ● 起こりうる失敗にどんな意味があるか ○ Discriminated Unionによる型レベルでの区別 ○ instanceofとクラスの継承関係による型レベルでの区別、グルーピング ■ それぞれ、付随する情報を持ちうる ■ 📝使う道具は別になんでもいい! ● 型により、パターン網羅・振る舞いの分岐が明確になる ● テストも、意味単位で確認できる ○ コード例がほしいね! ごめん! ロジック実装では、意味を定義する側として、失敗を雄弁に表現したい

Slide 12

Slide 12 text

意味を振る舞いに変える:プレゼンテーション実装 ● 設計された意味(=ラベル)を受け取り、判断材料とする ● UIや通知など、ユーザーへの伝達に使うための整理・抽象化が行われる ○ ユーザーに「見せる必要がない」もの ○ ユーザーに「見せてはいけない」もの ○ 適切な提示への変換 ■ ……利用者の行動が変化する「何か」に、その知識を変換したい ● すべての失敗を厳密に分類しないが、必要な意味は拾いたい ● 「判断できるだけの情報」を受け取れる設計が望ましい 表現に使うには、意味が“選べる”形で届く必要がある プレゼンテーション実装では、意味を受け取って、どう振る舞うかを選びたい

Slide 13

Slide 13 text

意味が振る舞いに変わる

Slide 14

Slide 14 text

Result型の周りにあること 失敗をどう分類し、どう定義して、意味をあらわすか 失敗のどの意味を使って、どう振る舞うか ロジック実装 プレゼンテーション実装 値と失敗を媒介する道具 Result

Slide 15

Slide 15 text

Result型の周りにあること 失敗をどう分類し、どう定義して、意味をあらわすか 失敗のどの意味を使って、どう振る舞うか ロジック実装 プレゼンテーション実装 やりたいのは、その処理をしたかった誰かの、次の行動を起こすこと 値と失敗を媒介する道具 Result

Slide 16

Slide 16 text

おまけ:おすすめResultライブラリ - https://github.com/option-t/option-t - Tree Shakingしやすい! いらないものが生えてない! - 制御構文に無駄に割り込んでこない!

Slide 17

Slide 17 text

誰 ● berlysia ○ Web engineer (mainly frontend) ○ 妄想を現実にすることをしている ● 株式会社ドワンゴ 教育事業 ○ Webフロントをやる人 ○ Webフロントのためにいろいろやる人 ● TSKaigiの中の人 ○ TypeScriptのカンファレンス TSKaigi 2025 ■ 5月23日(金)、24日(土)開催 ○ diniiさんスポンサードありがとうございます! ○ 今日はネタ被らせないよう大変でした