目次を公開!『良いコードの道しるべ』で何を学べるか?
こんにちは、森 篤史(Mori Atsushi)です。
このたび、『良いコードの道しるべ - 変化に強いソフトウェアを作る原則と実践』を2025年5月29日に発売することになりました。
前回の記事では、この本を執筆するに至った経緯や、本に込めた思いについて書きました。
今回は、本書の構成を章ごと・節ごとに詳しくご紹介します。
書き手としては、どの章も丁寧に気持ちを込めて書きました。
少しでも気になるところがあれば、ぜひ手に取ってもらえると嬉しいです。
※ 内容は執筆中のため、一部変更になる場合がありますのでご了承ください。
1 なぜ良いコードを書くのか
「良いコード」とは一体どういうものなのでしょうか?
なぜ良いコードを書く必要があるのでしょうか?
ソフトウェアの価値に着目して考えます。
1.1 ソフトウェアの価値
ソフトウェアの保守性がどういったものなのか、そして保守性がどうソフトウェアの価値に貢献するのかについて説明します。
そのうえで、本書における「良いコード」を定義します。
1.2 保守性とスピード
開発スピードを上げるために保守性を犠牲にする——この矛盾がなぜ生じるのでしょうか。
「速さ」と「保守性」がトレードオフの関係でないこと、保守性を上げることで開発のスピードが向上できることを説明しています。
1.3 技術的負債の発生と解消
保守性が低くなるのはなぜなのか?
この節では、技術的負債を発生するタイミングによって「意図的な負債」、「変化による負債」、「学びによる負債」の3つに分解し、負債とどう向き合うべきかを明らかにします。
1.4 良いコードを書くために
単に時間をかけてコードを書いても、「良いコード」にたどり着くことは難しいでしょう。
知識と経験をどのように積み上げれば「良いコード」に近づけるのか、この節では学び続ける姿勢の重要性と、その先にある成長の可能性を伝えます。
2 動くコードから意図の伝わるコードへ
動くコードを書くことは実はさほど難しくありません。
大事なのは書いたコードを他の人や将来の自分が読んで正しく理解できることです。
2.1 意図を表現する
動くコードより“伝わるコード”がなぜ重要なのか、どういったコードが正しく伝わるのかについて具体例を通じて解説します。
短くてスマートに見えるコードが、必ずしも良いコードではありません。
2.2 名前で伝える
コードを書く上で「命名」を避けて通ることはできません。
この節では、曖昧さを避けて意図を明確に伝えるための具体的な工夫やルールを紹介します。
適切な命名はコードを格段に読みやすくします。
2.3 コメントで補足する
コメントがコードの書き起こしになっていては意味がありません。
この節では、どういったコメントが役に立つのか、効果的なコメントの書き方を解説します。
2.4 ドメイン知識をコードで表現する
ドメイン駆動設計の根本的な考え方に触れ、現実世界の概念(ドメイン)をコードに落とし込む方法について紹介します。
ドメイン知識を正しくコードに落とし込むことで、理解しやすく、変更しやすい設計が実現できます。
3 大きな問題は分割して考えよう
人は一度に多くのことを考えたり、覚えたりすることはできません。
そのため、巨大で複雑な問題に取り組むときは、小さな問題に分けて1つずつ取り組むと良いでしょう。
コードも適切な粒度で分割することで、より大きな課題を解決することができます。
3.1 関数やクラスを分ける
まずは関数やクラスを使ってコードを分割する方法について具体例とともに紹介します。
関心ごと、責任ごとに分離することで、変更しやすく、また再利用可能な構造になります。
3.2 詳細を読まなくても使えるようにする
分割したコードは、中身を読まなくても使えるように設計することが大事です。
この節では、どうやってそれを実現するか、気をつけるべき点について紹介します。
3.3 コードの複雑度を計測する
コードを分割すべきかどうかを判断するうえで、各要素の複雑度は1つの指標になります。
どのような指標があるのかについて紹介します。
3.4 凝集度を高める
「凝集度」は、1つの関数やクラス内のコードがどの程度、密接に結びついているかを示す言葉です。
凝集度が高い関数やクラスは、目的が明確で、読みやすくて壊れにくくなります。
関数やクラスの凝集度をどのように高めればよいのか解説します。
4 コードの分類術
関数やクラスは大きくなりすぎないよう、目的ごとに分割すべきです。
一方、関数やクラスを細かく分割していくと、それらを管理する負担が大きくなります。
関数やクラスが増えてきたら、それらをグルーピングしながら整理しましょう。
4.1 ディレクトリ単位での整理
コードが増えてきたらまずはディレクトリ単位で分割してみましょう。
ディレクトリをどのように分けて配置するかについて解説します。
4.2 モジュール単位の整理
大規模プロジェクトでは、ディレクトリ単位だけではなく、モジュール単位でコードを管理することを検討しても良いでしょう。
モジュールとして分離することで、各チームが並列して作業できるようになったり、変更の影響範囲が明確になります。
モジュールを分離する際に意識すべき原則について紹介しています。
5 絡まった依存関係を解きほぐせ
関数やクラスを分割したり、ディレクトリやモジュールを使ってグルーピングを行うと、他のクラスを使うクラスや、他のモジュールに使われるモジュールといったように、要素間に関係性が生じます。
この使う、使われるといった関係性を依存関係と呼びます。
多くの関数やクラス、ディレクトリ、モジュールが存在したとしても、依存関係はシンプルに保つ必要があります。
5.1 依存を意識する
依存とはそもそも何か、そして依存することで何が生じるのかについて解説します。
何事も、なにかに依存しすぎることはよくありません。
5.2 依存の方向
要素間の依存の方向をどのように決定すべきかについて解説します。
依存の方向を適切に制御することで、システム全体の安定性や再利用性が向上します。
5.3 抽象に依存させる
具象ではなく抽象(インターフェース)に依存する手法について解説します。
しかし、常にインターフェースを作成すれば良いわけではありません。 メリットとデメリットについて説明したうえで、どのように判断すべきかを考えます。
5.4 結合度を低くする
要素同士が依存していると一言でいっても、その依存度合いはさまざまです。
どのくらい依存しているかを表す指標として、「結合度」というものがあります。 結合度のレベルを知ったうえで、要素間の適切な結合度について考えます。
6 良いコードを書く原則・教訓
これまで多くの人が良いコードについて考え、さまざまな原則や教訓を残してくれました。
ただし、多くの原則がいつでも適用可能なわけではありません。
それぞれの原則が意図していること、背景を深く理解し、どの原則をどのように適用していくか取捨選択することが求められます。
6.1 KISS原則: シンプルに保つ
コードは、できるだけ理解しやすく単純にするほど保守も拡張も楽になります。
最初のアイデアを思いついたら、他にも解決策がないか考え、複数の選択肢の中から一番シンプルなものを選んで下さい。
6.2 YAGNI原則: 必要になるまで実装しない
「いつか必要になるかも」と過剰に実装すると、コードの複雑さに悩まされます。
必要になった段階で最低限のものを実装するのが、賢い選択です。
この節では、先回りしすぎた実装がどんな問題を引き起こすのか、具体例を交えて紹介します。
6.3 DRY原則: 重複する知識を減らす
同じ情報やコードがあちこちにあると、変更時に手間もミスも増えてしまいます。
一方、異なるものを無理に共通化すると余計に複雑になります。
“本当に同じものか”を見極めることが、重要です。
6.4 車輪の再発明: 同じものを作らない
すでにあるコードやライブラリを活用できれば、時間も労力も節約できます。
不要なコードを書かないことが、保守性向上のコツです。
6.5 ハンマーしか持っていなければ、すべてが釘のように見える
解決策を一つしか知らなければ、本来合わない場所でもその解決策を無理やり当てはめがちです。
複数の手段を幅広く知り、状況に合わせて使い分けることが大切になります。
6.6 銀の弾丸などない
どんなに優れた技術やツールも、ソフトウェアの本質的な複雑さを一気に解消できるわけではありません。
便利な言語やフレームワークが増えてきた現代でこそ、本質的課題とどう向き合うかが問われてきます。
7 うっかりミスを防ぐために
どんなに気をつけていても、人間は必ずミスを起こします。
コードを書く際も、間違ったコードを書いたり、実装が抜け漏れることで、様々なバグを引き起こします。
そのため、できるだけ間違いにくいコード、またたとえ間違いを犯してもすぐに気がつくコードを書くことが重要です。
7.1 マジックナンバーを避ける
コード内のマジックナンバーを定数や列挙型に置き換えることで、意図が明確になり保守性が向上します。
また、一見すると発見できない、隠されたマジックナンバーについても紹介します。
7.2 型による制限を有効活用する
型を正しく活用することで、予期せぬエラーをコンパイル時に防ぎ、安全なコードを構築できます。
カスタム型や列挙型を使って、より信頼性の高いソフトウェアを目指します。
7.3 変更できないデータを使う
イミュータブルなデータは、一度生成された後に値が変化しないため、予期せぬバグを減らすのに役立ちます。
しかし、全てをイミュータブルで設計することは容易ではありません。
イミュータブルとミュータブルをどのように使い分けるべきかについて解説します。
7.4 データを一箇所で管理する
同じデータを複数箇所で持つと、一貫性が失われたり、修正漏れが発生しやすくなります。
Single Source of Truth(信頼できる唯一の情報源)を意識して、データの重複を避ける設計が重要です。
7.5 状態の変更と情報の取得を分ける
状態を変更する処理と情報を取得する処理を別の関数に分けることで、意図がはっきりし可読性と安全性が向上します。
一方、それらをまとめて行うべきケースもあります。 どのように判断すべきかについて解説します。
8 コードは書くよりも変更するほうが難しい
一度書かれたコードも、仕様を変更したり新しい機能を追加するために、コードを変更することが多くあります。
また、見つかった不具合に対応する必要もあるでしょう。
コードを変更するときは、最初にコードを書くとき以上に読みにくいコードが生まれやすくなっています。
8.1 ボーイスカウトルール: 来たときよりも美しく
コードを修正するタイミングで、その周辺のコードに改善できる点がないか確認してください。
細かい改善を積み重ねることで、読みやすく、堅牢なソフトウェアを構築することができます。
8.2 ゼロベースで コードを考える
コードを変更する際、変更時の差分を小さくすることを意識して、歪なコードになっていませんか?
常に、今、一から最善のコードを書くとしたらどのようなコードになるかをイメージすべきです。
具体例をもとに、どのようにコードを変更すれば良いのか、注意点を解説しています。
8.3 少しずつ変更する
一気にコードを変えるとその分リスクが高くなります。
この節では、どのようにタスクを分割し、着実に変更を加えていくのか解説します。
8.4 いらなくなったコードを削除する
使われないコードを残すと、無駄な修正やバグを呼び込んでしまいます。
いらなくなったコードは削除しましょう。
9 アーキテクチャを考える
アーキテクチャとは構造や設計を意味する言葉です。
ソフトウェア開発では、ソフトウェアの全体に対する設計を表す際に使われます。
ソフトウェアが大規模になってくると、全体として方針やルールが決まっているほうがやりやすいことがあります。
9.1 レイヤを整理する
大きなソフトウェアを開発する際、役割ごとに「レイヤ」を整理することで、コードの見通しがよくなり、変更にも強くなります。
どのようにレイヤを分割していくのか、各ソフトウェアに寄り添ったアーキテクチャを考えます。
9.2 レイヤ構成のアイデア
これまで、汎用的なレイヤ構成のアイデアやパターンが数多く提唱されています。
本書では、「レイヤードアーキテクチャ」と「クリーンアーキテクチャ」の2つの基本的な考え方について紹介します。
9.3 機能に着目して分割する
技術的な役割で分割するだけでなく、機能ごとに明確に分割することも可能です。
アプリケーションの例をもとに、どのような分割が可能かを解説します。
9.4 アーキテクチャを考えるということ
アーキテクチャは何のためにあるのでしょうか。
アーキテクチャとどう向き合うべきか、その理想について述べています。
10 壊さないための自動化テスト
コードを書いたら動作確認をします。
コードを変えたらまた動作確認をします。 コードが複雑になっていくと、次第に動作確認が大変になります。
可能な限り動作確認は自動化して楽をしましょう。
10.1 手動テストの限界と自動化
なぜテストを自動化する必要があるのでしょうか。
手動テストのデメリットから、その必要性を解説します。
10.2 自動化テストの種類
自動化テストには単体テスト、結合テスト、E2Eテストがありますが、それぞれ役割が異なります。
まだ、手動テストが有効な場面も存在します。
それぞれの効果的な使い分けについて説明します。
10.3 単体テストを書く
単体テストの書き方を、具体的な例とともに基本から解説します。
陥りやすいミスや、注意すべき点についても説明しています。
10.4 依存しているコードを偽物に差し替える
依存しているクラスや外部サービスを「偽物」に差し替え、テストをやりやすくするテクニックを紹介します。
一方、偽物を使う際に生じるデメリットについても述べ、使い分けについても解説します。
10.5 テストカバレッジを計測する
コードカバレッジは、テストがどの程度書かれているのかを知るのに役立ちます。
一方、カバレッジを過信することも問題です。 どのように活用すべきかについて解説します。
10.6 結合テスト / E2Eテスト
結合テストとE2Eテストでは、単体テストと比較してどのような点を気をつけるべきか、簡単に解説しています。
11 チームで書く良いコード
一人でも様々なソフトウェアを作ることは可能ですが、チームでコードを書くことで新しい世界が広がります。
さらに大きなソフトウェアを作ることができるかもしれませんし、新しいアイデアが生まれるかもしれません。
ここでは筆者の経験も踏まえ、チームでの開発を円滑に進める方法について紹介します。
11.1 Git でバージョン管理
Gitはチームで開発するうえで必要不可欠です。
この節では、Gitを効果的に使う方法について解説します。
11.2 コードレビューを行う
コードレビューは、単なるバグ発見だけでなく、チームの成長や協力を促す重要な手段です。
レビューを依頼する側、レビューを行う側それぞれで気をつけるべき点について、丁寧に解説します。
11.3 コーディング規約を作る
同じことを行うコードでも、複数の書き方ができることがあります。
人によって書き方が異なると混乱を招くので、チームで統一した規約を作ることを検討しましょう。
11.4 自動でチェックする
テストやコーディング規約など、自動でチェックできる項目に関しては自動でチェックすべきです。
11.5 設計書を作る
設計書は開発者間の情報共有を円滑にし、プロジェクト全体の効率化や品質向上に役立てることができます。
この節では「Design Doc」という手法とその活用方法について紹介します。


コメント