はじめに
「レガシーコードを書こう」と思ってコードを書く人はいません。それでもコードベースは確実に劣化していきます。本書は、その劣化を「気合い」や「センス」ではなく、しきい値とプロセスで押しとどめようとする一冊です。
本記事では、Mark Seemann 著『脳に収まるコードの書き方 ―複雑さを避け持続可能にするための経験則とテクニック』(オライリー・ジャパン)の内容を、設計・アーキテクチャに関心のあるエンジニア向けに総まとめします。
| 項目 | 内容 |
|---|---|
| 原著 | Code That Fits in Your Head: Heuristics for Software Engineering |
| 著者 | Mark Seemann(マーク・シーマン) |
| 訳者 | 吉羽龍太郎、原田騎郎(株式会社アトラクタ) |
| 出版 | Addison-Wesley Professional(2021年) / オライリー・ジャパン(2024年) |
| サンプル言語 | C#(C系オブジェクト指向言語なら読める保守的なサブセット) |
著者はデンマーク在住のプログラマー/ソフトウェアアーキテクトで、依存性注入の書籍で Jolt Award を受賞している人物です。自身のブログで700本以上の記事を書き続けてきた蓄積が、本書の密度に表れています。
本書の主張を一文で言うと
ソフトウェアエンジニアリングが解決すべき根本問題は、ソフトウェアが人間の脳に収まらないほど複雑になることである。
本書のタイトル「脳に収まる」は比喩ではなく、人間の短期記憶が4〜7個の情報しか保持できないという制約を、そのまま設計の制約として持ち込むという意味です。
コードを読むとき、私たちは頭の中でプログラミング言語のエミュレータを走らせています。追いかけるべきものが7つを超えた瞬間、そのコードは短期記憶に収まらなくなり、理解するために「長期記憶に刻み込む」という高コストな作業を強いられます。それがレガシーコードの正体だ、というのが本書の出発点です。
そして著者は、コードを資産ではなく負債として扱います。速く大量にコードを生み出せるツールは、そのぶん読まなければならないコードを増やすだけだ、という指摘は、コード生成が当たり前になった今こそ刺さります。
本書の構成
本書は「レストラン予約システム」というひとつのコードベースを最初の1行から最後まで育てていく形式を取っています。パターンカタログ的な羅列ではなく、物語の流れに沿って経験則が登場するのが最大の特徴です。
- 第Ⅰ部 加速:コードが何もない状態から、最初の機能をデプロイするまで
- 第Ⅱ部 持続可能性:デプロイ後、巡航速度をどう維持するか
サンプルコードは Git リポジトリとして公開されており、本文中のコードリストにはコミットIDが埋め込まれています。git checkout f729ed9 のように該当時点をチェックアウトすれば、実行可能な文脈でコードを読めるという仕掛けです。
面白いのは、第Ⅰ部と第Ⅱ部で Git 履歴の作り方を変えている点です。第Ⅰ部の履歴は何度もリベースして磨き上げられており、学習ノイズが除去されています。一方で第Ⅱ部の履歴は「正直なまま」で、直前のコミットを取り消すコミットなども残されています。実際の開発の泥臭さを、意図的に教材として残しているわけです。
第Ⅰ部 加速
1章 アートかサイエンスか
ソフトウェア開発の比喩として使われてきた「家を建てる」「庭を育てる」の両方を検討し、どちらも不十分だと結論づけます。過去50年の進歩の多くはハードウェア側のもので、ソフトウェア危機はまだ終わっていません。
ただし著者は悲観していません。「本物の」エンジニアがやっていることの多くは、今日から真似できる、というのが本書全体を貫くトーンです。
2章 チェックリスト
新しいコードベースを始めるときのチェックリストとして、著者は3項目だけを挙げます。
- Git を使え
- ビルドを自動化せよ
- エラーメッセージをすべて有効にせよ
拍子抜けするほど当たり前ですが、ポイントは「チェックリストはスキルを向上させずに結果を改善する」という点にあります。チェックリストは人を管理する道具ではなく、支援する道具だ、という位置づけが明確です。
3番目の「警告をエラーとして扱う」は、既存コードベースに後から入れると大量の警告に埋もれます。そこで本書は漸進的改善と「組織をハック」する現実的な手順も提示しています。
3章 複雑さに対処する
本書の理論的な中核です。ここで「複雑(complex)」を、リッチ・ヒッキーの用法にならって「単純(simple)の反対語」=部品が絡み合っている状態として定義します。多い/少ないではなく、絡んでいるかどうかの話です。
そして「コードは書く回数より読む回数のほうが多い」という古典的事実から、可読性のために最適化せよという指針を導きます。
この章で個人的にいちばん効いたのは、速度に関する逆説でした。J・B・レインズバーガーの言葉として引かれる「速度を落とす必要があるかもしれない」という指摘は、タイピング速度を上げることが生産性だと信じていた頃の自分に読ませたい部分です。
4章 バーティカルスライス
機能を「レイヤーごと」ではなく「縦に薄く」切って、入力から永続化までを最初に貫通させます。いわゆるウォーキングスケルトンです。
最初のスライスは、入力した値と保存される値が一致しないほど薄いものです。それでも意味があるのは、動くシステムとデプロイパイプラインが手に入るからです。あとはそこに改善を積むだけになります。
ここで Arrange-Act-Assert によるテスト構造、特性評価テスト、静的解析との付き合い方(ルールから外れるなら理由を残す)が導入されます。
5章 カプセル化
「フィールドを private にして getter/setter で包むこと」はカプセル化ではない、と本書は明言します。カプセル化の本質は、オブジェクトが決して無効な状態にならないことを保証することです。
- 事前条件:呼び出し側が果たすべき責任
- 事後条件:呼び出し側が責任を果たしたときにオブジェクトが与える保証
- 不変条件:その両者から形成される、常に成立する性質
そして契約設計の指針としてポステルの法則(送るものは慎重に、受け取るものは寛容に)が置かれます。呼び出し側に要求することが少ないほど使いやすく、保証が強いほど呼び出し側の防御的コードが減る、という非対称な設計方針です。
6章 三角測量
テスト駆動開発を「三角測量」の比喩で説明します。ただし測量と決定的に違うのは、測る対象がまだ存在しないという点です。テストという計測を増やすことで、これから作るシステムの輪郭が確定していきます。
ここで登場する 「悪魔の代弁者(Devil's Advocate)」 は実務で使いやすい経験則です。テスト対象をわざと誤った実装に書き換えてみて、それでもテストが通るなら、そのテストスイートには穴があります。「どれだけテストを書けば十分か」という永遠の問いに、操作可能な形で答えを与えてくれます。
7章 分解
タイトルを最も直接的に体現している章です。
コードの腐敗は一夜にして起こらず、少しずつ進みます。いわゆる「茹でガエル」です。これに対抗する手段が、しきい値を決めてメトリクスを監視することです。
本書が提示する具体的なしきい値は次のとおりです。
| 経験則 | 内容 |
|---|---|
| サイクロマティック複雑度 7 | 分岐の数。7を超えたら変更を拒否する運用にする |
| 80/24ルール | 1行80文字、メソッド24行に収める(VT100 の解像度が由来) |
| 変数を数える | ローカル変数・引数・フィールドをすべて数え、少なく保つ |
「7」という数字は3章の短期記憶の限界から来ています。著者はこれを ヘックスフラワー(6角形7つの図)として視覚化し、「1つのコードの中で7を超えることをしてはいけない」と表現します。7つのスロットが埋まったら、それ以上の複雑さは追加できない。だから分解する、という流れです。
重要なのは、著者自身が「数字は恣意的」と繰り返し断っていることです。7でも10でも120×40でも構わない。しきい値を設定し、それを守り続ける仕組みを自動化することが本質だと述べています。CI のビルドに複雑度分析を組み込み、超えたら落とす。これは「計測したものが手に入る」という管理効果を意図的にハックする試みだと明言されています。
そして本章の到達点が フラクタルアーキテクチャ です。高レベルで見ても7つ以下、中間レベルでも7つ以下、低レベルでも7つ以下。どのズームレベルで切り取っても複雑さが人間の範囲に収まる、自己相似的な構造を目指します。
分解の指針としては、凝集、特性の横恋慕(Feature Envy)、そして 「検証せずにパースする」(Parse, don't validate)が挙げられます。JSON や CSV のような構造のないデータは、できる限り早く構造化データへ変換する、という原則です。
8章 API設計
アフォーダンス、ポカヨケ、コマンドクエリ分離といった原則が並びますが、実務で最も持ち帰りやすいのは次の2つでした。
名前をXで置き換える
メソッド名を頭の中で X に置き換えて、シグネチャ(型)だけでどれだけ情報が伝わるか検証します。静的型付け言語では、型そのものが強力なドキュメントである、という主張を検証可能な形にした手法です。
コミュニケーション階層
将来の読み手に意図を伝える手段を、効果の高い順に並べたものです。
- API に明確な型を与える
- メソッドにわかりやすい名前をつける
- 良いコメントを書く
- 自動テストとしてわかりやすい例を提供する
- Git でわかりやすいコミットメッセージを書く
- わかりやすいドキュメントを書く
上位ほど重要で、そこから一般化されるルールが「メソッド名で伝えられることをコメントに書くな。型で伝えられることをメソッド名に書くな」です。
根拠はシンプルで、コードだけが最新であることを保証されている成果物だからです。名前もコメントもドキュメントも古くなりますが、型はコンパイルを通らなければ間違いに気づけます。
第Ⅱ部 持続可能性
9章 チームワーク
Git の使い方から入ります。コミットメッセージの 50/72ルール(サマリーは命令形で50文字以内、本文は72文字幅)が紹介されますが、訳注で「日本語なら現在形で半分程度の文字数を目安に」と補足されているのが親切です。
サマリーには「何を」ではなく 「なぜ」 を書く。何を変えたかは diff を見ればわかるからです。
コードの共同所有については、ペアプロ/モブプロによるカジュアルなレビューと、プルリクエストによる非同期レビューの両方を扱います。印象的なのは 「変更を拒否できなければレビューの価値はない」 という一節で、承認が形骸化したレビューへの批判として機能しています。
10章 コードの増大
既存コードベースに機能を足す方法論です。
- フィーチャーフラグ:半日で終わらない変更は、ブランチではなくフラグで隠しながら統合し続ける
- ストラングラーパターン:メソッドレベル/クラスレベルで新旧を共存させ、呼び出し元を徐々に移行する
- セマンティックバージョニング:破壊的変更は事前に廃止予定を告知してから
「フィーチャーブランチを使いたくなるが、その道はマージ地獄へ続いている」という警告は、統合前の作業時間は最大でも4時間まで、という具体的な指針とセットで語られます。
11章 ユニットテストを編集する
見落とされがちですが、本書で最も実務的な章のひとつです。
テストコードにはセーフティネットがありません。テストをテストする自動テストは存在しないからです。したがって、
- テストコードをリファクタリングするときは、プロダクションコードを触らない
- プロダクションコードをリファクタリングするときは、テストコードを触らない
- 両者は別のコミットに分ける
というルールが導かれます。テストの追加やアサーションの追加は安全、IDE組み込みのリファクタリングも安全、それ以外は慎重に、という安全度のグラデーションが整理されています。
12章 トラブルシューティング
科学的手法、ラバーダッキング、そして 二分法 を扱います。コードを半分削って問題が再現するか確かめ、最小の再現例に到達するまで繰り返す。git bisect も同じ発想です。
この章の締めくくりは強烈で、「本章でデバッグの話をまったくしていないことに気づいたか」と問いかけたうえで、本番環境ではデバッガは使えないと指摘します。科学的手法・自動テスト・二分法の組み合わせのほうが汎用的で効率的だ、という主張です。
デンマーク語のソート順(aa が å として扱われ bb より大きくなる)が原因で、特定マシンでだけテストが落ちたという実話も、経験則の限界と価値を同時に示していて記憶に残ります。
13章 関心事の分離
オブジェクト指向による分解の限界を率直に認めたうえで、関数型コア・命令型シェルへの移行を推奨します。
オブジェクトをネストして合成すると、重要なふるまいが階層の奥に隠れてしまい、脳に収まらなくなります。代わりに、純粋関数が返したデータを次の純粋関数の入力にする逐次合成を使えば、処理の流れが平坦に見えるようになります。
参照透明性(関数呼び出しをその結果で置き換えてもプログラムの振る舞いが変わらない)を「究極の抽象化」と位置づけ、純粋関数は合成しやすくテストしやすいと整理します。
そして横断的関心事(ロギング、キャッシュ、フォールトトレランス)は デコレーター で外から被せる。ロガーをビジネスロジックに注入するのは関心事の分離に反する、という指摘は耳が痛いところです。
14章 リズム
個人とチームのリズムを扱う短い章です。タイムボックス、休憩、意識的な時間の使い方、タッチタイプ。チーム側では依存関係の定期更新とコンウェイの法則。
「ベストなアイデアはコンピューターから離れているときに浮かんだ」という著者の述懐は、生産性ハック本にありがちな押しつけがない分、素直に受け取れます。
15章 いつもの顔ぶれ
パフォーマンスとセキュリティという「古典的」なトピックを簡潔に扱います。
セキュリティは STRIDE(なりすまし/改ざん/否認/情報漏えい/サービス妨害/権限昇格)による脅威モデリングを紹介。専門家でなくても取り組める枠組みとして提示されています。
パフォーマンスについての立場は明快で、正しく動くことのほうが速く動くことより重要、というものです。そして「コードベースが今後何年も組織を支えられることと、少し速く動くこと、どちらが重要か」はステークホルダーを巻き込んで決める問いだ、と結ばれます。
16章 ツアー
完成したコードベースを俯瞰し、フラクタルアーキテクチャが実際に成立していることを確認する章です。
締めくくりの主張は、エンジニアリングとは決定論的プロセスと人間の判断の混合物である、というものです。完全に決定論的になったらそれはエンジニアリングではなくマニュファクチャリングだ、と。方法論をいくら適用しても、自分の脳を使う責任はなくならない、という一文で本書は閉じます。
付録A が実質的なチートシート
本書には28個の経験則が付録Aにリスト化されており、それぞれ本文の参照先が明記されています。読み終えたあとはここだけを見返せば運用できる設計になっています。
主なものを抜粋します。
- 50/72ルール(コミットメッセージ)
- 80/24ルール(コードブロックのサイズ)
- Arrange-Act-Assert
- 二分法
- 新しいコードベースのためのチェックリスト
- コマンドクエリ分離
- 変数を数える
- サイクロマティック複雑度
- 横断的関心事のためのデコレーター
- 悪魔の代弁者
- フィーチャーフラグ
- 関数型コア・命令型シェル
- コミュニケーション階層
- ルールの例外を正当化する
- 検証せずにパースする
- ポステルの法則
- レッド/グリーン/リファクタリング
- 依存関係を定期的に更新する
- 欠陥をテストとして再現する
- コードをレビューする
- セマンティックバージョニング
- テストとプロダクションコードを別々にリファクタリングする
- スライス
- ストラングラー
- 脅威モデル
- 変換の優先順位
- X駆動開発
- 名前をXで置き換える
明日から試せること
読了後に、コストが低い順で実際に着手できそうなものを並べてみました。
- 警告をエラーとして扱う設定を入れる(既存プロジェクトなら新規プロジェクトから)
- コミットメッセージに「なぜ」を書く:diff でわかることは書かない
- メソッドの行数と変数の数を数えてみる:まずは計測だけ。直すのは次の段階
- CI にサイクロマティック複雑度のチェックを追加:最初は警告のみ、慣れたら失敗させる
- テストコードとプロダクションコードのコミットを分ける
- 「名前をXで置き換える」を PR レビューで使う:型だけで意図が伝わるかを確認
- 悪魔の代弁者でテストの穴を探す:既存の重要なロジックを1つ壊してみる
どれも新しいツールの導入を必要とせず、今のコードベースにそのまま適用できます。
気になった点
- サンプルが C# / ASP.NET Core です。C系オブジェクト指向言語の読解力があれば追えますが、DI コンテナや ASP.NET 固有の記述に馴染みがないと、4章あたりで少し立ち止まります。概念そのものは言語非依存なので、読み飛ばしても本筋は追えます。
- サンプルのターゲットフレームワークが .NET Core 3.1 で、既にサポートが終了しています。手を動かして追体験したい場合は、訳者あとがきにあるとおり一部コードの書き換えが必要です。
- 初心者向けではありません。長いメソッドが問題である理由やグローバル変数が悪である理由は既知の前提です。数年の実務経験があることが実質的な条件になります。
- 高レベルなアーキテクチャ論(ATAM、FMEA、サービスディスカバリなど)は明示的に範囲外です。「コードに近い側のアーキテクト」の本だと理解しておくとギャップがありません。
他の書籍との関係
本書は「散らばっている知識を一貫した形にまとめ直す」ことを目的としているため、単独で完結するというより、既読の本を接続するハブとして機能します。
| 書籍 | 本書との関係 |
|---|---|
| 『Code Complete』 | 本書が前提とする基礎。レイアウトや空行の扱いはこちらを参照 |
| 『リファクタリング』 | 分解の具体的手法。本書は「いつ分解するか」のしきい値を担当 |
| 『継続的デリバリー』 | フィーチャーフラグ、デプロイパイプラインの下敷き |
| 『LeanとDevOpsの科学』 | 「先進的なテクニックは既に存在し、無料で手に入る」という主張の根拠 |
| 『Clean Architecture』 | 抽象の定義(無関係を排除し本質を増幅する)を本書が引用 |
まとめ
本書の価値は、新しい概念を発明したことではなく、既知の経験則を「脳の短期記憶」という一本の軸で串刺しにしたことにあります。
- なぜメソッドを短くするのか → 短期記憶に収まらないから
- なぜサイクロマティック複雑度を7に抑えるのか → 短期記憶の限界が7だから
- なぜ型に語らせるのか → 名前もコメントも古くなるが、型だけは古くならないから
- なぜ純粋関数を優先するのか → ネストした合成はふるまいを隠し、脳に収まらなくなるから
個々のプラクティスは他書でも読めます。しかし「どれも同じ理由で正しい」と示されると、記憶に定着する強度が変わります。しきい値が恣意的であることを著者自身が認めたうえで、それでも設定して自動化しろと主張する姿勢も、実務的で信頼できます。
レガシーコードに苦しんだ経験があり、次はそうしたくないと考えているエンジニアには、強くおすすめできる一冊です。