すべてのソフトウェア開発で通用する、高度な設計スキルを Android アプリ開発を通して解説します。
自分の書いたプログラムに「何か違和感がある」と感じることが多い人は、ぜひ読んでみてください。その違和感の正体を言語化し、設計の視点として整理していきます。
単にベストプラクティスを踏襲する設計者ではなく、状況に応じて、自分でベストな設計を考えられるようになります。その結果、コードの構造は自然と整理されていきます。
人に依存した設計ではなく、構造に依存した設計にすることで、壊したくても壊せない「堅牢なソフトウェア」に導く本質を解説しています。
本書の主張は、次の一文に集約されます。
良い設計とは、壊そうと思っても壊せない構造を作ることである。
ここで言う「壊れない」とは、 バグが一切出ないという意味ではありません。 気合や注意力、経験年数に依存しないコードである、という意味です。
良い設計がされたコードは、 間違った使い方をしようとすると、
そもそもコンパイルできない。 あるいは、実行する前の段階で違和感として現れる。 これは、 構造そのものが人を正しい方向に導くから成り立つものです。
多くの現場では、 「速く書けること」が価値として評価されがちです。 短期間で動くものを作れること自体は、確かに意味があります。
ただし本書では、 速さそのものを価値だとは定義しません。
なぜなら、 速く書かれたコードは、 将来の時間を消費することが多いからです。
一方で、良い設計は、
読めば意図が伝わる構造 変更点が局所に閉じる構成 間違ったコードを書けない制約 引き継ぎ時の説明コストの低下 未来の時間を買う といった性質を持ちます。
これらは、 設計という構造の力によって生まれます。
本書では、シニアエンジニアが 「センス」や「経験則」として扱ってきた設計を、 明確に言語化された「文章」として記し、 設計するための思考の土台 としての役割を果たします。
例えば、設計の土台には次のようなものがあります。
判断をどこで完了させるか 状態に嘘をつかせないこと 責務の境界を明確にすること 変更理由が一つになるように構造を作ること 設計とは、 未来の自分や他人の行動を、構造によって制限すること だと考えています。
ドキュメントをどれだけ丁寧に整備しても、 時間が経てば読まれなくなります。 コードとの乖離が起き、 存在していないのと同じ状態になることもあります。
一方で、良い設計は、 ドキュメントを必要としません。
コードそのものがルールであり、 コンパイラがそのルールを強制する からです。
本書では、 この「コンパイラに仕事をさせる設計」を重視します。
設計がきれいなコードは、属人化しません。
特定の人がいなければ回らない状態は、 人の問題ではなく、設計の問題です。
後から参加した人が、 コードを読み、構造を理解し、 安心して変更できる。
その状態を作ることが、 設計に責任を持つということ だと考えています。
本書は、 特定の言語やフレームワークを解説する本ではありません。
Android、iOS、サーバーサイドなど、 分野が違っても共通する 設計の本質 を言語化することを目的としています。
早く書くための本ではなく、 壊れない構造を作るための本です。
なぜ「速さ」は評価されやすいのか 「まず動くものを作る」は間違いなのか 速く書かれたコードが抱える問題 速さが生む「負債」は、あとからやってくる 設計は「速さ」と対立するものではない この章のまとめ 多くの現場では、 「速く書けるエンジニア」が高く評価されます。
短期間で動くものを作れる。 仕様変更にもすぐに対応できる。 多少バグが残っていても、とりあえず形にできる。
こうした姿は、 一見するととても頼もしく見えます。
実際、 プロジェクトの初期段階や、 期限が迫った場面では、 速さが価値を持つこともあります。
ただし本書では、 速さが絶対的な価値であるとは考えていません。
なぜ「速さ」は評価されやすいのか 速さが評価される理由は、 とてもシンプルです。
速さは、目に見えるからです。
何日で終わったか 何機能実装できたか どれだけ早くリリースできたか これらは数値として観測できます。
一方で、 設計の良し悪しは、 すぐには見えません。
設計の差が表に出るのは、
仕様変更が入ったとき メンバーが入れ替わったとき 数か月、数年が経過したとき です。つまり、
速さは「今すぐ評価できる価値」 設計は「後から効いてくる価値」 だと言えます。
「まず動くものを作る」は間違いなのか ここで、一つはっきりさせておきたいことがあります。
本書は、 「まず動くものを作る」 こと自体を 否定しているわけではありません。
要件が曖昧な段階で、 いきなり完成形の設計を書くことは、 シニアエンジニアであっても難しいと思います。
まず動くものを作ることで、
他の実装と比較できる 問題の輪郭が見える 本当に難しい部分が浮かび上がる ということは、確かにあります。
この段階のコードは、 思考のラフスケッチ です。
問題は、そのラフスケッチを 完成した設計 だと誤認してしまうことにあります。
速く書かれたコードが抱える問題 速く書かれたコードは、 多くの場合、次のような特徴を持ちます。
一ヶ所修正すると、影響が爆発的に発生する 一つの部品が複数の責任を負っている 影響確認が人の注意力に委ねられている 変更時に、どこを直せばよいかわからない これらは、 「書いた人が賢いから私にはわからない」 「書いた人がわかるなら私にもわかるはず」 という問題ではありません。
暗黙の前提が、コードの外にある可能性があります。
...
本書における「設計」の定義 用語についての注意 設計の本質は「責任の分割」 責任を分けただけでは、設計は完成しない 構造による保証とは何か 設計とアーキテクチャの違い 設計の8割は、構造で決まる 良い設計は、壊そうと思っても壊せない 良い設計は、変更の影響が爆発しない 良い設計は、人間の思考コストを下げる 良い設計は、属人化しない 設計に正解はないが、説明は必要 この章のまとめ 多くのエンジニアは、 「設計」という言葉を使いながら、 実は同じものを指していません。
ある人は、 クラス図やレイヤー構成のことを指します。
ある人は、 アーキテクチャやフレームワーク選定を指します。
またある人は、 実装前に少し考えること自体を、 設計と呼んでいるかもしれません。
本書では、 まずこの曖昧さを取り除きます。
本書における「設計」の定義 本書では、設計を次のように定義します。
設計とは、
責任を分割し、その正しさを構造によって保証すること です。
これを、式で表すとこうなります。
責任の分割 + 構造による保証 = 設計
この式が、 本書全体を通しての軸になります。
用語についての注意 本書では、 構造による保証 を表現する際に、 文脈に応じて、
制約 制限 保証 強制 誘導 といった言葉を使います。
しかし、指している本質は一つです。
それは、
やってはいけないことを、人が判断するのではなく、 構造によって、そもそもできなくする
という考え方です。 (これらの言葉は、あくまで、文脈によって使い分けるだけです。)
設計の本質は「責任の分割」 まず、設計の目的から見ていきます。
設計の本質は、責任の分割 です。
ここで言う責任とは、 わかりやすく言えば、
クラスや関数が持つ役割
のことです。
何を知っているのか 何を知らなくてよいのか 何の役割を担うのか これらを明確に分けることが、 設計の出発点です。
責任が曖昧なまま集まっている構造は、 どれだけ読みやすく書かれていても、 壊れやすい設計になります。
...
判断とは何か 判断が構造として表現されていない場合、何が起こるか 「レビューで防ぐ」は、設計ではない 良い設計は、判断を構造で表現する 判断が構造で表現されると、何が変わるか 判断が構造で表現されていないコードの特徴 判断が構造で表現されると、コードは強くなる この章のまとめ 設計が壊れ始めるとき、 コードの中では、 ある現象が必ず起きています。
それは、
判断が、コードのあちこちに散らばっている
という状態です。
判断とは何か 本書で言う「判断」とは、 if 文や条件分岐そのもののことではありません。
これは今やってよいのか この順番で呼んでよいのか この値を渡してよいのか この状態で変更してよいのか といった、 「やってよい/いけない」を決める責任 そのものを指します。
この判断が、 どこで行われているか。
それが、 設計の壊れやすさを決めます。
判断が構造として表現されていない場合、何が起こるか 判断が構造として表現されていない場合、 それは次の場所に押し出されていきます。
呼び出し元のコード コメントやドキュメント コードレビュー テスト そして最終的には、人の記憶 この状態では、
正しく使えるかどうかは人次第 知っている人がいないと触れない うっかりミスが即バグになる という構造になります。
これは、 判断がコードの外に漏れ出している 状態です。
「レビューで防ぐ」は、設計ではない よくある会話があります。
これはレビューで気づけますよね テストを書けば大丈夫ですよね
一見、正しそうに聞こえます。
しかしこれは、 設計でやるべき判断を、 後工程に押し出している だけです。
書いてはいけないコードが書けてしまう 実行してはいけない状態が表現できてしまう 呼んではいけない順番で呼べてしまう この時点で、 構造はすでに負けています。
レビューやテストは、 設計の代わりにはなりません。
良い設計は、判断を構造で表現する 良い設計では、 判断は 構造の中 にあります。
正しい順番でしか呼べない 不正な状態を表現できない 間違った依存関係を書けない こうした制約は、
...
状態とは、「今、何が許されているか」である 判断は、必ず状態に依存する 状態が曖昧だと、判断が漏れ出す 良い状態設計は、「ありえない状況」を消す 状態を構造で表現する、ということ 状態を構造で表現できていないコードの兆候 状態設計は、設計の中核である この章のまとめ 第3章では、 判断を人に持たせるのではなく、 構造で表現する ことが設計だと述べました。
では、その判断は、 構造の中で どんな形 を取るのでしょうか。
その答えが、 状態 です。
状態とは、「今、何が許されているか」である 本書で言う状態とは、
変数の値 フラグの on / off データの有無 といった、 表面的な話ではありません。
状態とは、
「今、このコードは何をしてよくて、何をしてはいけないか」 を表すものです。
たとえば、
今はまだ送信してはいけない もう変更してはいけない ここから先は読み取り専用である この操作は一度しか許されない こうした判断が、 暗黙の了解ではなく、 コードとして表現されているかどうか
それが、 状態設計の良し悪しを分けます。
判断は、必ず状態に依存する 第3章で扱った判断は、 必ず何かに依存していました。
この順番で呼んでよいのか 今、この処理をしてよいのか この値を変更してよいのか これらはすべて、
「今がどんな状態か」 によって決まります。
つまり、
判断を構造で表現する 状態を構造で表現する この二つは、 実は 同じ話を違う角度から見ている だけです。
状態が曖昧だと、判断が漏れ出す 状態がはっきり定義されていないコードでは、 判断が漏れ出します。
null かもしれない 呼ばれる順番は暗黙 途中の状態が存在するかどうかわからない このときコードは、
if を増やして守る コメントで注意書きをする テストで網を張る という方向に進みます。
...
振る舞いは「判断の結果」であり、書くものではない 振る舞いは「考える」ものではない 良い設計では、振る舞いは「選ばれている」 振る舞いを制御するのは「構造」 「書けない振る舞い」を作る 振る舞いが増えたら、設計を疑う この章のまとめ 振る舞いは「判断の結果」であり、書くものではない ここまでで、 判断は 人がその場で行うもの ではなく、 構造として先に固定されるべきもの だ、という話をしてきました。
では、構造に固定された判断は、 最終的にどこへ行き着くのでしょうか。
それが 振る舞い(behavior) です。
振る舞いは「考える」ものではない 設計が弱いコードでは、 振る舞いは次のように書かれます。
if / when で分岐する 状態を見て都度判断する 条件が増えるたびに分岐が増える このとき、プログラムは常に 「今、どう動くべきか?」 を 実行時に考え続けている 状態になります。
これは一見、自然に見えます。 しかし設計として見ると、これは負けています。
なぜなら、
振る舞いの正しさが 毎回の判断に依存している からです。
良い設計では、振る舞いは「選ばれている」 設計されたコードでは、 振る舞いは 書かれるもの ではありません。
すでに選ばれている のです。
ある状態になった時点で どの振る舞いが可能かは決まっている それ以外の振る舞いは、そもそも存在しない つまり、
「どう動くか」を考える必要がない
状態が確定した瞬間に、 実行できる振る舞いも一意に確定する これが設計された状態です。
振る舞いを制御するのは「構造」 ここで重要なのは、 振る舞いを縛っているのが、
if 文 フラグ コメント 運用ルール ではない、という点です。
縛っているのは 構造 です。
型によって呼べる関数が決まる 状態オブジェクトによって持てる操作が決まる API によって渡せる情報が制限される このとき、 振る舞いは構造の結果として現れるだけ になります。
...
判断が散らばると、コードは壊れ始める 境界の役割は「判断を一箇所に押し込む」こと Android / Compose における「境界」 悪い例:UI が判断している 良い例:境界の内側で判断が完結している ViewModel は「判断を閉じ込める箱」 境界が曖昧だと、判断は漏れ出す この章のまとめ 設計において「境界」という言葉は、 しばしば次のように説明されます。
責務の境界 レイヤーの境界 モジュールの境界 しかし本書では、境界をもう一段深く捉えます。
境界とは、判断を閉じ込めるための構造 です。
判断が散らばると、コードは壊れ始める 設計されていないコードでは、 同じ判断がいろいろなところに散らばります。
例えば、ボタンの有効 / 無効を判断する処理が、 UI 内と ViewModel 内にそれぞれ存在するとします。
判断方法の仕様が変更になった際に、 UI 側の判断は仕様通りに変更したが、 ViewModel 側の変更を忘れた。
というような 構造的な破綻 が起き始めます。
境界の役割は「判断を一箇所に押し込む」こと 良い設計では、 判断は 境界の内側に閉じ込められます。
境界の外では、判断しない 境界の内側で、判断が完結する 境界を越えた時点で、前提が保証される つまり、
境界を越えたら、もう迷わない
という状態を作るのが、設計です。
Android / Compose における「境界」 Android / Jetpack Compose では、 典型的に次のような境界が現れます。
Composable と ViewModel の境界 ViewModel と UseCase の境界 UseCase と Repository の境界 重要なのは、 どこに判断を閉じ込めるかを意識しているか です。
...
状態とは何か 状態は、判断を繰り返させないための装置 状態が嘘をつくと、設計は壊れる 「ありえない状態」を表現できてはいけない 状態は、判断が終わった世界を表す この章のまとめ 設計の話をしていると、 「状態」という言葉が、 とても曖昧に使われがちです。
画面の表示状態 一時的な入力値 データベースの中身 これらはすべて「状態」と呼ばれます。
しかし本書では、 もう一段、踏み込んで定義します。
状態とは何か 本書における状態とは、
判断の結果を、構造として保持したもの
です。
言い換えると、
状態は、判断そのものではない 状態は、判断が終わった「あと」の姿 です。
「今、何ができるのか」 「次に何をしてよいのか」
それらはすべて、 状態が表現しています。
状態は、判断を繰り返させないための装置 判断が構造で表現されていない設計では、
毎回 if で確認する 毎回 null チェックをする 毎回条件を思い出す ということが起きます。
これは、 同じ判断を何度も繰り返している 状態です。
良い設計では、
一度判断したら その結果を状態として持ち 以降は状態を見るだけ になります。
状態とは、 判断を 保存するための構造 です。
状態が嘘をつくと、設計は壊れる 状態が正しく設計されていないと、 次のようなコードが生まれます。
本当は使えないのに、使えてしまう 本当は準備中なのに、完了に見える 条件次第で意味が変わるフラグ これはすべて、
状態が、判断結果を正しく表現できていない 状態です。
状態が嘘をつくと、 判断が再び人に戻ってきます。
「ありえない状態」を表現できてはいけない 良い状態設計では、
ありえない組み合わせ 矛盾した状態 中途半端な段階 を、そもそも表現できません。
たとえば、
Loading と Ready が同時に true 取得エラーなのにデータが存在する 未初期化なのに操作できる こうした状態を、 型や構造で排除します。
...
本書における「イベント」とは何か イベントは「判断の起点」ではない イベントは「判断が終わったこと」を表す なぜ、イベントが明確だと境界を越えたと分かるのか イベントは、境界を越えたことの通知である 判断が終わっていないものは、イベントではない この章のまとめ 本書における「イベント」とは何か 一般に、 イベントというと、
onClick onTextChanged onResume のような、 UI やフレームワークが発火する通知 を思い浮かべるかもしれません。
しかし、 本書で扱う「イベント」は、 それらとは少し意味が違います。
本書におけるイベントとは、
判断が完了したことを、 構造として外部に表現するもの
です。
イベントは「判断の起点」ではない よくある誤解として、
イベントが来たから判断する イベントを受けて状態をチェックする という考え方があります。
しかしこの考え方では、 判断はイベントの 後工程 に残ります。
その結果、
if が増える 状態チェックが散らばる 「このイベントは安全か?」を毎回考える という構造になります。
これは、 イベントが判断を内包していない 状態です。
イベントは「判断が終わったこと」を表す 本書でのイベントは、 次の役割を持ちます。
判断がすでに完了している 前提条件はすべて満たされている 境界を越えてよい状態になった つまりイベントは、
「ここまでの判断はすべて終わりました」 という合図
です。
イベントは、 これ以上判断を進めるための材料ではありません。
判断を 終わらせた結果 です。
なぜ、イベントが明確だと境界を越えたと分かるのか イベントが明確であるとは、
判断済みの値しか持たない 未検証・未確定な情報を含まない そのイベントが存在するだけで前提が成立している という状態です。
このようなイベントは、
境界の内側でしか生成できず 境界の外側では、前提を疑う必要がありません そのため、
そのイベントを受け取った瞬間に、 「判断はすでに終わっている」と分かる
のです。
...
悪い例:境界を越えたか分からないイベント 何が問題なのか 良い例:境界を越えたことが分かるイベント 状態(未判断の世界) イベント(判断完了の世界) ViewModel(判断を完了させる場所) ここで何が起きているか 境界の「手前」 境界の「向こう側」 イベントが「境界を越えた通知」になる理由 なぜ sealed class が効くのか この章のまとめ 第8章では、 本書における「イベント」を次のように定義しました。
イベントとは、「判断が完了したこと」を表す構造 です。
これは、
onClick onTextChanged onResume といった UI やフレームワークが発火する通知 とは、 まったく別の概念です。
ここでは、 この違いがコードとしてどう現れるのかを、 具体例で確認します。
悪い例:境界を越えたか分からないイベント まずは、よくある実装から見てみます。
class FormViewModel : ViewModel() { private var uiState = FormUiState() fun onTextChanged(text: String) { uiState = uiState.copy(inputText = text) } fun onSubmitClicked() { // 本当に送信してよい状態か? // ここで毎回判断する必要がある } } 一見、問題なさそうに見えます。
しかし、この onSubmit() には、 次の疑問が常につきまといます。
入力は本当に完了しているのか バリデーションは終わっているのか この時点で「判断」は終わっているのか コード上では、 どこで判断が完了したのかが分かりません。
...
状態・イベント・境界は「同じ話」をしている 判断とは何か —— 設計で最初に決めるべきもの 状態とは「判断が終わった世界のスナップショット」である イベントとは「判断が完了したこと」を表す構造である 境界とは「判断をこれ以上持ち越さない」ための線である Tips:Flow の各関数は「判断を閉じるための境界」である ViewModel は「判断済みの世界」を作る場所である 「状態・イベント・境界」が噛み合ったときに起きること この章のまとめ:名前は違うが、問いは一つである 状態・イベント・境界は「同じ話」をしている 状態設計、イベント設計、境界設計。 これらは、多くの場合、 別々の設計トピック として語られます。
しかし実装を重ねていくと、 こんな違和感を覚えたことはないでしょうか。
状態をきれいに設計したはずなのに、 if が消えない イベントを分けたはずなのに、どこで判断しているのか分からない 境界を引いたはずなのに、責務がにじみ出てくる それは、あなたの設計力が足りないからではありません。
実はこれらはすべて、 同じ問いを、違う名前で考えているだけ なのです。
本書で扱ってきた
状態 イベント 境界 は、どれも本質的には、
「判断をどこで終わらせるか」
という、ひとつの設計問題を別の角度から見たものです。
この章では、それらを一度すべて並べ、 「同じ話をしている」ことを明確にします。
そうすることで、 これまでバラバラに見えていた設計判断が、 一本の軸としてつながるはずです。
判断とは何か —— 設計で最初に決めるべきもの この章の軸になる定義
判断とは 分岐 検証 条件評価 正常/異常の決定 つまり 「複数の可能性の中から、1つに確定させる行為」 ここで強調するポイント:
判断は「いつまでも続いてはいけない」 判断が終わらない世界は 状態が不安定 テストできない 責務が混ざる 状態とは「判断が終わった世界のスナップショット」である 本書における 状態の再定義
状態 = 判断がすでに完了した結果を、嘘なく表現しているもの ここでこれまでの章と接続:
UI State に if が多い nullable が意味を持ちすぎている 「この状態でこの画面、あり得る?」という違和感 → それは 状態が、判断途中の世界を含んでしまっているから
...
状態設計のチェック イベント設計のチェック 境界設計のチェック ViewModel / UI 分離のチェック(Android) 統合チェック(最重要) 状態設計のチェック この状態は 判断がすでに完了した結果 だけを表しているか 「この状態の UI はあり得る?」と自分に聞いて、即答できるか nullable や flag の組み合わせで「状態を表現していないか」 状態を見ただけで、 これ以上判断が不要だ と分かるか イベント設計のチェック このイベントは「これから判断してほしい」通知になっていないか イベントの型を見ただけで、何が確定したのか が分かるか Boolean や Int で「意味を持たせて」いないか sealed class にしたとき、アプリがとり得る状態として過不足がないか 境界設計のチェック この境界を越えたあと、判断が発生していないか 境界の向こう側で if / when が増えていないか Flow の各関数の前後が「意味の切れ目」になっているか 「ここから先は事実だけが流れる」と断言できるか ViewModel / UI 分離のチェック(Android) ViewModel は「判断済みの世界」だけを外に出しているか UI は判断せず、状態とイベントを そのまま描画/反映 しているか UI の onClick などから、判断済みのイベントを作成しているか 「この判断、UI でも ViewModel でもできるよね」と思う箇所が残っていないか 統合チェック(最重要) 「これは判断前/判断後のどちらか?」と即答できるか 新しいコードを書く前に「判断はどこで終わらせる?」と自問しているか → これが自然にできていれば、設計は崩れにくい
「後戻りできない」の正体は、構造の蓄積である 最初に置いた判断が、全体の前提になる 暗黙の前提は、静かに複製される 設計を変えたいと思ったとき、すでに遅い 境界を後から引き直すのが難しい理由 「設計はあとからでも直せる」は、なぜ魅力的なのか 技術的負債の正体は「判断の負債」である 小さな設計は、後戻りできる 良い設計は「決断を早くする」 設計とは、未来の選択肢を整理すること この章のまとめ 設計の話をしていると、 よくこんな言葉を耳にします。
とりあえず作って、あとで直せばいい 実装しながら設計を詰めればいい
一見すると、合理的に聞こえます。
しかし現実には、 設計は、後から直そうとするほど難しくなっていく という現象が、ほぼ必ず起こります。
この章では、その理由を 精神論ではなく 構造の問題 として説明します。
「後戻りできない」の正体は、構造の蓄積である 設計が後戻りできなくなる理由は、単純です。
判断が、コード全体に染み出していくから です。
判断を構造として表現せずに実装を進めると、 その判断は次のような形で現れ始めます。
if 文として 呼び出し順の前提として コメントの注意書きとして テストの前提条件として つまり、 ひとつの判断が あらゆる場所に複製 されていきます。
この状態になると、 もはや一箇所を直すだけでは済みません。
最初に置いた判断が、全体の前提になる 初期段階では、設計を深く考えずに とりあえず次のようなコードを書いてしまうことがあります。
fun submit(data: Data) { if (data.isValid()) { // 処理 } } このコードは、一見すると何の問題もありません。 しかし、この一行には、すでに 重要な設計判断 が含まれています。
この時点で置かれている判断は、次の二つです。
Data は「不正な状態」で渡される可能性がある その妥当性を判断する責任は submit が持つ つまりこのコードは、
「呼び出し元は、Data の正しさを気にしなくてよい」
という前提を、 コードの構造として表現している のです。
...
設計は「判断を構造に置く」タイミングでやる 実装前にやる設計、実装中にやる設計 実装前にやる設計 実装中にやる設計 「動くものを作ってから考える」の落とし穴 設計は「早すぎる」のではなく「深すぎる」と失敗する 良い設計は「観測してから行う」 設計をやるべきサイン 設計は、一度で終わらない 設計を「後回し」にしてよいケース 設計とは、タイミングの技術である この章のまとめ 設計について語ると、 必ず出てくる問いがあります。
設計は、いつやるべきですか?
この問いに対して、 よくある答えは曖昧です。
最初にやりすぎてもダメ 実装しながら考えるのが大事 状況による どれも間違ってはいません。 しかし、 判断基準が書かれていない のです。
本書では、 この問いに対して、 はっきりした答えを出します。
設計は「判断を構造に置く」タイミングでやる 設計をやるべきタイミングは、
判断を、人の頭から構造に移す必要が生じたとき
です。
つまり、
まだ判断が曖昧な段階 仕様が流動的な段階 では、無理に設計を固める必要はありません。
しかし、
その判断が繰り返し使われ始めたとき 複数人が触り始めたとき 変更が入り始めたとき その瞬間が、 設計のタイミング です。
実装前にやる設計、実装中にやる設計 設計には、大きく二種類あります。
実装前にやる設計 境界の仮置き 責任の大まかな分割 依存関係の向き これは、 「壊れ方を予測するための設計」 です。
正解を決めるためではありません。
実装中にやる設計 判断が散らばり始めたとき if が増え始めたとき 呼び出し順が暗黙になったとき このタイミングで行う設計は、
判断を構造として回収する行為 です。
これは、遅すぎると致命的になります。
「動くものを作ってから考える」の落とし穴 「まず動かす」は、非常に強力な考え方です。
しかし注意点があります。
動くものを作る過程で、
判断を仮置きしたまま 境界を曖昧にしたまま 型で表現しなかったまま 進んでしまうと、
その仮置きが、事実上の設計になる からです。
...
設計ができない人は、正しさを探す 設計ができる人は、壊れ方を見る 設計ができない人は、実装でカバーしようとする 設計ができる人は、判断を減らす 設計ができない人は、「あとで直す」と言う 設計ができる人は、「今は決めない」を決める 設計ができない人は、境界をまたぐ 設計ができる人は、境界で止まる 設計ができる人は、説明できる 設計ができない人は、結果で語る 設計ができる人は、未来の自分を見る 設計ができる人は、速さを急がない この章のまとめ 設計ができる人と、 できない人の違いは、 知識量ではありません。
経験年数でも、 使っているフレームワークでもありません。
違いが出るのは、 コードを書く前と後の「見え方」 です。
設計ができない人は、正しさを探す 設計がうまくいかないとき、 多くの場合、 こう考えています。
正しい書き方はどれか ベストプラクティスは何か サンプルはどうなっているか これは、 正解を探す思考 です。
しかし設計の世界では、 正解は一つではありません。
必要なのは、 正解探しではなく、 壊れ方を想像する力 です。
設計ができる人は、壊れ方を見る 設計ができる人は、 コードを見てこう考えます。
これは、どこから壊れるか 判断は、どこに漏れそうか 誰が、どこで迷うか つまり、
「失敗のルート」から設計を見る
のです。
これは、 悲観的 という意味ではありません。 現実的 という意味です。
設計ができない人は、実装でカバーしようとする 判断が曖昧なとき、 設計ができない人は、
if を足す チェックを増やす テストで守る という方向に進みがちです。
これは、 構造で解くべき問題を、実装で無理やり押し込んでいる 状態です。
一時的には動きますが、 壊れやすさは増えていきます。
設計ができる人は、判断を減らす 設計ができる人は、 逆の方向に進みます。
そもそも書けなくできないか 間違った状態を表現できないか 呼び出し順を型で縛れないか つまり、
...
速さは、目に見える 設計の価値は、時間差で現れる 人間は、未来の価値を割り引く 「まず動くものを作る」は、間違いではない 問題は、「速さで止まってしまう」こと 速さは、設計を先送りにする免罪符になる 本当に速い人は、止まれる人 設計は、速さを否定しない この章のまとめ 多くの現場で、 こんな評価がされがちです。
早く実装できる人は優秀 手が止まらない人は頼りになる とりあえず動くものを出せる人が強い 設計よりも、 速さが価値として見える瞬間 です。
なぜ、 こうなってしまうのでしょうか。
速さは、目に見える まず、 速さはとてもわかりやすい指標です。
何日でできたか どれくらいの量を書いたか すぐに画面が動いたか これらは、 誰の目にも見えます。
一方で、
判断が構造に閉じ込められているか 責任がきれいに分かれているか 将来の変更に耐えられるか といった設計の価値は、 その場では見えません。
設計の価値は、時間差で現れる 設計の良し悪しは、 あとから効いてきます。
仕様変更が入ったとき 人が入れ替わったとき 想定外の使われ方をしたとき その瞬間に初めて、
修正が一瞬で終わる 影響範囲が限定されている 原因がすぐに特定できる といった差が現れます。
つまり、 設計は未来のための仕事 です。
人間は、未来の価値を割り引く ここに、 人間の認知の癖があります。
人は、
今すぐ得られる成果 目の前の評価 今日怒られないこと を優先しがちです。
半年後の自分の苦労より、 今日の「進んでいる感」の方が、 強く感じられます。
その結果、 設計より速さを選んでしまう のです。
「まず動くものを作る」は、間違いではない ここで、 大事な補足があります。
「まず動くものを作る」 という考え方自体は、 間違いではありません。
むしろ、
要件が曖昧な段階 正解がまだ見えていない段階 問題空間を理解する段階 では、 動くものがないと、設計できない ことも多いです。
...
設計は、未来への投資 まず小さく止めて、判断を構造に埋め込む 状態・境界・判断を意識する 「速さ」と「設計」は両立できる この章のまとめ:設計との向き合い方 最後に 設計の本質は 責任の分割 であり、 それを 構造で保証する ことが重要です。
これまで述べてきた 判断を構造で表現する ことで、それが実現できます。
では、実際の開発現場で、 私たちは、どのように設計と向き合うべきでしょうか。
設計は、未来への投資 設計は、未来のための行為です。
今すぐ動かすことが最優先の時もある その場で作るコードに価値がある それでも、設計を全く無視すると、
判断が散らばり 責任が混ざり 境界が曖昧なコードが増え 結果として、未来の変更コストが爆発します。
設計とは、 将来の自分やチームを守る投資 だと理解しましょう。
まず小さく止めて、判断を構造に埋め込む 設計と向き合う第一歩は、
「まず止まること」 です。
この変更はどこに影響するか ここでの判断はどこに置くべきか 責任は分かれているか そして、それを コードの構造に落とし込む のです。
小さく止まることは、速さを失うことではありません。 むしろ、全体としての速さを保つための準備です。
状態・境界・判断を意識する 設計と向き合う際の基本は、次の3つです。
状態
状態を正確に表現する ありえない状態を作れないようにする 判断
「やってよい/いけない」を構造で表現する 型、API、オブジェクト生成条件、モジュール境界に集約する 境界
判断や責任を閉じ込め、影響範囲を限定する モジュール間、画面間、レイヤー間で明確にする この3つは、別々の話ではなく、同じ設計の本質を違う角度で見たものです。
「速さ」と「設計」は両立できる 速さを求めながらも設計を無視しない方法があります。
小さな単位で止まる 判断を構造に埋め込む 境界を明確にする 状態を正しく管理する このプロセスを繰り返すことで、コードは壊れにくく、かつ速く開発できるようになります。
この章のまとめ:設計との向き合い方 設計とは、単なる図や抽象化ではありません。
設計は、責任を分割し、構造に落とし込み、判断を閉じ込めること。
向き合い方のポイントは次の通りです。
止まる勇気を持つ
判断や責任が散らばる前に、構造に埋め込む 構造を信頼する
人に頼らず、構造が守る 未来の価値を意識する
今の速さよりも、将来の保守性と拡張性を優先 小さく繰り返す
...