最初の一手は、「判断の置き場所」に打つ


既存のモノリスを前にして、
次に必ず出てくる問いがあります。

「で、どこから直せばいいんですか。」

この問いに対して、
多くの現場ではこう答えがちです。

  • 影響範囲が小さいところから
  • テストがあるところから
  • 簡単そうなところから

これらは、
間違いではありません。
しかし、
十分でもありません。


なぜ「小さいところから」は失敗しやすいのか

影響範囲の小さい修正は、
安全に見えます。

しかし、
それは多くの場合、 判断に触れていない修正 です。

  • ロジックを少し整理した
  • クラスを分割した
  • 命名を直した

コードはきれいになります。
ですが、
設計はほとんど変わりません。

なぜなら、
判断がそのままだからです。


最初の一手が変えるべきもの

最初の一手で変えるべきなのは、
構造ではなく、

判断です。

もっと正確に言うと、
「この判断は、ここに置く」という
配置の仕方です。

つまり、
判断の所在地を動かすこと
効果的な最初の一手になります。


「守られている判断」を一つ選ぶ

第5章で見つけた、
判断の軸を思い出してください。

その中から、
次の条件を満たすものを
一つだけ選びます。

  • 長く守られてきた
  • 壊れると致命的
  • 多くの場所から参照されている

これは、
今のモノリスの中心判断 です。

最初の一手は、
必ずここに打ちます。


切り出すのは「処理」ではない

ここで、
よくある誤解があります。

それは、
「ロジックを別クラスに切り出すことが設計だ」
と思ってしまうことです。

たとえば、
次のような if 文を見たとします。

if (user.isPremium && order.total >= 1000 && !order.isCampaign) {
    applySpecialDiscount(order)
}

条件が長く、
意味も一目では分かりません。

そのため、
多くの現場では次のような改善が行われます。

if (canApplySpecialDiscount(user, order)) {
    applySpecialDiscount(order)
}

一見すると、
読みやすくなりました。

しかし、
設計としては、ほとんど前進していません。


判断は、まだ if 文の中にある

この時点でも、
次の問いには答えられていません。

  • なぜプレミアムユーザーなのか。
  • なぜ 1000 円以上なのか。
  • なぜキャンペーン中は除外されるのか。

つまり、
「いつ特別扱いしてよいのか」という判断は、
依然としてコードの外にあります。

関数名が付いただけで、
判断そのものは、
その場しのぎで書かれたままです。


切り出すべきなのは「条件」ではない

ここで切り出すべきなのは、
条件式でも、
処理の塊でもありません。

切り出すべきなのは、
その分岐が存在する理由 です。

つまり、
「どんな状態のときに、特別割引が許されるのか」
という判断そのものです。


改善例:判断に名前と置き場所を与える

この判断を、
明示的な存在として表現すると、
次のようになります。

class SpecialDiscountPolicy {

    fun isEligible(user: User, order: Order): Boolean {
        return user.isPremium &&
               order.total >= MINIMUM_AMOUNT &&
               !order.isCampaign
    }
}

重要なのは、
このクラスが「割引ロジック」を表しているわけではない、
という点です。

これは、
「特別割引が許される条件とは何か」
という判断を表現したものです。


何が変わったのか

この形にすると、
設計上の意味がはっきり変わります。

「判断の置き場所が 偶然 決まっているか、
それとも 意図して固定 されているか」

が変わります。

この変更により、

  • 判断が一箇所に集まる
  • 変更理由を説明できる
  • 条件の妥当性を議論できる

状態になります。


canApplySpecialDiscount の場合に起きていること

if (canApplySpecialDiscount(user, order)) {
    applySpecialDiscount(order)
}

この形では、

  • 判断は「ある」
  • しかし どこに属する判断なのかが決まっていない

という状態です。

canApplySpecialDiscount は、

  • util 関数かもしれない
  • ViewModel に置かれているかもしれない
  • 別のファイルに散らばるかもしれない

つまり、

「とりあえず今ここにある判断」
でしかありません。

この判断は、

  • 他でも使われるかもしれない
  • 別の文脈で書き直されるかもしれない
  • 微妙に条件がズレた別バージョンが生まれるかもしれない

という、 漂流状態 にあります。


isEligible の場合に起きていること

class SpecialDiscountPolicy {

    fun isEligible(user: User, order: Order): Boolean {
        ...
    }
}

こちらでは、

  • 判断が SpecialDiscountPolicy という概念に所属している
  • 「この判断はここにある」と明示されている

という状態です。

これは単なるリファクタリングではなく、

「この判断は、特別割引というルールの一部だ」 という設計上の宣言 です。

その結果、

  • 似た判断はここに集まる
  • 変更理由をここに集約できる
  • 仕様の会話がコードと一致する

ようになります。


「置き場所が決まる」と何が変わるのか

重要なのは、
置き場所が決まると 責任が決まる という点です。

  • canApplySpecialDiscount → 「誰の判断か分からない」
  • isEligible → 「特別割引 ( SpecialDiscountPolicy ) の判断」

この差は、

  • テストを書くとき
  • 仕様変更が入ったとき
  • 説明責任を果たすとき

に、はっきり効いてきます。


もう一つ大事な違い

もう一つだけ補足します。

canApplySpecialDiscount は、

「今、この条件を通していいか」

という 局所的な判断 です。

一方、isEligible は、

「特別割引とは、どういう状態か」

という ドメイン定義上の判断 です。

実行可否概念の定義
ここが、決定的に違います。


設計とは、if を減らすことではない

if 文が悪いわけではありません。

問題なのは、
本来は言葉で説明されるべき判断を、
if 文の中に閉じ込めてしまうことです。

設計とは、
構文を整理することではありません。

判断が、
どこに置かれているのかを
自分の言葉で説明できる状態にすることです。


成功する最初の一手の形

成功する最初の一手には、
共通した特徴があります。

  • 呼び出し側が楽になる
  • 責務の説明が一文で言える

そして何より、
「ああ、そういう判断だったのか」
と周囲が納得する。

この納得感が、
次の一手を呼び込みます。


最初の一手は、小さくていい

ここまで読むと、
大きな変更が必要だと
感じるかもしれません。

ですが、
最初の一手は小さくて構いません。

重要なのは、
変更量ではなく、
方向性 です。

判断の置き場所が、
一つでも明確になれば、
設計は動き始めます。


この章のまとめ

最初の一手は、
安全な場所に打つものではありません。

意味のある場所に打つものです。

構造を直す前に、
判断を動かす。

その一手が、
モノリスを
「理解可能な塊」へと
変えていきます。

次の章では、
この一手を
どうやって連鎖させるのか。

つまり、
改善を止めないための考え方を
扱います。