悪しき構造の弊害
1-2

ネストした条件分岐の迷宮

「条件を満たすときだけ処理を進める」つもりで if を重ねると、本来やりたい処理がネストの最深部に沈む。どこからどこまでが各 if のブロックなのか目で追うのが難しくなり、条件を1つ足すたびにさらに深くなる。分岐構造を正確に理解しないまま仕様変更すれば、そのままバグになる。

01 確定申告の電子申告を送信できるかの判定

前提 確定申告データを電子送信できるかを判定し、可能なら送信処理を実行する

1
申告期間内であること?
self.is_within_filing_period()
No ↓
Yes →
2
全帳票の検算が済んでいること?
self.all_forms_validated
No ↓
Yes →
3
電子証明書が有効であること?
self.certificate.is_valid()
No ↓
Yes →
self.transmit() を実行する
送信せず処理を抜ける
Bad

送信できる条件を if で積み上げた結果、肝心の送信処理が3段ネストの奥に埋まっている

条件を重ねるほど深くなるネスト
def submit_tax_return(self) -> None:
    # 申告期間内か
    if self.is_within_filing_period():
        # 全帳票の検算が済んでいるか
        if self.all_forms_validated:
            # 電子証明書が有効か
            if self.certificate.is_valid():
                self.transmit()
Good

満たせない条件を先に返す。送信処理が左端に戻り、何が主処理なのかすぐ読める

早期 return で主処理を左端に戻す
def submit_tax_return(self) -> None:
    if not self.is_within_filing_period():
        return
    if not self.all_forms_validated:
        return
    if not self.certificate.is_valid():
        return

    self.transmit()

第1章ではまず「これが読みにくさとバグの温床になる」と知覚するところまで。条件を反転して先に抜ける早期 return による解消は、第6章のトピック1で扱う。

02 else がどの if と対なのか追えなくなる

前提 確定申告の現在の状況を文字列で返す(送信可能 / どの条件が欠けているか)

1
申告期間内であること?
self.is_within_filing_period()
No ↓
Yes →
2
全帳票の検算が済んでいること?
self.all_forms_validated
No ↓
Yes →
3
電子証明書が有効であること?
self.certificate.is_valid()
No ↓
Yes →
「送信可能」を返す
欠けている条件に応じた状況メッセージを返す
Bad

ネストが深まると if と else の対応を目視で照合する作業が発生する。行が離れるほど取り違えやすく、修正事故の原因になる

if と else の対応が遠い
def build_filing_status(self) -> str:
    if self.is_within_filing_period():
        if self.all_forms_validated:
            if self.certificate.is_valid():
                status = "送信可能"
            else:
                status = "電子証明書の更新が必要"
        else:
            status = "検算未了の帳票あり"
    else:
        status = "申告期間外"
    return status
Good

各条件で即座に結果を返す。else の対応関係を追わず、上から順に判定理由を読める

else を重ねず結果を返す
def build_filing_status(self) -> str:
    if not self.is_within_filing_period():
        return "申告期間外"
    if not self.all_forms_validated:
        return "検算未了の帳票あり"
    if not self.certificate.is_valid():
        return "電子証明書の更新が必要"

    return "送信可能"

各ブロックの中身が数十行に育つと、対応する else は画面の遥か下に行ってしまう。この構造のまま条件を追加し続けたコードは実在し、読む者の時間と気力を奪う。

もう一段抽象化: ネストで潜る vs ガードで戻す

Bad と Good は判定している条件自体は同じ。違うのは読み手の進路。ネスト if は条件を満たすたびに右へ潜り、主処理が奥へ沈む。早期 return は進めない条件をその場で出口へ流し、最後に残った本処理だけを左端に置く。

Bad: 条件を満たすたびに潜る本処理が最深部まで押し込まれる
if 申告期間内
if 全帳票検算済
if 電子証明書有効
本処理

読者は「今どの if の中か」を覚えながら右へ潜る。

Good: 進めない理由を出口へ流す残った本処理だけが左端に並ぶ
not 申告期間内return
not 全帳票検算済return
not 電子証明書有効return
残ったら本処理

読者は「ここで終わる理由」を上から消していくだけでよい。

定義として見ると: 同じ集合を別方向から指している

ネスト if

A ∩ B ∩ C

成功条件の交差を内側へ組み立てる。

早期 return

U \ (¬A ∪ ¬B ∪ ¬C)

失敗条件を外へ返し、残った集合を処理する。

Bad: ネストで潜るGood: ガードで戻す
書き方A → B → C と条件を満たすたびに内側へ進むnot A / not B / not C を先に返し、残りを処理する
読者の視線右へ右へとインデントを追う上から順に「ここで終わる理由」を読む
主処理の位置ネストの最深部に沈む左端、関数の末尾に並ぶ
条件追加さらに1段深くなる出口を1行足すだけ

この「先に終わる理由を並べ、残りを本処理にする」発想は、確定申告に限らず多くの場面で現れる。

  • 型の絞り込みif not isinstance(x, str): return # 違う型を切る → 残りは str
  • バリデーションif amount <= 0: raise ValueError # 不正値を弾く → 残りは安全な値
  • フィルタパイプラインvalid_items = [i for i in items if not i.is_invalid] # 不正を除外 → 残りを処理
  • 権限チェックif not user.is_admin: return 403 # 権限なしを切る → 残りは admin

ネストを読む作業は「どこまで潜ったか」を覚える作業になる。ガード節は「ここで終わる理由」を先に片づけるので、読み手は最後に残った本処理だけに集中できる。

参考: 『良いコード/悪いコードで学ぶ設計入門』(ミノ駆動 著、技術評論社)第1章。コード例は原則を自分の題材で表現し直したオリジナル。
1-2

