- 「後戻りできない」の正体は、構造の蓄積である
- 最初に置いた判断が、全体の前提になる
- 暗黙の前提は、静かに複製される
- 設計を変えたいと思ったとき、すでに遅い
- 境界を後から引き直すのが難しい理由
- 「設計はあとからでも直せる」は、なぜ魅力的なのか
- 技術的負債の正体は「判断の負債」である
- 小さな設計は、後戻りできる
- 良い設計は「決断を早くする」
- 設計とは、未来の選択肢を整理すること
- この章のまとめ
設計の話をしていると、 よくこんな言葉を耳にします。
とりあえず作って、あとで直せばいい 実装しながら設計を詰めればいい
一見すると、合理的に聞こえます。
しかし現実には、 設計は、後から直そうとするほど難しくなっていく という現象が、ほぼ必ず起こります。
この章では、その理由を 精神論ではなく 構造の問題 として説明します。
「後戻りできない」の正体は、構造の蓄積である
設計が後戻りできなくなる理由は、単純です。
判断が、コード全体に染み出していくから です。
判断を構造として表現せずに実装を進めると、 その判断は次のような形で現れ始めます。
- if 文として
- 呼び出し順の前提として
- コメントの注意書きとして
- テストの前提条件として
つまり、 ひとつの判断が あらゆる場所に複製 されていきます。
この状態になると、 もはや一箇所を直すだけでは済みません。
最初に置いた判断が、全体の前提になる
初期段階では、設計を深く考えずに とりあえず次のようなコードを書いてしまうことがあります。
fun submit(data: Data) {
if (data.isValid()) {
// 処理
}
}
このコードは、一見すると何の問題もありません。 しかし、この一行には、すでに 重要な設計判断 が含まれています。
この時点で置かれている判断は、次の二つです。
Dataは「不正な状態」で渡される可能性がある- その妥当性を判断する責任は
submitが持つ
つまりこのコードは、
「呼び出し元は、Data の正しさを気にしなくてよい」
という前提を、 コードの構造として表現している のです。
暗黙の前提は、静かに複製される
この前提のまま実装が進むと、 同じ考え方が別の場所にも持ち込まれます。
fun submitDraft(data: Data) {
if (data.isValid()) {
// 別の処理
}
}
あるいは、
if (data.isValid()) {
submit(data)
}
といったコードが現れ始めます。
ここで起きているのは、
- 妥当性チェックが複数箇所に現れる
- 「どこで判断するのか」が揺れ始める
- 判断の責任が、構造として整理されなくなる
という状態です。
この段階では、 まだ「少し重複している」程度にしか見えません。
設計を変えたいと思ったとき、すでに遅い
しばらく実装が進んだ後で、 次のように考え直すことがあります。
実は、Data は生成時点で必ず valid にしたい submit には、正しい Data しか渡らない設計にしたい
これは、より良い設計判断 です。
しかし、この判断を構造として表現し直そうとすると、 すぐに問題にぶつかります。
- すでに
submitの中でチェックしている - 呼び出し側にもチェックが存在する
- テストが「invalid な Data を渡す前提」で書かれている
つまり、
「Data は invalid かもしれない」 という判断が、 コード全体の前提として表現されてしまっているのです。
この状態では、
- if を消すだけでは済まない
- 前提を洗い出す必要がある
- 影響範囲が読めない
という状況になります。
これが、
設計は後から直せない
と感じる正体です。
境界を後から引き直すのが難しい理由
境界は、
- 依存関係
- 呼び出し順
- 型
- ライフサイクル
と深く結びついています。
一度、判断を曖昧なままにして実装を進めると、
- あちこちから直接触られ
- 暗黙の前提が増え
- 例外的な扱いが混ざり込む
結果として、 境界を引き直すだけで大量の修正が必要 になります。
これは、 境界が単なる「線」ではなく、 構造全体の形そのもの だからです。
「設計はあとからでも直せる」は、なぜ魅力的なのか
この考え方が魅力的に見えるのは、
- 早く動くものが見える
- 考える時間を減らせる
- 手戻りしている感覚がない
からです。
しかし実際には、
- 判断を表現しない
- 境界を定めない
- 構造を意識しない
という 設計上の借金 を、 静かに積み上げています。
技術的負債の正体は「判断の負債」である
技術的負債という言葉は、 しばしば曖昧に使われます。
本書では、次のように捉えます。
技術的負債とは、 構造で表現すべき判断を、 人と時間に押し付けた結果 です。
- 「ここは気をつけて」
- 「この順番だけ守って」
- 「この条件は暗黙で」
これらが増えるほど、 設計を修正するコストは指数関数的に増えていきます。
小さな設計は、後戻りできる
ここで重要な補足があります。
すべての設計が、 後戻りできないわけではありません。
- 小さな関数
- 局所的なクラス
- 閉じたモジュール
これらは、 判断の影響範囲が小さい ため、 後から直すことができます。
問題になるのは、
- 境界
- 依存関係の向き
- 状態の持ち方
といった 構造の根幹 です。
良い設計は「決断を早くする」
設計とは、 可能性を狭める行為です。
- できることを減らし
- 書けるコードを制限し
- 判断を構造として表現する
これは一見、 不自由に見えるかもしれません。
しかし、この制限こそが、
- 後から迷わない
- 変更時に悩まない
- 壊れにくい
という自由を生みます。
設計とは、未来の選択肢を整理すること
設計は、 未来を縛る行為ではありません。
未来の選択肢を、 安全な形に整理する行為 です。
- 危険な選択肢を消し
- 正しい変更だけを残す
そのために、 判断を構造として表現します。
この章のまとめ
- 設計が後戻りできなくなるのは、判断が広がるから
- 判断は、構造で表現しないと複製される
- 境界や依存関係は、後から直しにくい
- 技術的負債の正体は「判断の負債」
- 良い設計は、決断を早くする
次の章では、 ここまでの話を踏まえて、
「では、設計はいつやるべきなのか」 ――タイミングの話に進みます。