Curated Tech Reading Map

次に読むべき技術書が見つかるサイト

ブログ記事

変更が怖いコードと変更を歓迎するコードの違い:仙場大也『良いコード/悪いコードで学ぶ設計入門』

著者: DevBookPath 編集部公開日: 更新日:

コードを追加するたびにどこかが壊れる。レビューで「なぜこう書いたのか」を説明できない。そういった状況は、個別の技術不足というより、設計の基礎知識が体系化されていないことから生じることが多い。

仙場大也(ミノ駆動)の『良いコード/悪いコードで学ぶ設計入門』は、「悪いコードがなぜ問題を起こすのか」を具体的な症状から出発し、それを解消する設計の手法へとつなげる構成をとっている。Java でサンプルコードが書かれているが、考え方自体は言語に依存しない。

1. データクラスが引き起こす「低凝集」問題

バグが繰り返し発生する箇所の多くは、データを持つクラスとそのデータを使うロジックが別々の場所に散らばっている。この状態を「低凝集」と呼ぶ。

金額を int で持つクラスがあるとき、その金額に対する計算ロジックは利用側のあちこちに書かれることになる。似た計算が複数の場所に存在し、仕様変更が発生すると修正漏れが起きる。

本書が提示する対策は「値オブジェクト+完全コンストラクタ」だ。

  • 値オブジェクト: 金額を単なる int ではなく Money クラスとして扱い、計算ロジックをそのクラス内に持たせる
  • 完全コンストラクタ: インスタンス化の時点で全フィールドを初期化し、不正な値をガード節で弾く

この2つを組み合わせることで、クラス自身がデータの整合性を守る「自己防衛」の構造が生まれる。外部から不正な値をセットされる経路がなくなり、不変(イミュータブル)にすることで状態変化のトレースが容易になる。

flowchart LR
    subgraph NG["❌ 低凝集(データクラス)"]
        DC["Order クラス\nint price のみ保持"] --> L1["計算ロジック(散在)\nOrderService\nDiscountService\nTaxService"]
    end
    subgraph OK["✅ 高凝集(値オブジェクト)"]
        VO["Money クラス\nint amount を保持\n+ add() + applyTax() を内包\n+ コンストラクタでガード"]
    end
    style NG fill:#fce8e8,stroke:#e53935
    style OK fill:#e8f8e8,stroke:#4caf50

2. 条件分岐のネストを解消する2つのアプローチ

if の中に if が入り込む多重ネストは、コードの見通しを悪化させる代表的なパターンだ。どの条件がどこまで影響するのかを追いかけるだけで時間が奪われる。

早期 return: 条件を反転させて、処理対象でないケースを冒頭で除外する。本来やりたい処理が関数の中心に来て、読み流せる構造になる。

ストラテジーパターン: 同じ条件軸(例: 魔法の種類)の switch 文が複数箇所に散らばっている場合、条件ごとにクラスを作り interface で切り替える構造に変えると、新しい種類を追加しても既存コードを変更せずに済む。

ポリシーパターン: 「購入金額が10万円以上 かつ 購入頻度が月10回以上 かつ 返品率が0.1%以下」といった複合条件は、各ルールを個別のクラスとして定義し、「ポリシー」として束ねることで if 文の肥大化を防げる。

これら3つのアプローチに共通するのは「条件分岐を分散させず、一カ所に集約する」という考え方だ。

3. クラスの設計が読みやすさを決める

