設計とは、きれいな構造や流行のパターンを選ぶことではない。本書が一貫して問い続けるのは、「どこに判断を閉じ込めるか」という視点だ。
要件は変わり、前提は崩れ、設計は必ず外れる。その現実を前提に、判断をコードに残し、強さを選び、チームで共有できる状態をつくる。
本書は、正解を当てる設計ではなく、選び直せる設計を目指すすべてのエンジニアに向けた、実践的な思考の記録である。
設計とは、きれいな構造や流行のパターンを選ぶことではない。本書が一貫して問い続けるのは、「どこに判断を閉じ込めるか」という視点だ。
要件は変わり、前提は崩れ、設計は必ず外れる。その現実を前提に、判断をコードに残し、強さを選び、チームで共有できる状態をつくる。
本書は、正解を当てる設計ではなく、選び直せる設計を目指すすべてのエンジニアに向けた、実践的な思考の記録である。
設計の話が、なぜ噛み合わないのか 「良い構造」を真似ても、うまくいかない理由 設計とは、何をしている行為なのか 判断は、コードのどこに現れるのか 判断が「にじみ出る」と何が起きるか 「閉じ込める」という発想 この本で扱わないこと まとめ 設計の話が、なぜ噛み合わないのか 設計の話をしているはずなのに、会話がどこか噛み合わない。 そんな経験はないだろうか。 「このクラス、責務が重くないですか?」 「ここ、MVVM 的におかしくない?」 「もう少し共通化できそうですよね」 言っていることは、どれもそれなりに正しい。 しかし次の瞬間、場の空気は微妙になる。 なぜそれが“悪い”のか、うまく説明できない 相手もなんとなく納得していない 結局「今回はこのままで…」となる 設計の議論が難しいのは、知識が足りないからではない。 多くの場合、話題にしているレイヤがズレているだけだ。 私たちは「構造」について話しているつもりで、 本当はそこに含まれている 判断 について語れていない。 「良い構造」を真似ても、うまくいかない理由 世の中には、良いとされる設計がたくさんある。 レイヤードアーキテクチャ、MVVM、Clean Architecture、DDD。 それらを学び、 「正しい形」に沿ってコードを書いたはずなのに、 しばらくするとシステムはまた苦しくなる。 変更のたびに広範囲を確認する必要がある 触ってはいけない雰囲気のコードが増える なぜこの構造なのか、誰も説明できない これは「構造の選択を間違えた」から起きているのではない。 問題はもっと単純で、 その構造が前提としている判断と、現実の判断が一致していなかっただけだ。 設計はレシピではない。 同じ構造でも、 どんな判断を閉じ込めるために使うかによって、結果はまったく変わる。 設計とは、何をしている行為なのか ここで、本書における設計の定義をはっきりさせておきたい。 設計とは、 不確実な判断を、どこに、どの強さで閉じ込めるかを決める行為である。 判断とは、次のようなものだ。 将来、変わるかもしれない決定 特定の文脈に強く依存した選択 あとから「間違っていた」と分かる可能性があるもの 設計が難しいのは、 これらの判断が 必ず間違える前提 で存在しているからだ。 設計の仕事は、 判断をなくすことではない。 判断が壊れたときに、 被害がどこまで広がるかを制御することにある。 判断は、コードのどこに現れるのか 「判断」と聞くと、 if 文や分岐ロジックを思い浮かべるかもしれない。 しかし実際には、 判断はコードのあらゆる場所に現れる。 クラスを分けたという事実 ファイルを分けたという事実 名前を付けたという事実 共通化した、しなかったという決定 たとえば、2 つのクラスを別々にした瞬間、 あなたはこう判断している。 ...
判断は「悪者」ではない 判断は、必ず寿命を持つ 判断が「広がる」ときに起きること 「変更が怖い」という感覚の正体 判断は、静かに増殖する 再利用が判断を壊す瞬間 判断が壊れると、構造も壊れる まとめ 第1章では、設計とは「判断」を扱う行為である、という前提を置いた。 では次に浮かぶ疑問は、こうだろう。 なぜ判断は、システムを壊すのか。 判断は必要なものだ。 それなのに、なぜ判断が増えるほど、 コードは触りづらくなっていくのか。 この章では、 判断そのものが悪いわけではない という事実から出発し、 それでもシステムが壊れていく理由を丁寧に掘り下げていく。 判断は「悪者」ではない まず、はっきりさせておきたい。 判断は、設計において避けるべきものではない。 「判断が多いコードは悪い」 「ロジックは少ないほうがよい」 こうした言葉は、しばしば聞かれるが、 それは半分しか正しくない。 判断がなければ、ソフトウェアは振る舞えない。 要件を満たすこともできない。 問題は、 判断が存在することではなく、 判断が制御されていないこと だ。 判断は、必ず寿命を持つ すべての判断には、寿命がある。 今は正しいが、将来は分からない この前提が崩れたら成立しない 別の文脈では通用しない それでも私たちは、 あたかもその判断が永遠に正しいかのように、 コードに埋め込んでしまう。 設計が壊れ始めるのは、 判断の寿命が尽きた瞬間だ。 しかし実際には、 その瞬間がはっきりと分かることは少ない。 少しずつ前提がズレ、 少しずつ例外が増え、 気づいたときには全体が歪んでいる。 判断が「広がる」ときに起きること 判断が一箇所に閉じ込められていれば、 その寿命が尽きても、修正は局所的で済む。 しかし判断が広がっていると、 話はまったく変わる。 同じ判断が、複数の場所に存在する 微妙に違う形でコピーされている どれが本体か分からない この状態では、 一つの判断を変えることが、 システム全体を揺るがす行為になる。 結果として、 誰も判断を変えられなくなる。 「変更が怖い」という感覚の正体 現場でよく聞く言葉がある。 「この辺、変更すると影響範囲が分からなくて…」 これは、技術力不足の告白ではない。 設計上の問題だ。 変更が怖いとは、 判断がどこにあるか分からない という状態を指している。 触る前に全体を読む必要がある テストがあっても安心できない レビューでも確信が持てない こうしてコードは、 徐々に「触れない領域」へと変わっていく。 ...
「閉じ込める」とは、どういうことか 強く閉じる、弱く閉じる 境界は、判断のために存在する 型は、判断を固定する道具である 抽象化は、判断を動かす余地を残す 共通化は「閉じ込め」ではない 判断を閉じ込められている状態とは まとめ 第2章では、 判断がどのようにシステムを壊していくのかを見てきた。 この章では、いよいよ本題に入る。 では、判断をどのように扱えばよいのか。 答えはシンプルだ。 判断を、閉じ込める。 ただしこれは、 単にロジックを隠すという話ではない。 設計として意味のある「閉じ込め方」には、 いくつかの技術と考え方がある。 「閉じ込める」とは、どういうことか 判断を閉じ込めるとは、 その判断に責任を持つ場所を決めることだ。 この判断は、どこで行うのか どこから触ってよいのか どこからは触ってはいけないのか これを曖昧にしたまま設計すると、 判断は必ず広がる。 逆に言えば、 閉じ込めるとは 判断の影響範囲を限定する行為 に他ならない。 強く閉じる、弱く閉じる すべての判断を、 同じ強さで閉じ込める必要はない。 判断には、それぞれ性質がある。 外れたら致命的な判断 外れてもすぐに直せる判断 プロダクト全体に影響する判断 画面単位で完結する判断 重要なのは、 判断の重さに応じて、閉じ込め方を変えること だ。 強く閉じるとは、 型や境界によって、 簡単には触れないようにすること。 弱く閉じるとは、 変更を前提に、 差し替えやすい形で置いておくこと。 設計とは、 この強弱を意識的に選び続ける行為だ。 境界は、判断のために存在する レイヤやモジュールの境界は、 整理のためにあるのではない。 そこには必ず、 「この判断は、ここまで」 という線が引かれている。 境界をまたいで判断が漏れ出すと、 設計は一気に脆くなる。 上位レイヤが下位の事情を知りすぎる 本来関係のない変更が波及する 境界を守る意味が失われる 境界とは、 判断を隔離するための装置 だと捉えたほうがよい。 型は、判断を固定する道具である 型は、 安全性のためだけに存在するのではない。 型は、 「この判断は、こういう形でしか存在しない」 と宣言するための道具でもある。 型を使うことで、 ...
この章で伝えたいこと 構造は「結果」であり「目的」ではない 判断が失われると、構造は硬直する 良い設計は「線を引く」行為である 判断をコードに残すという発想 まとめ この章で伝えたいこと 第2章・第3章では、構造を共通化しすぎた結果として起きる歪みや、モノリスが生まれる過程を見てきました。 本章では一段視点を上げ、「良い構造」を直接目指すのではなく、良い判断が積み重なった結果として構造が決まる という考え方を扱います。 設計が破綻する現場では、多くの場合「判断」がコードの外に散らばり、後から追えなくなっています。 その結果、なぜこの構造になっているのか、どこまで壊してよいのかが分からなくなります。 本章のゴールは、 構造よりも判断に目を向ける 判断をコードに閉じ込める 将来の変更に耐えられる思考の軸を持つ この3点を理解することです。 構造は「結果」であり「目的」ではない 設計の話になると、 レイヤードにするか モジュールをどう切るか どこを共通化するか といった 構造そのもの に議論が集中しがちです。 しかし実際には、構造はその時点での判断の積み重ねの結果にすぎません。 なぜこの責務をここに置いたのか なぜこのクラスに依存させたのか なぜこの抽象を導入したのか これらの判断が変われば、同じ要件でも構造はまったく別の形になります。 構造だけを真似しても、判断の前提が共有されていなければ、すぐに歪みが生まれます。 判断が失われると、構造は硬直する モノリス化したコードベースでよく見られるのが、 このクラスを触っていいのか分からない どこまで変更してよいのか判断できない とりあえず既存の流れに合わせる という状態です。 これは「構造が悪い」以前に、 なぜその構造になったのかという判断が失われている ことが原因です。 判断の背景が見えない構造は、 壊すのが怖い 変更が最小限になる 例外処理が積み重なる 結果として、さらに判断不能な構造へと進化していきます。 良い設計は「線を引く」行為である 設計とは、突き詰めると 線を引く行為 です。 どこからどこまでがこの責務か ここから先は別の判断に委ねる このユースケースでは扱わない 線を引くということは、同時に「やらないこと」を決めることでもあります。 線が引かれていない設計では、 責務が広がり続ける 境界を越えた依存が増える 修正の影響範囲が読めなくなる という問題が必ず起きます。 判断をコードに残すという発想 判断は、ドキュメントや頭の中に置いておくと必ず失われます。 そこで重要になるのが、 判断をコードの形で残す という考え方です。 型で制約する sealed class で分岐を限定する インターフェースで責務を分ける これらはすべて、 「こういう使われ方は想定していない」という判断をコードに刻む行為です。 ...
壊れやすさは、構造ではなく判断の偏りから生まれる 典型的な三つの偏り 「今きれいであること」を優先しすぎる 「将来どうせ変わる」を理由に判断を放棄する 「自分が分かっている」前提で判断を外に漏らす 健全な設計は、偏りを自覚して選び直すことから始まる 壊れやすさに気づける設計者になる ここまでの章で、設計とは「判断をどこに閉じ込めるか」であり、 そして判断は必ず変わる、という前提に立つ必要があることを見てきました。 では、次の問いに進みます。 なぜ、現場では壊れやすい設計が繰り返し生まれてしまうのでしょうか。 この章では、技術力不足や経験不足といった分かりやすい理由ではなく、 設計者が無意識に選んでしまう判断の置き方 に注目します。 これは誰かを責める章ではありません。 むしろ、自分自身が何度も陥ってきたパターンを言語化する章です。 壊れやすさは、構造ではなく判断の偏りから生まれる 壊れやすい設計を見ると、つい構造に目が向きがちです。 クラスが大きすぎる 責務が多すぎる 共通化しすぎている しかし、これらはすべて「結果」にすぎません。 その前段には必ず、 どの判断を重要だと見なしたか どの判断を軽視したか どの判断を将来に押し付けたか という偏りがあります。 壊れやすい設計とは、 一部の判断だけを過度に優先した設計 だと言い換えることができます。 典型的な三つの偏り 壊れやすい設計に共通する判断の偏りは、いくつかの型に分けられます。 ここでは特に遭遇頻度の高い三つを取り上げます。 「今きれいであること」を優先しすぎる 最もよくあるのが、この偏りです。 重複は悪だ 抽象化は善だ 共通化すれば美しい こうした価値観自体は間違っていません。 問題は、将来の判断の変化よりも、現在の見た目の整合性を優先してしまう ことです。 結果として、 まだ揃っていないものを無理に揃える 違いが見えていない段階で共通化する 変更理由の異なる処理を一箇所に集める といった構造が生まれます。 これは「きれいに設計したつもり」で壊れやすくする、典型例です。 「将来どうせ変わる」を理由に判断を放棄する 逆方向の偏りも存在します。 どうせ仕様は変わる 先のことは分からない 今はとりあえず動けばいい この考え方が行き過ぎると、 判断そのものを行わない設計 になります。 境界を引かない 責務を定義しない 型や制約を置かない 結果として、 変更が起きた瞬間に「全部を考え直す」必要が出てきます。 「将来に備えない」のではなく、 将来に備えるための最小限の判断すら閉じ込めていない 状態です。 「自分が分かっている」前提で判断を外に漏らす 三つ目は、設計者本人には気づきにくい偏りです。 これは普通こうだよね このクラスはそういう前提だから 使う側が気をつければいい こうした前提は、コードの外に存在します。 つまり、 ...
壊れない設計を目指すと、判断は過剰に固められる 壊れる設計と、壊してよい設計は違う 「直しやすさ」は、後から付け足せない 壊れやすい場所は、あらかじめ弱くしておく 判断の強度を揃えないという選択 設計は、未来の自分への引き継ぎである 壊すことを恐れなくなると、設計は楽になる ここまでの章では、 設計とは判断を閉じ込める行為であること 判断は必ず変わること 壊れやすい設計は、判断の偏りから生まれること を見てきました。 ここで、設計に対する前提を一段階更新します。 設計は、壊れないようにする行為ではありません。 壊れることを前提に、回収できる形にしておく行為です。 この章では、 「壊れる前提」を受け入れたときに初めて見えてくる設計の姿を掘り下げます。 壊れない設計を目指すと、判断は過剰に固められる 壊れない設計を目指すと、 設計者は無意識に、判断を強く閉じ込めようとします。 将来の変更を想定しすぎる 例外ケースをすべて先取りしようとする 型や抽象で厳重に縛る 一見すると堅牢そうに見えますが、 ここには大きな落とし穴があります。 それは、 想定が外れた瞬間に、全体が一気に硬直する ということです。 壊れないように固めた設計ほど、 壊れたときの修復コストは高くなります。 壊れる設計と、壊してよい設計は違う 「壊れる前提」と言うと、 雑に作ってよい、という意味に受け取られることがあります。 しかし、それは違います。 無秩序 無境界 無責任 な設計は、単に放棄です。 ここで言うのは、 壊れる場所が分かっている設計 です。 どこが変わりやすいか どこは変わりにくいか 壊れるとしたら、どこからか これが説明できる状態を作ることが目的です。 「直しやすさ」は、後から付け足せない よく、次のような言葉を耳にします。 まずは作って、あとで直せばいい 動くものを早く出そう これはスピードが求められる場面では正しい判断です。 ただし、 直しやすさを設計せずに作ったものは、後から直しにくい という事実もあります。 直しやすさとは、 境界があること 責務が分かれていること 判断が局所化されていること の積み重ねです。 直すという行為は、何かを後から付け加えることではありません。 すでに存在するコードを修正し、置き換え、削る行為です。 そのため、どんなに小さな変更であっても、既存の判断や構造に多かれ少なかれ影響を与えます。 壊れやすい場所は、あらかじめ弱くしておく 壊れやすい場所を、 あえて「弱く」設計するという考え方があります。 差し替えやすい 捨てやすい 影響範囲が小さい こうした性質を持たせることで、 壊れたときに迷わず手を入れられます。 これは、 ...
判断は「意図」ではなく「制約」として残す 名前は、最も軽い制約である 型は、判断を強制する 境界は、判断の置き場所を固定する 判断を残しすぎないという判断 制約の「強さ」を意識して設計する 設計とは、判断の履歴を残すこと まとめ ここまでの章で、設計とは判断を閉じ込める行為であり、 その判断は必ず変わり、壊れ、直されるものであることを見てきました。 では、次の問いに進みます。 その判断を、どこに、どうやって残すのか。 この章では、設計の議論をいよいよコードの中に降ろします。 抽象論ではなく、日々書いているコードの中で 判断をどう扱えばよいのかを整理していきます。 判断は「意図」ではなく「制約」として残す 設計の意図は、しばしばコメントやドキュメントに書かれます。 このクラスは◯◯のために存在する ここは将来変更される想定 これ自体は悪いことではありません。 しかし、意図だけが残り、制約が残らない設計には限界があります。 コードに残すべきなのは、 「こう使ってほしい」ではなく「こう使うことはできない」 という判断です。 制約として残された判断は、 誤った使い方を防ぐ 設計者の不在時にも効力を持つ 時間が経っても意味を失いにくい という性質を持ちます。 名前は、最も軽い制約である 判断を残す最初の手段は、名前です。 クラス名 関数名 変数名 名前は、 「これは何か」「これは何ではないか」 を同時に伝えます。 たとえば、 Manager Helper Util といった名前は、判断をほとんど閉じ込めていません。 逆に、 何の責務を持つのか どこまでを扱うのか が読み取れる名前は、 それだけで利用範囲を制限します。 名前は弱い制約ですが、 弱いからこそ、最初に置く価値があります。 型は、判断を強制する 名前だけでは防げない誤用は、必ず出てきます。 そこで次に使うのが、型です。 プリミティブ型をそのまま使わない 意味のある値オブジェクトを作る null を許可しない型にする 型は、 守らなければならない判断 をコードとして強制します。 型として残された判断は、 コンパイル時に検証される 利用側に説明を要求しない という強い効力を持ちます。 境界は、判断の置き場所を固定する 名前と型だけでは、 判断の影響範囲を限定しきれないことがあります。 そこで登場するのが、境界です。 モジュール レイヤ インターフェース 境界の役割は、 この判断は、ここに閉じ込める と明示することです。 ...
設計が属人化する本当の理由 レビューは、正解探しではなく判断の確認である 設計の会話は「構造」ではなく「理由」から始める 暗黙知を減らすための小さな工夫 判断が共有されると、責任も分散される チームで設計するとはどういうことか まとめ 前章では、判断をコードに残す方法として、 名前・型・境界という具体的な手段を見てきました。 しかし、実際の開発は一人では行われません。 判断は、必ずチームの中で扱われます。 この章では、 判断を属人化させず、チームで扱える状態にするにはどうすればよいか をテーマに掘り下げていきます。 設計が属人化する本当の理由 設計が属人化している現場では、よく次のような状態が見られます。 あの人しか触れないコードがある 変更前に必ず特定の人に確認が必要 レビューが形式的になっている これは単に「その人が優秀だから」ではありません。 多くの場合、 判断がコードの外に置かれている ことが原因です。 なぜそうなっているのか どこを壊してよいのか どこは触ってはいけないのか これらが共有されていなければ、 設計は必然的に人にひもづきます。 レビューは、正解探しではなく判断の確認である コードレビューというと、 書き方が正しいか パフォーマンスに問題がないか 規約に違反していないか といった点に目が向きがちです。 もちろん重要ですが、 それだけでは設計レビューにはなりません。 設計レビューで確認すべきなのは、 この変更で、どんな判断を置いたのか 既存の判断は変わっていないか 判断の置き場所は適切か という点です。 レビューとは、 判断の共有と再確認の場 です。 設計の会話は「構造」ではなく「理由」から始める 設計の相談や議論が噛み合わないとき、 話題がいきなり構造から始まっていることがあります。 このクラスを分けるべきか モジュールを切るべきか 共通化した方がよいか こうした問いは、 理由が共有されていない状態では平行線になります。 先に共有すべきなのは、 何が変わりやすいのか 何を守りたいのか 今回はどこを優先するのか という判断の前提です。 理由が共有されていれば、 構造の選択肢は自然に絞られます。 暗黙知を減らすための小さな工夫 判断をすべてドキュメント化する必要はありません。 しかし、 暗黙知のままにしないための小さな工夫 は有効です。 PR の説明に「今回置いた判断」を一行書く レビューコメントで「ここはあえて分けていない理由」を残す 名前を変えるときに、責務の境界が分かる名前にする これだけでも、 判断は少しずつコードの外に漏れにくくなります。 ...
設計者は、正解を当てる人ではない 設計者の責任は「選び直せる状態」を作ること 良い設計は、未来への配慮である 設計し続けるという責任 この本のまとめ この本では一貫して、 設計とは「判断をどこに閉じ込めるか」という行為である、という視点を扱ってきました。 判断は必ず変わること 壊れることを前提にすること 判断をコードに残すこと 判断をチームで共有すること ここまで読み進めてきた読者であれば、 設計という行為が、単なる構造設計やパターン選択ではないことは、 すでに感じ取っているはずです。 この終章では、 そうした設計観を踏まえたうえで、 設計者の責任とは何か を改めて言葉にします。 設計者は、正解を当てる人ではない 設計者という言葉から、 先を見通す人 最適解を選べる人 失敗しない構造を作る人 を想像するかもしれません。 しかし、本書の立場は明確です。 設計者は、 正解を当てる人ではありません。 なぜなら、 要件は変わる 前提は崩れる 想定外は必ず起きる からです。 設計者に求められるのは、 当てる力ではなく、外れたときに耐えられる力です。 設計者の責任は「選び直せる状態」を作ること では、設計者は何に責任を持つべきなのでしょうか。 それは、 将来、判断を選び直せる状態を残すこと です。 どこに判断があるか分かる どの判断が強いか弱いか分かる どこを変えればよいか議論できる この状態を作ることが、 設計者の責任です。 逆に言えば、 なぜこうなっているか分からない 触るのが怖い 壊したら戻せない という状態を残すことは、 設計者の責任放棄だと言えます。 良い設計は、未来への配慮である 設計は、 その場の問題を解くだけの行為ではありません。 数か月後の自分 これから参加するメンバー 自分の知らない誰か そうした未来の人たちが、 コードと向き合うことになります。 良い設計とは、 未来の他者に対する配慮 です。 迷わなくて済むようにする 壊してよい場所を示す 判断の痕跡を残す これは、 技術的な話であると同時に、 とても人間的な話でもあります。 設計し続けるという責任 設計は、一度して終わりではありません。 要件が変わる チームが変わる 技術が変わる そのたびに、 判断を見直し、置き直す必要があります。 ...