TL;DR
- ガード節は「条件を満たさない場合に早期に抜ける(return/throw/continue)記述」で、ネストを浅くし幸せな経路(Happy Path)を左側にまっすぐ流せます
- 効果:可読性・保守性・差分の見やすさが上がり、バグ混入を減らします
- 注意:後処理(リソース解放/トランザクション)とエラーログの重複に気を付ける。言語ごとの「安全な抜け方」を使う(defer/with/using/try-with-resources 等)
1. ガード節とは?
「想定外の入力や前提違反を冒頭で弾くことで、以降の処理を前提が満たされた“幸せな経路”に限定する」テクニックです。
別名:早期リターン(early return)、fail-fast。
典型的には以下の3種類を"上から順に"並べます:
- 存在/Nullチェック
- 権限/状態チェック
- 入力妥当性チェック
満たさない場合は return / throw / continue / break などで早期に離脱します。
2. なぜ使うのか(メリット)
-
ネスト削減:
ifの入れ子地獄(「矢印コード」)を防ぎ、1〜2段の深さに保てる。 - 読みやすさ:否定条件を先に処理することで、本筋が左にまっすぐ読める。
- 差分が明瞭:PRレビューで本筋の変更が見やすく、レビュー効率とバグ検出率が上がる。
- 早期失敗:壊れた前提で先に進まず、バグの影響範囲を最小化できる。
3. Before / After(多言語サンプル)
JavaScript / TypeScript
Before(矢印コード):
function checkout(user?: User, cart?: Cart) {
if (user) {
if (user.active) {
if (cart && Array.isArray(cart.items) && cart.items.length > 0) {
// 本処理
} else {
throw new Error('Empty cart');
}
} else {
throw new Error('Inactive user');
}
} else {
throw new Error('Unauthenticated');
}
}
After(ガード節):
function checkout(user?: User, cart?: Cart) {
if (!user) throw new Error('Unauthenticated');
if (!user.active) throw new Error('Inactive user');
if (!cart || cart.items.length === 0) throw new Error('Empty cart');
// 本処理(Happy Path)
}
チーム規約例:ESLint の
no-else-returnとmax-depth(例: 2)を有効化
Python
def parse_age(payload: dict) -> int:
if "age" not in payload:
raise KeyError("age is required")
try:
age = int(payload["age"])
except (TypeError, ValueError):
raise ValueError("age must be an integer")
if age < 0:
raise ValueError("age must be non-negative")
return age
例外で早期離脱。リソース扱い時は
with文で後始末を自動化
Go
func Save(u *User, repo Repository) error {
if u == nil {
return errors.New("nil user")
}
if err := u.Validate(); err != nil {
return err
}
// Happy Path
if err := repo.Save(u); err != nil {
return err
}
return nil
}
エラーパスは上から落とす。後処理は
deferを活用。
Java
public Order place(String orderId, List<Item> items) {
Objects.requireNonNull(orderId, "orderId");
if (items == null || items.isEmpty()) {
throw new IllegalArgumentException("items must not be empty");
}
// Happy Path
return doPlace(orderId, items);
}
I/O は
try-with-resourcesでガード途中の return/throw でも確実にクローズ。
Kotlin
fun pay(user: User?, amount: Int) {
requireNotNull(user) { "user is required" }
require(amount > 0) { "amount must be positive" }
// Happy Path
}
Swift(言語構文としての guard)
func handle(user: User?) {
guard let user = user else {
logger.error("no user")
return
}
// Happy Path
}
Ruby
def ship(order)
raise ArgumentError, "order is required" unless order
return if order.canceled?
# Happy Path
end
RuboCop:
Style/GuardClauseをオンにするとレビューが楽になります。
C#(素朴な形 + ガード用ライブラリ)
public void Ship(Order order, int quantity) {
if (order is null) throw new ArgumentNullException(nameof(order));
if (quantity <= 0) throw new ArgumentOutOfRangeException(nameof(quantity));
// Happy Path
}
(オプション:Ardalis.GuardClauses を使う)
Guard.Against.Null(order);
Guard.Against.NegativeOrZero(quantity);
// Happy Path
4. どこで使う?
- 入力/前提の検証:必須フィールド、ID、権限、状態(active/inactive など)
-
ループ処理:不対象は
continueで先に弾く - 早期キャンセル:非同期/長時間処理は「キャンセル済みなら即 return」
- 境界/エッジ条件:空配列、上限超過、タイムアウト など
5. 実運用での注意点
-
後始末(リソース解放)
- 早期離脱でも確実に解放される構文を使う:
- Python
with - Java
try-with-resources - C#
using - Go
defer - Ruby
ensure - Swift
defer
- Python
- 早期離脱でも確実に解放される構文を使う:
-
エラーログの重複
- 「投げる側でログ、受ける側でもログ」の二重記録に注意。原則1箇所で記録
-
例外か戻り値か
- プログラミングエラー(契約違反) → 例外で fail-fast
-
ドメインとして想定済みの失敗 → 戻り値(
Result/Either/Option)やエラーコードで表現する設計も検討
-
ガードの乱立
- 似た条件をまとめる、メッセージを一貫させる、関数抽出で読みやすく
- 3〜5行を超えるロジックは専用のバリデータ/ポリシーに切り出す
6. チーム導入の小ワザ
-
Lint/Formatter
- JS/TS:
no-else-return,max-depth - Ruby:
Style/GuardClause - Go:
golangci-lintのnestifで入れ子検出
- JS/TS:
-
レビュー指針(Snippet)
- 「否定条件は先に返す」「Happy Path を左に寄せる」「深さは最大2」
-
テスト
- 前提違反ケースの網羅(Null・空・境界・権限)を先に書くと、ガード節の価値がすぐに見える
7. アンチパターン
- 深いネストを“気合い”で維持:保守性が落ちます。ガード節で平坦化
- 早期 return が散らばりリソース漏れ:上記の構文(with/using/defer 等)を徹底
- 例外投げすぎ:ドメイン上の通常失敗は戻り値/型で表現する設計も検討
8. 用語メモ(他言語の「ガード」)
-
Swift:
guardキーワードが言語機能 -
Haskell/Rust/Elixir など:パターンマッチのガード(
pattern if cond、when)という文脈でも登場
この記事の「ガード節」は一般的な早期離脱テクニックを指します
9. まとめ
- ガード節は前提違反を先に弾くことで、本筋をまっすぐ読めるようにするシンプルで強力な手法
- 導入は小さな関数から。否定条件を反転して先に返すだけで可読性が変わります
- 仕上げに、後処理の安全化とログ戦略の一貫性だけ忘れずに