はじめに
「良いコードとは何か」という問いに対する答えは、たいてい抽象的になりがちです。
「読みやすく」「疎結合で」「テストしやすく」——どれも正しいのですが、いざレビューで後輩に伝えようとすると、言葉が滑って相手に届かないという経験をお持ちの方も多いのではないでしょうか。
本書『ルールズ・オブ・プログラミング』は、その「滑る言葉」を、あえて具体的で、数字入りで、反論可能な21個のルールに翻訳してみせた一冊です。
著者は Chris Zimmerman 氏。ビデオゲーム開発スタジオ Sucker Punch Productions の共同創業者で、『怪盗スライ・クーパー』『inFAMOUS』、そして『Ghost of Tsushima』のプログラミングチームを率いてきた人物です。四半世紀にわたって一つのコードベースを育て続けてきたチームが、社内で実際に運用してきたルール集がそのまま書籍化されています。
本記事では、設計・アーキテクチャに関心のあるエンジニア向けに、本書の構造と、特に議論の余地がありそうなルールを中心に整理します。
書誌情報
| 項目 | 内容 |
|---|---|
| 書名 | ルールズ・オブ・プログラミング ―より良いコードを書くための21のルール |
| 著者 | Chris Zimmerman(クリス・ジマーマン) |
| 訳者 | 久富木 隆一 |
| 出版社 | オライリー・ジャパン |
| 発行 | 2023年8月24日 初版第1刷 |
| ISBN | 978-4-8144-0041-6 |
| コード例 | C++(付録にPython/JavaScript読者向けの読解ガイドあり) |
日本語版には、ゲームAI開発者の三宅陽一郎氏による推薦文が寄せられています。また、本書の印税は全額、若年女性のプログラミング教育に取り組む Girls Who Code に寄付されるとのことです。
本書がユニークな点:ルールが「生まれた経緯」から始まる
本書は、いきなりルール1から始まりません。
冒頭に「ルールにまつわる物語」という章が置かれており、そこで著者は、このルール集が苛立ちから生まれたことを率直に明かします。
新卒プログラマーがレビューに出してくるコードが、目の前の単純な問題ではなく、その問題を部分集合として含む「もっと大きな問題」を解こうとしている。「今それを解かなくていい」と口頭で指摘しても、同じことが繰り返される。そこで著者は、抽象的な指導をやめ、「例が3つ揃うまで一般化してはならない」という具体的なルールとして言い切ってみた——すると、これが機能した、というエピソードです。
これが本書全体の設計思想を表しています。つまり、
- 一般的な哲学は伝わらない
- 具体的な条件と閾値を持つルールに変換すると伝わる
という、チームへの知識移転についての方法論が土台にあるわけです。
そして本書には「ルールに反対する方法」という章まで用意されています。著者は、読者がすべてのルールにうなずきながら読み進めてしまったら、それは失敗だと書きます。むしろ最も反発を覚えたルールこそ価値がある、と。反対意見を持ったときの調停プロセスとして、次のような手順が提示されます。
- その主張の中に、欠陥だけでなく真実を探す
- その主張が真になる状況はどんな状況かを考える
- 逆に、自分の反対意見が偽になる状況を考える
- 状況は変わりうるので、自分がその状況に迷い込んでいないか警戒する
著者自身、テスト駆動開発は自社の状況と噛み合わないとしつつ、そこに真実があることは認め、状況が変われば哲学も変わると述べています。この誠実さが、本書を単なる「俺流ベストプラクティス集」から引き離しています。
21のルール全体像
21個のルールは独立して読めますが、テーマごとにゆるくグループ化して眺めると構造が見えてきます。
土台となるルール
| # | ルール |
|---|---|
| 1 | できるだけ単純であるべきだが、単純化してはいけない |
コードの読みやすさ・名前づけ
| # | ルール |
|---|---|
| 3 | 優れた名前こそ最高のドキュメントである |
| 9 | 集約可能なコードを書け |
| 14 | コードには種類が4つある |
| 18 | コードに自らの物語を語らせろ |
早すぎる抽象化・最適化への抑制
| # | ルール |
|---|---|
| 4 | 一般化には3つの例が必要 |
| 5 | 最適化に関する教訓その1は、「最適化するな」 |
| 10 | 複雑性を局所化せよ |
| 17 | 大きな問題ほど解決しやすいこともある |
バグ・デバッグ・堅牢性
| # | ルール |
|---|---|
| 2 | バグは伝染する |
| 7 | 失敗が起こる場合をなくす |
| 8 | 実行されていないコードは動作しない |
| 13 | 雪崩を起こした小石を探せ |
| 16 | コードから先に進むのではなく、結果から後ろ向きにたどれ |
チームとプロセス
| # | ルール |
|---|---|
| 6 | コードレビューが役に立つ3つの理由 |
| 12 | 大きなチームには強い規則が必要 |
| 15 | 雑草は抜け |
| 19 | 作り直しは並列で行うこと |
意思決定と規律
| # | ルール |
|---|---|
| 11 | 2倍良くなるか? |
| 20 | 計算をやっておけ |
| 21 | とにかく釘を打たなきゃいけないこともある |
特に読み応えのあったルール
ルール1:できるだけ単純であるべきだが、単純化してはいけない
アインシュタインに帰せられる格言をプログラミングに引き写したもので、本書全体の背骨にあたります。要件をすべて満たす最も単純な実装が最良である、というシンプルな主張です。
面白いのは、著者がこの主張を「短ければ良い」と混同していない点です。ビット演算を駆使した高速なビットカウント実装と、素朴なループによる実装を並べたうえで、行数が少ないことと理解しやすいことは別であると示します。単純さの評価軸は、文字数ではなく、読み手が背負う認知的負荷の方に置かれています。
ルール4:一般化には3つの例が必要
本書で最も引用されるであろうルールです。
「赤い標識を探す関数」を書くとき、私たちはほぼ反射的に「任意の色の標識を探す関数」を書きたくなります。その直感自体は自然なものですが、著者はここに歯止めをかけます。将来の要求は予測できず、予測に基づいた一般化は、たいてい間違った軸で一般化されるからです。
章の締めくくりで使われる比喩が的確でした。一般的な解法を作るというのは、チームにハンマーを配ることに等しい。手元にあるのが釘の袋だと確信できるまで、ハンマーを配ってはいけない——という話です。
抽象化を「早くやる/やらない」ではなく、「3つ目の実例が現れたときに初めてやる」という運用可能な閾値に落としているのが、このルールの実務的な価値だと感じました。
ルール5とその直後の「幕間」
「最適化するな」というルールに対し、著者は Knuth の有名な引用を援用しつつ、単純明快なコードを先に書き、必要になってから対処せよと説きます。
驚いたのは、この章の直後に「幕間 ルール5の章への批判として」が置かれていることです。著者はこのルールが社内で唯一、同僚から即座に不支持を表明されたと述べ、複数の反対論者を合成した架空の人物との対話形式で、その反論を丸ごと収録しています。
反対側の言い分は、おおむね次のようなものです。
- Knuth の引用は、あらゆる低速コードの正当化に濫用されてきた
- パフォーマンス上の設計ミスは、後から局所最適化では取り返せない
- 実際、UI アーキテクチャの選択ミスで出荷が半年遅延した事例がある
これを受けて著者は、パフォーマンス問題が致命的になるのは計測している場合に限って早期に露見するのだと補足し、Sucker Punch が日常的にプロファイラを回していることを明かします。そして反対論者から、「最適化を気にするな」ではなく「単純なコードは最適化しやすいから単純に書け」が本意だろうと指摘され、著者はそれを認めます。
主張を書いた直後に、その主張への反論を自ら掲載する構成は、技術書としてかなり珍しいと思います。ルールを絶対視させないための仕掛けとして、非常によく効いています。
ルール9:集約可能なコードを書け
個人的に最も設計に効くと感じたルールです。
著者は、コードを読む行為を「短期記憶で複数のボールをジャグリングする作業」として捉えます。人間が同時に保持できるのは7個前後(プラスマイナス2)が限度で、未解決の謎、判明した事実、それらの関係のすべてがボールとして数えられる。上限を超えた瞬間にボールは落ち、謎は永久に解けなくなります。
そして重要なのは、ボールは増えるだけでなく減るという点です。エラトステネスの篩のコードを一行ずつ追っていく例が示され、断片的な理解が積み上がったあと、「この配列は素数で埋まる」と気づいた瞬間に、抱えていた考えの数が一気に1へ集約される様子が描かれます。
良いコードとは、この集約が起こるように書かれたコードである——という定義は、「読みやすさ」という曖昧な語をかなり操作的な基準に置き換えてくれます。関数分割の粒度を議論するときの共通言語として使えそうです。
ルール14:コードには種類が4つある
問題を「やさしい/難しい」、解法を「単純/複雑」の2軸で切り、4象限を作るモデルです。
| やさしい問題 | 難しい問題 | |
|---|---|---|
| 単純な解法 | 期待通り | 野心的 |
| 複雑な解法 | 実に、実にひどい | 容認されている |
配列の最大値・最小値を求めるという典型的な「やさしい問題」に対して、テンプレートと抽象化を何層も重ねた実装例が提示されます。個々の判断には一応の理由が想像でき、命名も説明的で、悪意もない。それでも4倍の分量になり、理解しにくくなっている——という例示が痛烈でした。
「悪意なく積み上がった善意の抽象化」こそが、左下の象限を生む主犯である、という指摘は耳が痛い方も多いはずです。
ルール13:雪崩を起こした小石を探せ
デバッグを「症状から原因へ、時間を段階的に遡って再構成するプロセス」と定義し直すルールです。
雪崩を引き起こした最初の小石まで遡らずに、途中で症状を潰して勝利宣言してしまう誘惑が、遡行のあらゆる段階に存在する、という指摘が鋭い。null ポインタチェックを足せばクラッシュは止まりますが、小石はそこに残ります。実際に例示される修正は、チェックを足す方向ではなく、そもそもポインタ変数を排除して問題が起こりえない形にする方向でした。
この「症状ではなく小石を消す」という発想は、後述するルール7と地続きです。
ルール7:失敗が起こる場合をなくす
「このインターフェースのユーザーが自滅するのを、どれだけ難しくできているか」を設計時の中心的な問いとして据えるルールです。
機能を公開すれば、誰かが必ず奇妙な使い方をします。そして誤用しやすいインターフェースにおいては、誤用は設計段階で「組み込まれてしまっている」と著者は言います。やるべきなのは、誤用を設計段階で外すことです。
型で不正な状態を表現できなくする、初期化順序に依存しない構造にする、といった話は他書でも語られますが、「失敗のケースを検査するのではなく、失敗のケースそのものを設計から消す」という言い切りは記憶に残ります。
ルール16:コードから先に進むのではなく、結果から後ろ向きにたどれ
既存コードの都合から発想するのではなく、達成したい結果の側から逆算せよ、というルールです。
例として、JSON 設定ファイルからブロック対象サーバーの一覧を読む処理が挙げられます。パーサーが提供する API を素直に組み立てていくと、キーの存在確認と型チェックが延々と続く冗長なコードになります。ここで著者は、そもそも呼び出し側が書きたかったコードは何だったのかを起点に据え直し、インターフェースの側を変えていきます。
「手元のライブラリで何ができるか」から始める癖がついていると、なかなか出てこない発想でした。
ルール19:作り直しは並列で行うこと
大規模な作り直しを長命ブランチでやると何が起こるか、という現実的な話です。基幹ブランチとの統合の苦痛、旧システムの挙動を見失うこと、他メンバーからは進行中の作業が見えないこと——身に覚えのある方も多いでしょう。
本書が推すのは、旧実装と新実装を同時に本流に置き、両方を動かして結果を突き合わせるアプローチです。スタックアロケーターの作り直しを題材に、切り替えとロールバックが常に可能な状態を保ちながら移行していく手順が具体的に語られます。
Strangler Fig パターンやフィーチャーフラグを使った移行と発想は近いのですが、「差分検証を移行の主役に置く」という点にゲーム開発ならではの実践的な手触りがありました。
ルール20:計算をやっておけ
エンジニアの判断は曖昧なものが多いが、中には単純な四則演算で決着がつく判断が混ざっている。それを見逃すな、というルールです。
自動化すべきかどうかの判断が、実測値を使った掛け算で示されます。手作業1回あたりの実測時間、章あたりのコード例の数、章数、修正回数を掛け合わせると48分。自動化の実装見積もりは1時間。したがって、計算上は自動化すべきでない、という結論になります。
ここで終わらないのが本書の良いところで、著者はさらに問いを重ねます。便益を悲観的に見積もった可能性と、実装時間を楽観的に見積もった可能性、どちらが高いか? プログラマーなら答えは分かるはずだ、と。そして、コストと便益が拮抗しているなら自動化するなという判断基準を導きます。
見積もりの非対称性を前提に閾値を寄せておく、という考え方は、自動化に限らず幅広く応用が効きそうです。
ルール12:大きなチームには強い規則が必要
「自分のコード」と「他人のコード」に線を引いてスタイルを分ける運用は、2人チームでも早晩破綻するという主張です。境界を頻繁に往復することになりますし、そもそも境界上のインターフェースの命名を誰が決めるのかという問題が残ります。
創造性のために制約を減らすべきだという主張に対して、著者は、スタイルの差異が生む複雑性がチーム全体の生産性を削るとして退けます。規則の内容そのものよりも、規則が存在し一貫していることに価値を置く立場です。
このルールは、SES や受託でチームを移動する機会が多い立場だと、特に実感を伴って読めるのではないかと思います。
本書の限界と、どう読むか
本書は徹頭徹尾、Sucker Punch というひとつの組織の文脈から書かれています。最終章「結論 ルールを自分のものにする」で、著者はその前提条件を明示します。
- プロジェクトが長い(『Ghost of Tsushima』は約6年)
- コードの寿命が長く、10年後も動いている可能性がある
- コーディングチームは30人規模、コードベース全体を把握している人はいない
- パフォーマンスは重要だが、高速に動く必要があるのはコードのごく一部
- リリース頻度が低く、大きな変更に着手しやすい
- 後方互換性の制約がほぼない
- 開発は反復的で、作ってみないと面白いかどうか分からない
Web サービスのように継続的にデプロイし、後方互換性に強く縛られ、ユニットテストで守りを固める開発とは、条件がかなり異なります。実際、著者はユニットテストをあまり書かないと明言しており、その理由として、多少のバグは許容できるがパフォーマンス問題に不意を突かれたくないから、と説明しています。
したがって本書は、「そのまま適用するチェックリスト」としてではなく、自チームの前提条件を言語化するための鏡として読むのが正解だと感じました。あるルールに違和感を覚えたなら、その違和感の正体こそが、自分たちの制約条件です。著者自身が「ルールに反対する方法」で促しているのは、まさにその作業です。
こんな人におすすめ
- コードレビューで「なぜこの書き方が良くないのか」を言語化したい人
- 早すぎる抽象化や過剰な汎用化にチームとして歯止めをかけたい人
- 「読みやすいコード」を、もう少し操作的な基準で語りたい人
- 設計原則を、自チームの文脈に翻訳し直す作業に関心がある人
逆に、体系立った設計理論やパターンカタログを求める場合は、本書は少し肩透かしかもしれません。本書はカタログではなく、経験則の集合です。
C++ の知識は必須ではありません。オブジェクト指向の基礎があれば読み通せますし、付録として Python 向け・JavaScript 向けの C++ 読解ガイドがそれぞれ用意されています。
まとめ
『ルールズ・オブ・プログラミング』の価値は、21個のルールそのものよりも、経験則を反論可能な形式に落とし込む姿勢にあると思います。
- 抽象的な哲学ではなく、閾値を持つ具体的なルールにする(例が3つ揃うまで一般化しない)
- ルールの適用範囲となる前提条件を明示する
- 反対意見を排除せず、本文中に同居させる(幕間の対話形式)
この3点は、そのままチームのコーディング規約やレビュー文化の設計指針として使えます。自チームのルールを書き起こす前に、本書がどうやって21個を導出したかを眺めておくと、だいぶ違うものが書けるはずです。
読み終えたあと、自分が最も反発を覚えたルールを1つ選び、それがなぜ自分の現場に合わないのかを説明してみる——本書の意図に沿った読み方は、たぶんそれです。
関連書籍
- 『Code That Fits in Your Head(脳に収まるコードの書き方)』 Mark Seemann — ルール9の「集約可能性」と問題意識が重なります
- 『A Philosophy of Software Design』 John Ousterhout — 複雑性の局所化(ルール10)と近い立場
- 『達人プログラマー 第2版』 Andrew Hunt / David Thomas — 経験則を短い格言に凝縮する形式が共通しています