第10章以降で扱われる「名前設計」と「コメント設計」は、コーディングの習慣として定着させやすい知見がまとまっている。

  • 技術的な仕組みで命名する(MemoryStateManager)のではなく、ビジネス上の意図で命名する(ShoppingCart
  • コメントはコードが「何をしているか」ではなく「なぜそうしているか」を書く。前者はコード自体が語るべきだからだ

命名はリファクタリングの単位でもある。名前がうまく付けられないクラスは、複数の責務を抱えているシグナルとして読める。

4. リファクタリングを安全に進める手順

テストのないコードを変更するのは怖い——この恐怖を乗り越えるために本書が示す手順がある。

仕様化テスト: まず現在のコードに対してテストを書き、「実際に何が返るか」を記録する。振る舞いを固定してからコードを変える。

試行リファクタリング: 本番コードへの取り込みを前提にせず、構造を理解するために「お試し」でリファクタリングしてみる。ゴールが見えたら破棄し、テストを先に書いてから正式に作業する。

2つ帽子の原則も重要だ。「機能追加中」と「リファクタリング中」を同時にやらない。コミットも分ける。バグが発生したとき原因を探しやすくするためだ。

こんな人に向いている

「コードレビューで指摘を受けるが、なぜその書き方がよくないか言語化できない」「良いコードと悪いコードの違いが感覚では分かるが、人に説明できない」というエンジニアに特に向いている。経験3〜7年程度で、コードの品質を自分の言葉で言語化したいタイミングに最もフィットする。

一方で、プログラミングの文法をこれから学ぶ段階の人には早い。サンプルは Java だが、クラスやメソッドを読める前提で設計の話が進むためだ。また本書は第17章で自らを「設計の初歩から中級へのハシゴ」と位置づけている。すでにアーキテクチャ層やドメイン駆動設計の粒度で日々悩んでいる人には、扱う対象が関数・クラス単位に寄っている分、物足りなさが残る。その場合は本書を土台の再確認として使い、より上位の設計書へ進む前提で読むのが合っている。

どこから読み、どこを繰り返すか

全17章あるが、頭から順に通読しなくてよい。第1章で「悪しき構造をどう知覚するか」という視点を得たら、第4〜9章(不変の活用・低凝集・条件分岐・コレクション・密結合)は症状別のカタログとして扱える。手元のコードで困っている症状から逆引きする読み方が効率的だ。

明日のコーディングにそのまま持ち込めるのは第10〜12章の名前設計・コメント・メソッドで、習慣として定着させやすいため先に読む価値が高い。第15〜17章は設計を組織やプロセスに根づかせる話で、チームで品質を上げる立場になったときに効いてくる。まず症状カタログと名前設計を押さえ、残りは必要になった章を引く——それがこの厚みの本と付き合う現実的な距離感だ。

読んだあとに手を動かす一歩

本書は第17章で「インプット2・アウトプット8」を勧めている。読み流さず、次のどれか1つを実際に試すのが定着への近道だ。

  • 触っているコードから「悪しき構造」を1つ名指しする。技術駆動命名でも、多重ネストでも、データと計算が離れた低凝集でもいい。まず知覚できることが出発点だ。
  • 影響範囲の小さい private メソッドを練習台にして、早期 return か値オブジェクト化を1つだけ適用し、機能追加とは分けてコミットする(2つ帽子の原則)。
  • 全コードを直そうとしない。本書は費用対効果の観点から、変更が頻繁で価値の高い「コアドメイン」に設計投資を絞れと説く。自分のプロダクトのコアがどこかを言語化するところから始めると、労力が空回りしない。

筆者の体験から

コードレビューを担当するようになって数年、指摘の理由づけをその都度組み立て直している自覚があった。データを保持するだけのクラスに計算処理が分散する、条件分岐が幾重にも重なる——そうした症状への指摘の粒度をチーム内で揃えられずにいたのが本書を手に取った動機だ。

読んだ後はガード節をコンストラクタに寄せる書き方がチームの型として根づき、レビューの場で「低凝集」「密結合」という語彙を共有できるようになった。もっとも、共通化そのものが密結合を招くという指摘には最初納得しきれず、それまで疑いなく使ってきた共通関数を洗い直す作業が発生した。共通化と分離の線引きは結局チームで議論を重ねるほかなく、読書だけで答えが出る話ではなかった。

記憶に残るのは、税込計算のロジックが数か所に微妙に異なる形で埋め込まれ、値引き仕様の変更に伴う修正漏れから金額がずれた不具合だ。計算を1つのクラスに閉じ込める提案には当初懐疑的な声もあったが、次の改修で修正が単一箇所で済んだことで、以後はレビューで同様の構造を勧める側に回っている。


本記事のリンクには Amazon アソシエイト等の広告が含まれる場合があります。リンク経由の購入で運営者に紹介料が支払われることがあります。

この記事を共有

この地図を共有