複数エラーを統合する errors.Join() の設計判断レビュー

Go 1.20から導入された errors.Join() は、複数のエラーを一つにまとめて返却する新しいエラーハンドリング構文である。
便利である反面、レビュー時には設計意図の曖昧さや責務の混在が発生しやすいため、利用箇所を慎重に評価する必要がある。

1. errors.Join()とは何か?

複数の error 値をひとつにまとめて返却できる新関数。
エラーごとの型判定・errors.Is() / errors.As() によるチェックにも対応している。

Joinの基本構文
return errors.Join(err1, err2, err3)
Comment
@Reviewer: `err1`〜`err3` がそれぞれ独立した意味を持つ場合、それらをまとめて返すことで呼び出し元での判断を容易にする狙い。
ただし、呼び出し側が正しく各エラーを扱える設計になっているかを併せて確認すべき。

2. 使用例とレビュー対象コード

Join使用例
func saveAndLog(path string, logMsg string) error {
    var errs []error
    if err := saveToFile(path); err != nil {
        errs = append(errs, fmt.Errorf("save error: %w", err))
    }
    if err := writeLog(logMsg); err != nil {
        errs = append(errs, fmt.Errorf("log error: %w", err))
    }
    return errors.Join(errs...)
}
Comment
@Reviewer: `saveToFile`と`writeLog`は処理目的が異なるため、エラー統合の目的は「すべての処理結果を報告する」ことにあると推察される。  
呼び出し元が `errors.Is(err, os.ErrPermission)` などで判定しやすい設計になっているかを確認。

3. エラー統合による曖昧化リスク

統合されたエラーに対して err.Error() をそのまま表示する設計では、以下のように複雑な出力になる。

エラー出力の複雑化例
save error: permission denied; log error: disk full
曖昧なメッセージの問題点
  • どの処理が失敗したか不明瞭になりやすい
  • エラーの種類が混在し、責務の分離が不明確になる
  • 呼び出し側でのハンドリングが難しくなる

4. エラー統合の構造と意図

UML Diagram

5. errors.Is()errors.As()の挙動確認

統合されたエラーも、個別の型チェックには対応している。

if errors.Is(err, fs.ErrPermission) {
    // 一部エラーがアクセス拒否系
}
Comment
@Reviewer: `errors.Is()` を想定したエラーラップ構造(%w)で `Join` しているかを確認。
適切なラッピングをしないと意図通りに判定されない。

6. レビュー時の指摘観点

観点 内容
統合の意味 処理結果を合算すべき文脈か?
呼び出し側の設計 統合されたエラーを分解・判定できる設計か?
型ラップ fmt.Errorf("xxx: %w", err) を適切に使っているか?
メッセージ エラーメッセージが可読かつ責務に紐付いているか?
統合対象の粒度 同一責務内の複数エラーか、異なる責務か?
ロギング設計 統合されたエラーをログで正確に伝えられるか?

7. 過剰統合の問題例

過剰統合のアンチパターン
return errors.Join(validate(), save(), notify(), logAudit())
Comment
@Reviewer: この構造では各処理の責務が異なるため、 `errors.Join()` による統合が意味をなさず、  
呼び出し側での個別判断が非常に難しくなります。エラー型・ラップ・処理単位の再設計を検討。

8. まとめ:設計の明確さと利用意図の説明責任を評価する

errors.Join() は強力な表現だが、乱用はエラーのトレーサビリティと設計の明確性を損ねるリスクを伴う。

レビューアーは以下を確認する必要がある:

  • 統合の構造的な妥当性
  • 呼び出し元による判定可読性
  • 適切なラップ構造による型保持
  • 責務単位での構造設計

特に複数責務のエラーを一括返却するケースでは、統合ではなくラップ+構造体による明示的返却の選択も含めて検討させる視点が重要である。