ネストした条件分岐の迷宮

「条件を満たすときだけ処理を進める」つもりで if を重ねると、本来やりたい処理がネストの最深部に沈む。どこからどこまでが各 if のブロックなのか目で追うのが難しくなり、条件を1つ足すたびにさらに深くなる。分岐構造を正確に理解しないまま仕様変更すれば、そのままバグになる。

01 確定申告の電子申告を送信できるかの判定

前提 確定申告データを電子送信できるかを判定し、可能なら送信処理を実行する

1
申告期間内であること?
self.is_within_filing_period()
No ↓
Yes →
2
全帳票の検算が済んでいること?
self.all_forms_validated
No ↓
Yes →
3
電子証明書が有効であること?
self.certificate.is_valid()
No ↓
Yes →
self.transmit() を実行する
送信せず処理を抜ける
Bad

送信できる条件を if で積み上げた結果、肝心の送信処理が3段ネストの奥に埋まっている

条件を重ねるほど深くなるネスト
def submit_tax_return(self) -> None:
    # 申告期間内か
    if self.is_within_filing_period():
        # 全帳票の検算が済んでいるか
        if self.all_forms_validated:
            # 電子証明書が有効か
            if self.certificate.is_valid():
                self.transmit()
Good

満たせない条件を先に返す。送信処理が左端に戻り、何が主処理なのかすぐ読める

早期 return で主処理を左端に戻す
def submit_tax_return(self) -> None:
    if not self.is_within_filing_period():
        return
    if not self.all_forms_validated:
        return
    if not self.certificate.is_valid():
        return

    self.transmit()

第1章ではまず「これが読みにくさとバグの温床になる」と知覚するところまで。条件を反転して先に抜ける早期 return による解消は、第6章のトピック1で扱う。

02 else がどの if と対なのか追えなくなる

前提 確定申告の現在の状況を文字列で返す(送信可能 / どの条件が欠けているか)

1
申告期間内であること?
self.is_within_filing_period()
No ↓
Yes →
2
全帳票の検算が済んでいること?
self.all_forms_validated
No ↓
Yes →
3
電子証明書が有効であること?
self.certificate.is_valid()
No ↓
Yes →
「送信可能」を返す
欠けている条件に応じた状況メッセージを返す
Bad

ネストが深まると if と else の対応を目視で照合する作業が発生する。行が離れるほど取り違えやすく、修正事故の原因になる

if と else の対応が遠い
def build_filing_status(self) -> str:
    if self.is_within_filing_period():
        if self.all_forms_validated:
            if self.certificate.is_valid():
                status = "送信可能"
            else:
                status = "電子証明書の更新が必要"
        else:
            status = "検算未了の帳票あり"
    else:
        status = "申告期間外"
    return status
Good

各条件で即座に結果を返す。else の対応関係を追わず、上から順に判定理由を読める

else を重ねず結果を返す
def build_filing_status(self) -> str:
    if not self.is_within_filing_period():
        return "申告期間外"
    if not self.all_forms_validated:
        return "検算未了の帳票あり"
    if not self.certificate.is_valid():
        return "電子証明書の更新が必要"

    return "送信可能"

各ブロックの中身が数十行に育つと、対応する else は画面の遥か下に行ってしまう。この構造のまま条件を追加し続けたコードは実在し、読む者の時間と気力を奪う。

もう一段抽象化: ネストで潜る vs ガードで戻す

Bad と Good は判定している条件自体は同じ。違うのは読み手の進路。ネスト if は条件を満たすたびに右へ潜り、主処理が奥へ沈む。早期 return は進めない条件をその場で出口へ流し、最後に残った本処理だけを左端に置く。

Bad: 条件を満たすたびに潜る本処理が最深部まで押し込まれる
if 申告期間内
if 全帳票検算済
if 電子証明書有効
本処理

読者は「今どの if の中か」を覚えながら右へ潜る。

Good: 進めない理由を出口へ流す残った本処理だけが左端に並ぶ
not 申告期間内return
not 全帳票検算済return
not 電子証明書有効return
残ったら本処理

読者は「ここで終わる理由」を上から消していくだけでよい。

定義として見ると: 同じ集合を別方向から指している

ネスト if

A ∩ B ∩ C

成功条件の交差を内側へ組み立てる。

早期 return

U \ (¬A ∪ ¬B ∪ ¬C)

失敗条件を外へ返し、残った集合を処理する。

Bad: ネストで潜るGood: ガードで戻す
書き方A → B → C と条件を満たすたびに内側へ進むnot A / not B / not C を先に返し、残りを処理する
読者の視線右へ右へとインデントを追う上から順に「ここで終わる理由」を読む
主処理の位置ネストの最深部に沈む左端、関数の末尾に並ぶ
条件追加さらに1段深くなる出口を1行足すだけ

この「先に終わる理由を並べ、残りを本処理にする」発想は、確定申告に限らず多くの場面で現れる。

  • 型の絞り込みif not isinstance(x, str): return # 違う型を切る → 残りは str
  • バリデーションif amount <= 0: raise ValueError # 不正値を弾く → 残りは安全な値
  • フィルタパイプラインvalid_items = [i for i in items if not i.is_invalid] # 不正を除外 → 残りを処理
  • 権限チェックif not user.is_admin: return 403 # 権限なしを切る → 残りは admin

ネストを読む作業は「どこまで潜ったか」を覚える作業になる。ガード節は「ここで終わる理由」を先に片づけるので、読み手は最後に残った本処理だけに集中できる。

参考: 『良いコード/悪いコードで学ぶ設計入門』(ミノ駆動 著、技術評論社)第1章。コード例は原則を自分の題材で表現し直したオリジナル。