はじめに
『The Pragmatic Programmer: your journey to mastery, 20th Anniversary Edition, 2nd Edition』(Andrew Hunt / David Thomas)を通読しました。
原著初版は1999年、20周年記念版は2019年(邦訳『達人プログラマー 第2版』)に出ています。「名著」として名前だけは知られている一方で、実際に通しで読むと**技術書というより「働き方と判断基準の本」**であることに気づきます。
本記事は、設計・アーキテクチャに関心のあるエンジニア向けに、全9章 + 53トピックを一本にまとめた総括レビューです。個々のTipsを列挙するのではなく、本書全体を貫く一本の軸を中心に整理します。
本書には全部で100個の「ヒント(Tips)」が番号付きで散りばめられています。本記事ではそのうち議論の骨格になるものを取り上げます。
目次
- 第2版で何が変わったのか
- 結論:全体を貫く軸は「ETC」
- 第1章 実用的な哲学
- 第2章 実用的なアプローチ
- 第3章 基本的なツール
- 第4章 実用的なパラノイア
- 第5章 曲げるか、折るか
- 第6章 同時実行性
- 第7章 コーディング中
- 第8章 プロジェクト開始前
- 第9章 実用的なプロジェクト
- 設計・アーキテクチャ視点での要点整理
- おわりに
第2版で何が変わったのか
著者たちは第2版の序文で、改訂にあたって二つの選択肢があったと書いています。参照している技術だけを差し替えるか、20年分の経験を踏まえて推奨してきたプラクティスの前提そのものを再検討するか。結果として両方をやったそうです。
そのため第2版は「テセウスの船」のような本になっています。
| 変更 | 内容 |
|---|---|
| 全面的に新規 | 約1/3のトピック |
| 書き直し | 残りの大部分(部分的〜全面的) |
| 削除 | 付録「リソース」(陳腐化が速く、検索で足りるため) |
| 再構成 | 並行処理の章(並列ハードウェアの普及を反映) |
| 追加 | 関数型のイディオム、プライバシー・セキュリティ、アジャイルの再定義 |
CORBA、CASEツール、インデックス付きループといった1990年代の記述は姿を消しました。一方で著者たちは、技術は変わっても人間は変わっていないため、当時有効だった実践の多くはそのまま有効だ、とも述べています。この「変わった部分」と「変わらなかった部分」の対比自体が、本書を読む価値の一つになっています。
結論:全体を貫く軸は「ETC」
先に結論を書きます。本書の設計論は、トピック8「優れたデザインの本質」で提示されるETC原則に集約されます。
ETC = Easier To Change(変更をより容易に)
著者たちは踏み込んで、こう主張します。
良いデザインは悪いデザインよりも変更しやすい(ヒント14)
そして、自分たちの知る限りあらゆる設計原則はETCの特殊ケースである、と言い切ります。
- なぜ関心の分離が良いのか → それぞれを個別に変更しやすくなるから
- なぜ単一責任原則が有用なのか → 要件変更が1モジュールの変更で済むから
- なぜ命名が重要なのか → 読みやすいコードは変更しやすいから
重要なのは、ETCが**ルールではなく「価値(value)」**として位置づけられている点です。ルールは適用の可否を判定するものですが、価値は意思決定を助けるものです。「今やったことは、システム全体を変更しやすくしたか、しにくくしたか?」と自問し続けることで、判断の基準として身体化していくものだと説明されています。
将来どう変更されるか分からない場合の対処も明快です。書いたコードを「置き換え可能」にしておくこと。分離を意識し、凝集性を保つ。これが「最も変更しやすい」形だという理屈です。
以降の章は、この一点を様々な角度から具体化したものとして読めます。
第1章 実用的な哲学
技術論に入る前に、態度の話から始まります。
責任を引き受ける(トピック2)
達人プログラマーの土台は、自分の行動に責任を持つことだとされます。無知や誤りを認めることを恐れない。そのうえで、**責任は「積極的に同意するもの」**であるという整理が入ります。不可能に見える状況、リスクが過大な状況、倫理的に曖昧な状況については、責任を負わないという選択をする権利がある。この線引きは意外と語られない論点です。
ソフトウェアエントロピーと「割れた窓」(トピック3)
有名な「割れた窓理論」の話です。
- 一枚の割れた窓が放置されると、建物は急速に荒廃する
- コードも同じで、放置された設計の綻び・誤った判断が「もう全部ダメだ」という空気を伝染させる
- 修正する時間がないなら、せめて板で塞ぐ(コメントアウト、未実装メッセージ、ダミーデータ)
割れた窓を放置しない(ヒント5)
面白いのは、著者が「絶望感は伝染する」という心理学的な観点を根拠に置いていることです。技術的負債の議論が、しばしば「返済計画」の話に矮小化されるのに対し、本書はチームの心理的な状態こそが腐敗の速度を決めると主張します。
石のスープと茹でガエル(トピック4)
同じ「徐々に変わること」の光と影を扱った、うまい構成のトピックです。
- 石のスープ:全体の許可を待たず、合理的に要求できる範囲を作り込み、動くものを見せて巻き込む。変化の触媒になる(ヒント6)
- 茹でガエル:一方で、変化に気づかないままゆっくり茹でられる危険もある。全体像を見失わない(ヒント7)
「割れた窓」が気にすることをやめた状態なのに対し、「茹でガエル」は変化に気づいていない状態である、という区別が明示されています。
十分に良いソフトウェア(トピック5)
「Good Enough」はずさんなコードの言い訳ではありません。要件・パフォーマンス・プライバシー・セキュリティの基準は当然満たす前提で、「どこまで作り込むか」を要件の一部としてユーザーと一緒に決めるという主張です。
品質を要件の問題にする(ヒント8)
多くのユーザーは、1年後の完璧よりも今日の「そこそこ」を望む。しかも1年後に必要とされるものは、たいてい別のものになっている——という現実的な指摘が添えられています。
知識ポートフォリオ(トピック6)
知識を金融ポートフォリオになぞらえる、よく引用される節です。定期的に投資する / 分散する / リスクを管理する / 安く買って高く売る / 定期的に見直す。具体的な目標として、毎年1つ新しい言語、毎月1冊の技術書、といった水準が示されます。
コミュニケーション(トピック7)
英語(自然言語)は単なるプログラミング言語の一つ(ヒント11)
自然言語にもDRYやETCを適用せよ、という主張です。同じ変更を、エンドユーザー / マーケティング / 開発マネージャー / 運用にどう説明し分けるか、という具体例が挙げられています。
第2章 実用的なアプローチ
本書の設計論の中核です。
DRY — 知識の重複(トピック9)
第2版で最も大きく整理し直された概念です。著者は初版で説明が不十分だったと認め、こう訂正します。
- DRYは「ソースコードをコピペするな」ではない(それはDRYのごく一部で、些末な部分)
- DRYが対象とするのは知識と意図の重複である
- 判定基準:ある側面を変更するとき、複数の場所を、複数の異なる形式で変更する必要があるか?
コードとドキュメント、DBスキーマとそれを保持する構造体、といった異なるレイヤーをまたぐ重複こそが本命だ、という整理です。
同じ処理が2箇所にあっても、それが偶然一致しているだけなら(=それぞれ独立に変わりうるなら)DRY違反ではない。逆に見た目が全然違っていても、同じ知識を二重に表現していればDRY違反である。この「見た目ではなく知識で判断する」視点が第2版の要点です。
直交性(トピック10)
無関係なもの同士の影響を排除する(ヒント17)
ヘリコプターの操縦の比喩が秀逸です。サイクリック、コレクティブ、ペダル——どれを操作しても他のすべてに二次的影響が出るため、操縦者は常にすべてを同時にバランスさせる羽目になる。非直交システムとはこういうものだ、と。
得られる利点は2つに整理されます。
| 利点 | 内容 |
|---|---|
| 生産性の向上 | 変更が局所化し、開発・テスト時間が短縮。再利用も進む |
| リスクの低減 | 問題箇所が隔離され、テストしやすく、特定ベンダへの依存も減る |
DRYが「知識を1箇所に」と言うのに対し、直交性は「1つの知識を複数コンポーネントに分割するな」と言っている——という対比が第2章の導入で明示されており、この2つはセットで理解すべき概念です。
可逆性(トピック11)
重要な意思決定ほど覆しにくく、決定を重ねるほど選択肢は狭まる。だからこそ、「方法は一つしかない」という前提を持たないことが説かれます。
DRY・分離・外部設定を守っていれば、そもそも「取り返しのつかない決定」の数自体が減る、という論理構成になっています。データベースを抽象化し「永続化をサービスとして提供する」レベルまで落とし込めば、途中での差し替えが現実的になる、という例が挙がります。
曳光弾(トピック12)
要件が曖昧、技術が未知、環境が変わる——この状況で「入念に仕様化してから一発撃つ」のは推測航法だ、という批判から入ります。
代わりに、要件から最終システムの一側面までを、薄く・速く・目に見える形で貫通させる。実弾と同じ環境・制約下で動くから、即座にフィードバックが得られる。
曳光弾を使って標的を見つける(ヒント20)
著者が挙げる最初の曳光弾は「プロジェクトを作成し、Hello Worldを出して、ビルドと実行を確認する」こと。そこからアプリ全体の不確実な部分を探し、動作に必要な骨組みだけを足していきます。5層あるアーキテクチャを、1つの機能で斜めに貫くイメージ図が添えられています。
プロトタイプとの違いも明確です。
| 曳光弾 | プロトタイプ | |
|---|---|---|
| 成果物 | そのまま育てる骨格 | 学習後に捨てる |
| 詳細 | 妥協できない | 大胆に無視してよい |
| 目的 | 統合の確認とフィードバック | 特定の問いへの回答 |
「詳細を譲れない状況にいるなら、それは本当にプロトタイプなのか自問せよ」という一文が効いています(トピック13)。
ドメイン言語(トピック14)
問題領域に近いところでプログラムする(ヒント22)
言語が思考を規定するというヴィトゲンシュタインの引用から始まり、RSpecやCucumberが例に挙がります。
ここで印象的なのは、著者がCucumberについて**「ビジネスユーザーが読む想定だが、実際にはほとんど読まれない」**と正直に書いていることです。その理由の説明が本書らしい。要件定義書に署名させるのは、読めない言語で書かれた文章の校正を頼むようなもので、相手は体面を保つために適当に修正して署名する。動くコードを渡して初めて、本当のニーズが浮かび上がる——という主張につながっていきます。
見積もり(トピック15)
驚きを避けるために見積もる(ヒント23)
実務的に一番効くのは、単位が精度を伝えるという指摘です。
| 期間 | 使うべき単位 |
|---|---|
| 1〜15日 | 日 |
| 3〜6週間 | 週 |
| 8〜20週間 | 月 |
| 20週間超 | 「見積もる前によく考える」 |
「130営業日」と言えば相手は日単位の精度を期待し、「約6か月」と言えば5〜7か月の幅を読み取ります。同じ期間でも伝わる精度が違う。伝えたい正確さに合わせて単位を選ぶ、というのはすぐ使える技術です。
第3章 基本的なツール
職人の道具箱の比喩から始まる章です。IDE一つに閉じこもるのは大きな間違いで、その制限を超えた使い方に慣れる必要がある、と釘を刺されます。
プレーンテキストの力(トピック16)
知識をプレーンテキストで保存する(ヒント25)
バイナリ形式の問題は、データを解釈するのに必要な文脈がデータ自体から切り離されていることだ、と定義されます。HTML / JSON / YAML、HTTP / SMTP / IMAP がすべてプレーンテキストであることには理由がある。
- 陳腐化に対する保険(形式の部分的な知識だけで解析できる)
- 既存ツールを活用できる
- テストが容易になる
バージョン管理(トピック19)
「巨大なUndoキー」という説明から入りますが、本題はコラボレーション・デプロイパイプライン・課題追跡の中心的な会議場としてのVCSです。
共有ディレクトリはバージョン管理ではない、という節が明快でした。同期機構のない並行コードを書いているようなものだ、と。さらに「バージョン管理は使っているがリポジトリをクラウドドライブに置いている」パターンはもっと悪く、状態が破損しうると警告されます。
デバッグ(トピック20)
非難するのではなく、問題を解決する(ヒント29)
慌てない(ヒント30)
バグが誰のせいかは問題ではない。それはあなたの問題である、と切り捨てます。ラバーダック・デバッグの由来(ロンドンの研究助手が黄色いアヒルを端末に置いていた)もここで語られます。
エンジニアリング日記帳(トピック22)
紙のノートを使え、という一節。3つの効用が挙げられます。
- 記憶より信頼できる
- 目の前のタスクと無関係なアイデアの退避先になる(集中を切らさずに済む)
- ラバーダックとして機能する — 書き留めようと手を止めた瞬間に、脳のギアが切り替わる
書こうとして初めて「今やっていることが根本的に間違っている」と気づく瞬間がある、という記述には心当たりのある人が多いはずです。
第4章 実用的なパラノイア
完璧なソフトウェアは書けない(ヒント36)
これを人生の公理として受け入れよ、という宣言から始まります。防御的運転の比喩で、他人のコードだけでなく自分自身も信用しないのが達人だ、と展開されます。
契約による設計(トピック23)
Bertrand MeyerのDBC(Design by Contract)が扱われます。
| 要素 | 意味 |
|---|---|
| 事前条件 | ルーチンが呼ばれるために満たされているべきこと(呼び出し側の責任) |
| 事後条件 | ルーチンが確実に達成すること(=必ず終了することも含意) |
| クラス不変条件 | 呼び出し側から見て常に真である条件 |
「正しいプログラムとは、主張する以上のことも以下のこともしないプログラムである」という定義が的確です。
重要な補足として、事前条件をユーザー入力の検証に使ってはいけないとあります。契約違反は「起きてはならない事象」=バグであり、想定内の不正入力はエラー処理の領域だからです。
死んだプログラムは嘘をつかない(トピック24)
エラーは情報を運んでくる。「そんなことは起こりえない」と無視するのではなく、起きたなら何か非常に深刻なことが起きていると考える。
すべてのcase/switch文にdefault節を書くべき理由も、ここに接続されます。「ありえないこと」がいつ起きたかを知りたいから。
例外を全部キャッチしてログを書いて再スローするスタイルへの批判(「キャッチアンドリリースは魚のためのもの」)も痛快です。
アサーション(トピック25)
不可能を防ぐためにアサーションを使う(ヒント39)
「もちろんそんなことは起こりえない」と思ったら、チェックするコードを追加せよ。ただし実際のエラー処理の代わりに使ってはいけない、という線引きが繰り返されます。
リソースのバランス(トピック26)
始めたことは最後までやり遂げる(ヒント40)
リソースを確保した関数/オブジェクトが、その解放も担うべき。読み込みと書き込みでファイルハンドルをインスタンス変数越しに共有する例が「悪いコード」として示されます。
ヘッドライトから逃げない(トピック27)
常に小さな一歩を踏み出す(ヒント42)
ヘッドライトの照射距離を超える速度で走ると、反応が間に合わない。ソフトウェア開発でも同じで、フィードバックの速度が速度制限である、という比喩です。
「大きすぎるタスクとは何か」という問いへの答えが秀逸で、**「占い(fortune-telling)を必要とするタスク」**だと定義されます。
第5章 曲げるか、折るか
ETCを実装レベルに落とす章です。
デカップリング(トピック28)
分離されたコードは変更しやすい(ヒント44)
橋を設計するときは剛性が欲しいので部材を結合する。しかしソフトウェアに欲しいのは柔軟性なので、逆をやる——という導入が分かりやすい。
結合は推移的である、という指摘が効きます。AがB・Cに結合し、BがM・Nに、CがX・Yに結合していれば、Aは実質M・N・X・Yにも結合しています。
結合の兆候として挙がるリストが実務的です。
- 無関係なモジュール/ライブラリ間の奇妙な依存
- 1モジュールの「単純な」変更が無関係な場所に波及する
- 何が影響を受けるか分からず、変更を恐れる開発者
- 誰が影響を受けるか分からないため、全員参加が必要になる会議
最後の一つは、コードの問題が組織の症状として現れる例で、後述のコンウェイの法則と呼応しています。
具体的な結合パターンとして、列車事故(メソッドチェーン)、グローバル化(静的なものの危険)、継承が挙げられます。
現実世界でのジャグリング(トピック29)
イベント駆動の4戦略が整理されます。
- 有限状態機械(FSM)
- Observerパターン
- Publish/Subscribe
- リアクティブプログラミングとストリーム
FSMについて「難しい」「ハードウェア向け」「専用ライブラリが要る」という思い込みを否定し、数行のコードで書けて、潜在的な混乱を解消できると推している点が印象的でした。
プログラミングの変革(トピック30)
本書の中でも特に思考の転換を迫ってくるトピックです。
すべてのプログラムはデータを変換する。入力を出力に変換する。
しかし設計を考えるとき、我々はクラス・モジュール・データ構造・アルゴリズム・フレームワークのことばかり考えていて、変換そのものについて考えていない、という指摘。
例として、1970年代のUnixプログラマーがディレクトリ内の最長ファイル5件を求める場面が出てきます。彼らはCを書き始めず、パイプラインを組む。
find . -type f | xargs wc -l | sort -n | tail -5
要件が、そのまま一連の変換になっています。
ディレクトリ名
→ ファイルのリスト
→ 行数付きリスト
→ ソート済みリスト
→ 上位5件 + 合計
→ 上位5件
この視点に立つと、構造が明確になり、エラー処理が一貫し、結合度が大幅に下がると主張されます。関数型パイプラインを言語が直接サポートしていなくても取り入れられる、という現実的なスタンスです。
相続税(トピック31)
もっとも挑発的なトピックです。
オブジェクト指向言語で継承を使っていますか? もしそうなら、やめてください。
Simula 67以来の系譜(型の合成としての継承 → C++/Java、振る舞いの動的合成としての継承 → Ruby/JavaScript)を追ったうえで、どちらの用法にも問題があると論じます。
核心は「継承は結合である」という一点です。子クラスが親に結合するだけでなく、子クラスを使うコードがすべての祖先クラスに結合する。バナナが欲しかったのにジャングルごと手に入る、というJoe Armstrongの引用がぴったりです。
代替として、インターフェース+ポリモーフィズム、委譲、mixin/traitが提示されます。
設定(トピック32)
外部設定でアプリをパラメータ化する(ヒント55)
静的設定(フラットファイル / DBテーブル)に加え、第2版では**設定サービス(Configuration as a Service)**が推奨されています。
- 複数アプリで設定を共有でき、認可でスコープを絞れる
- 変更をグローバルに反映できる
- 専用UIで管理できる
- 設定が動的になる
「パラメータ1つ変えるためにアプリを停止・再起動する」という発想は高可用性の現実と噛み合わない、という指摘です。設定は薄いAPIでラップし、グローバル変数として撒かないこと、とも念押しされます。
第6章 同時実行性
第2版で全面的に書き直された章です。まず定義から入ります。
- 並行性(concurrency):2つ以上のコードが同時に実行されているかのように振る舞うこと
- 並列性(parallelism):実際に同時に実行されること
時間的結合(トピック33)
時間的結合とは、問題を解くのに本来必要のない順序をコードが強制してしまうことです。
ワークフローを分析して並行性を高める(ヒント56)
アクティビティ図でワークフローを可視化し、「本来並列にできるのに直列になっている」箇所を見つける、という手法が紹介されます。ピニャコラーダ製造ロボットの例は、手順書としては直列に書かれているが実際には多くが並列可能、という良い例でした。
「アーキテクチャにおいて時間はしばしば無視される側面である」という指摘は、設計をやる人ほど刺さるはずです。
共有状態(トピック34)
共有状態は不正な状態である(ヒント57)
ダイナーのアップルパイの例(2人の店員が同じショーケースを見て、それぞれ客に最後の1切れを約束してしまう)で、非アトミックな更新が説明されます。
問題は「2プロセスが同じメモリに書き込めること」ではなく、どちらもそのメモリに対する一貫したビューを保証できないことである、という定式化が正確です。
セマフォ/相互排他の議論に進みますが、結論は「もっと良い方法がある」です。
アクターとプロセス(トピック35)
共有状態なしの並行性にはアクターを使う(ヒント59)
アクターモデルの制約が列挙されます。
- 全体を制御するものが存在しない(スケジューラも調停役もいない)
- 状態はメッセージと各アクターのローカル状態にのみ存在する
- すべてのメッセージは一方通行(返信という概念がない)
- 各アクターは一度に1メッセージを、完了まで処理する
同期の負担なしに並行性を実装できるのは、そもそも何も共有していないからという一点に尽きます。
黒板(トピック36)
殺人事件の捜査ボードの比喩で説明されるアーキテクチャです。Linda、JavaSpaces、T Spacesが挙げられます。
- 参加者は互いの存在を知らなくてよい
- 専門も所属もバラバラでよい
- 出入りが自由
当初は普及しなかったが、今日ではBlackboard的なセマンティクスを持つミドルウェアが増えており、適切に使えば高度な分離が得られる、と評価されています。
第7章 コーディング中
「コーディングは設計を機械的に書き写す作業である」という考え方こそが、ソフトウェアプロジェクト失敗の最大の原因である——という強い主張から始まる章です。
爬虫類脳に耳を傾ける(トピック37)
本能とは、無意識に蓄積されたパターンへの反応であり、言葉を持たない。だから閃きではなく「なんとなく気が進まない」「面倒すぎる気がする」という感情として現れる。
まずそれが起きていることに気づき、次になぜかを突き止めよ、という順序が示されます。「空白ページへの恐怖」への対処も具体的です。
偶然によるプログラミング(トピック38)
地雷原を銃剣で突きながら進んだ兵士が、安全だと確信して立ち上がった瞬間に吹き飛ばされる——という寓話から始まります。
なぜ動いているのか分からないコードは、なぜ壊れたのかも分からない。「実装時の事故」「文脈の事故」「暗黙の前提」といった形で、偶然の成功を意図的な設計と取り違えるパターンが列挙されます。
アルゴリズムの速度(トピック39)
Big-O記法の実務的な扱い方。厳密な計算量解析ではなく、「今やっていることは妥当か」を素早く確認する道具として位置づけられています。
リファクタリング(トピック40)
建築ではなく庭仕事、というメタファーの転換が本トピックの主張です。
ソフトウェアは建設よりガーデニングに近い。コンクリートというより有機的である。
Martin Fowlerの定義(外部の振る舞いを変えずに内部構造を変える、規律ある手法)を引きつつ、リファクタリングは**「たまに行う大掛かりな儀式」ではなく「雑草取りのような日々の低リスクな活動」**である、と繰り返し強調されます。
テスト(トピック41)
第2版で最も見解が鮮明なトピックの一つです。
テストはバグを見つけるためのものではない(ヒント66)
主張はこうです。テストの最大の便益は、実行するときではなく、テストについて考え、書くときに得られる。
本文中の思考プロセスの実演が説得力を持っています。関数を書き始めた瞬間に「これをどうテストするか」を考えると、
- グローバルなDBインスタンスではなく、DBを引数で渡す設計になる
- 要件の曖昧さ(「10本以上視聴」の"視聴"とは何か)に、コードを書く前に気づく
- フィールド名を引数化するなど、結合を下げる方向に設計が動く
つまりテストは、設計・API・結合についてのフィードバック装置である、という位置づけです。「テストしやすいコードは良い設計である」という主張を、逆側から証明した形になっています。
プロパティベーステスト(トピック42)
プロパティベーステストで前提を検証する(ヒント71)
自分でコードとテストの両方を書くと、同じ誤った前提が両方に埋め込まれるという問題への処方箋です。
契約と不変条件をまとめて「プロパティ」と呼び、それを機械に検証させる。ソートなら「要素数は変わらない」「任意の要素は次の要素以下」といった性質を宣言し、入力はツールに生成させます。
テストと実装を別人が書く案を退けている点が重要で、それではテストを書くことが設計を改善するという効果が失われるからだ、と説明されます。
安全に過ごす(トピック43)
第2版で新設された章です。初版の「スパイほど偏執的になる必要はない」という記述を、著者自ら間違いだったと撤回しています。
侵害の多くは攻撃者が優秀だったからではなく、開発者が不注意だったから起きている、という認識のもと、基本原則が挙げられます。
- 攻撃対象領域を最小限に抑える
- 最小権限の原則
- 安全なデフォルト
- 機密データを暗号化する
- セキュリティアップデートを維持する
コードの複雑さそのものが攻撃ベクトルになる、という指摘は設計の議論に直結します。複雑さは攻撃対象領域を広げ、予期せぬ副作用の可能性を高める。ここでもETCと同じ方向を向いています。
命名(トピック44)
ストループ効果(色名と文字色が食い違うと読みにくくなる現象)を根拠に、脳は書かれた言葉を優先的に処理するため、名前は相応の重みを持つ、と論じます。
指針は「コードにおける役割で名前をつける」。何かを作るたびに立ち止まり、「これを作る動機は何か」を問う。適切な名前が出てこないとき、そもそもやろうとしていることが意味をなしていない、と気づくことが多い——というのは非常に実感のある指摘でした。
// 何をするか、ではなく、なぜそうするか
deductPercent(amount) // amountは絶対値?パーセント?
applyDiscount(percent) // 意図が明確
第8章 プロジェクト開始前
要件の落とし穴(トピック45)
「要件収集(gathering)」という言葉自体への批判から始まります。この語は要件が既に存在していて、拾い集めればよいことを含意しますが、現実は違う。要件は思い込み・誤解・政治的思惑の層の下に埋もれており、そもそも存在しないことも多い。
誰も自分が何を望んでいるのか正確には知らない(ヒント75)
プログラマーは人々が何を望んでいるかを理解するのを助ける(ヒント76)
クライアントが持ち込む最初のニーズ表明は要件ではなく、探求への招待状である、という捉え方が提示されます。
不可能なパズルを解く(トピック46)
ゴルディアスの結び目の逸話から。パズルが解けない原因は、想像上の制約を本物の制約だと思い込んでいることにあります。
枠の外で考えるのではなく、枠を見つける(ヒント81)
「Think outside the box」という決まり文句への修正提案です。箱=制約の境界だとすれば、重要なのは箱を出ることではなく箱がどこにあるかを正確に知ることで、それは思っているよりずっと大きい。
協力して働く(トピック47)
コンウェイの法則が扱われます。
システムを設計する組織は、その組織のコミュニケーション構造を写した設計を作らざるを得ない
著者はこれを「制約」としてだけでなく、逆向きにも使えるものとして提示します。つまり、コードに欲しい形に合わせてチームを意図的に構成できる。地理的に分散したチームはよりモジュール化・分散化されたソフトウェアを好む傾向がある、と。
そして最も重要なのは、ユーザーを巻き込んだチームはその関与が反映されたソフトウェアを作り、巻き込まないチームもそれを反映する、という一文です。
アジリティの本質(トピック48)
アジャイルは名詞ではない。アジャイルとは物事のやり方である(ヒント83)
「これをやればアジャイルになれる」と言う人は、定義上間違っている、と切り捨てます。
アジリティとは変化への対応、出発後に遭遇する未知への対応であり、走るガゼルが一直線に進まないのと同じで、事前に定められた単一の計画などありえない。マニフェストの4つの価値のうち3つはフィードバックの収集と応答についてのものだ、という読み解きが示されます。
第9章 実用的なプロジェクト
実践的なチーム(トピック49)
小規模で安定したチームを維持する(ヒント84)
「50人はチームではなく群衆である」。10〜12人程度で、入れ替わりが少なく、互いを信頼できる単位。コミュニケーション経路はメンバー数の2乗に比例して増えるため、規模が大きくなるほどコミュニケーションは破綻する、と脚注で補足されます。
「品質責任者」というロールへの批判が鋭い。品質は全員の個々の貢献からしか生まれず、後付けできないからです。
ココナッツだけではダメ(トピック50)
カーゴ・カルトの寓話です。滑走路と管制塔を蔦とヤシの葉で再現しても飛行機は来ない。形式だけを真似て中身を真似ていないから。
流行ではなく、効果のあることをやる(ヒント87)
Spotify、Netflix、Stripe、GitLabのやり方を輸入する前に、文脈——市場、制約、専門性、組織規模、経営、文化、ユーザー——が同じかを問え、という指摘です。
では何が効くのかをどう知るのか。答えは「試してみること」。小さく試し、機能する部分を残し、それ以外は無駄として切り捨てる。
実用的なスターターキット(トピック51)
方法論・言語・技術スタックを問わず、あらゆるチームに必要な3本柱として提示されます。
| 柱 | 内容 |
|---|---|
| バージョン管理 | ビルドに必要なすべてを管理下に置く |
| 回帰テスト | 容赦なく継続的に |
| 完全自動化 | 手作業は一貫性を偶然に委ねる |
バージョン管理でビルド・テスト・リリースを駆動する(ヒント89)
コミット/プッシュがビルドとテストをトリガーし、タグがリリースを指定する。ビルドマシンは**使い捨て(ephemeral)**として扱い、オフィスの隅にある「触るのが怖い神聖なマシン」を廃する。この記述が2019年時点で書かれている点に、初版からの20年が現れています。
ユーザーを喜ばせる(トピック52)
コードを提供するだけでなく、ユーザーを喜ばせる(ヒント96)
ユーザーの期待を掘り起こす質問が一つだけ示されます。
このプロジェクトが終わって1か月後(あるいは1年後)に、我々全員が成功したとどうやって分かるのでしょうか?
レコメンド改善のプロジェクトが実際には顧客維持率で評価される、DB統合プロジェクトがデータ品質やコスト削減で評価される——といったズレが、この問いで浮かび上がります。ソフトウェアは目的ではなく手段だ、という当然のことを、当然として運用するための質問です。
肩書きが何であれ、真に重要なのは「問題解決者」である、と結ばれます。
高慢と偏見(トピック53)
作品に署名する(ヒント97)
匿名性は、ずさんさ・ミス・怠惰・質の悪いコードの温床になる。ただしコードは所有されるべきだが、必ずしも個人が所有する必要はない、という但し書きが付きます。共同所有を採るなら、匿名化を防ぐための追加のプラクティス(ペアプログラミング等)が必要だ、と。
道徳の羅針盤
最後に、倫理についての2つの問いが置かれます。
- ユーザーを保護したか?
- これを自分で使うか?
まず、害を与えないこと(ヒント98)
卑劣な奴らを助長するな(ヒント99)
必要なデータだけを保存しているか、個人情報を暗号化しているか、自動化された機器に手動制御の逃げ道を用意しているか。「あらゆる結果を列挙してユーザーを守ろうと努めた」と正直に言えないなら、事態が悪化したときに責任の一端はこちらにある、という厳しい記述で締められています。
設計・アーキテクチャ視点での要点整理
全体を、設計判断のチェックリストとして再構成すると次のようになります。
| 観点 | 問い | 対応トピック |
|---|---|---|
| 変更容易性 | 今やったことは、変更を容易にしたか? | ETC(8) |
| 知識の重複 | この側面を変えるとき、複数箇所を複数形式で直すか? | DRY(9) |
| 独立性 | 一方の変更が他方に影響するか? | 直交性(10) |
| 可逆性 | この決定は覆せるか。覆せないなら本当に今決めるべきか? | 可逆性(11) |
| 不確実性 | 最もリスクの高い部分から着手しているか? | 曳光弾(12) |
| 結合 | 変更の影響範囲を、変更前に説明できるか? | デカップリング(28) |
| データフロー | これは何から何への変換か? | 変換(30) |
| 継承 | 共有したいのはコードか、型か、振る舞いか? | 相続税(31) |
| 時間 | この順序は問題が要求しているか、実装の都合か? | 時間的結合(33) |
| 状態 | 可変データを2箇所以上から参照していないか? | 共有状態(34) |
| テスト | このコードは、書く前にテストの形を描けるか? | テスト(41) |
| 命名 | 「何をするか」ではなく「なぜそうするか」の名前か? | 命名(44) |
| 文脈 | この手法を選んだのは効果があるからか、流行だからか? | カーゴカルト(50) |
特に第2版で読む価値が高い3トピック
初版を読んだことがある人向けに、差分として特に価値が高いと感じた箇所を挙げます。
- トピック9「DRY」 — 「コピペするな」から「知識と意図の重複」への再定義。最も誤解されてきた原則の公式訂正です
- トピック30「プログラミングの変革」 — オブジェクトではなくデータの変換としてプログラムを捉え直す視点。関数型の考え方を、言語に依存しない形で設計に持ち込む道筋
- トピック41「テスト」 — 「テストはバグを見つけるためではない」。テストを検証手段から設計フィードバック装置へと位置づけ直しています
おわりに
読み終えて残るのは、個々のTipsよりも判断の軸が一本通っているという感触でした。
- 良い設計とは変更しやすい設計である(ETC)
- 変更しやすさを損なう最大の要因は結合である
- 結合には、モジュール間の結合と時間的な結合の両方がある
- 知識の重複(DRY違反)は、離れた場所を同時に変えることを強制するため結合の一種である
- 継承もまた結合であり、無条件に使ってよいものではない
- テストが設計に効くのは、テスト容易性が結合の低さと同義だからである
こう並べると、本書のほぼ全編が「結合をどう減らすか」の各論として読めます。「達人プログラマー」というタイトルから連想される職人芸の話ではなく、極めて一貫した設計論の書だというのが読後の印象です。
一方で、本書は答えを与える本ではありません。ETCが「ルールではなく価値」と繰り返されるように、判断そのものは常に読者側に残されます。トピック50の「試してみてください」という一文が象徴的です。
初版を読んだことがある人にも、20年分の差分だけで再読の価値があります。まだ読んでいない人には、章単位でつまみ読みできる構成なので、第2章(設計原則)と第5章(柔軟性)から入るのがおすすめです。
参考
- Andrew Hunt, David Thomas 『The Pragmatic Programmer: your journey to mastery, 20th Anniversary Edition, 2nd Edition』Addison-Wesley, 2019
- 邦訳:『達人プログラマー ―熟達に向けたあなたの旅― 第2版』オーム社, 2020