低凝集:役割の違う処理が1つに混ざっている状態
更新、通知、ログ記録、権限判定、表示整形のような処理が1つの箱に混ざると、部品の主目的がぼやけます。説明しにくく、どこまで直せばよいかの境界も曖昧になります。
この状態では、「ユーザー更新の関数」と説明したつもりでも、中で通知や表示整形までやっているため、実際には何でも屋になっています。差分レビューでも、どこが本題でどこがついでの処理かが読み取りにくくなります。
凝集度(Cohesion)とは、1つのモジュールや関数の中に、共通の目的に関わる処理がどれだけ自然にまとまっているかを表す指標です。凝集度が高いほど「この部品は何をするものか」が明確になり、コードの理解しやすさ、変更の影響範囲の特定、テストのしやすさが大きく向上します。
逆に凝集度が低い設計だと、更新・通知・ログ・権限チェックのような性質の違う処理が1か所に混ざり、AIにコード生成や修正を依頼したときに文脈を読み違えやすくなります。私自身も、凝集度の低い関数をAIにリファクタリングさせた際、修正漏れや意図しない変更が増えた経験があり、これはAI時代の見えにくい負債だと感じました。
この記事では、凝集度の基本から、よく混同される「結合度」との違い、高凝集と低凝集の見分け方、そしてAIを味方につけるための高凝集設計のコツまでを、図とサンプルコードで丁寧に整理します。
凝集度とは、関数・クラス・モジュールの中身が、1つの目的に向かってどれだけ自然にまとまっているかを見る指標です。見るべきなのはコードの長さではなく、役割の揃い方です。
高凝集な部品は、「これは何をする部品か」を一言で説明できます。逆に低凝集な部品は、更新・通知・ログ・権限判定・表示整形のような別の関心ごとが混ざりやすく、読み手にもAIにも意図が伝わりにくくなります。
重要なのは、凝集度が高いほど理解しやすく、変更箇所を絞りやすく、テスト観点も切り分けやすくなることです。AIで実装や改修を進める今は、この視点が特に大切で、AIに修正を頼むときも、「どこを触ってよいか」を狭く保てるので、修正漏れや余計な変更を減らしやすくなります。
この2つは一緒に語られがちですが、見ている場所が違います。先に比較表で並べると、検索意図で多い「凝集度と結合度の違い」が整理しやすくなります。
「どちらか一方が良ければいい」のではなく、両方のバランスを整えるのが、AIにメンテナンスしてもらいやすいコードにする秘訣です。
| 観点 | 凝集度 | 結合度 |
|---|---|---|
| 見る対象 | 関数・クラス・モジュールの内側 | 部品どうしの外側の関係 |
| 何を見るか | 1つの責務に集中しているか | 依存の強さと影響範囲の広さ |
| 悪い状態 | 更新・通知・ログなどが1か所に混ざる | 1か所の変更が別モジュールへ波及する |
| 良い状態 | その部品が何者かを一言で説明できる | 必要最小限の依存で差し替えや修正がしやすい |
| AI時代の見方 | AIが主目的を読み取りやすいか | AIが安全に触れる境界を保てているか |
凝集度は、関数・クラス・モジュールの内側を見ます。1つの責務に集中しているか、目的が揃っているか、つまり「その部品は何者か」が明確かどうかを判断する視点です。
一方の結合度は、部品同士の外側の関係を見ます。依存が強すぎると変更の影響範囲が広がりやすくなり、修正対象の切り分けも難しくなります。つまり、凝集度とは見る場所が違います。
理想は、内側はよくまとまり、外側とはつながりすぎない状態です。高凝集でも外部依存が重いと改修はつらく、疎結合でも中身がごちゃ混ぜなら使いにくいままです。内側を整えるのが凝集度、外側を整えるのが結合度と考えると、役割の違いがつかみやすくなります。
図で見ると、低凝集は「何でも屋」、高凝集は「役割ごとに名前が付けやすい部品」です。図だけでなく本文でも要点を言い換えると、低凝集は修正対象の境界が曖昧で、高凝集は目的ごとの境界がはっきりしています。
更新、通知、ログ記録、権限判定、表示整形のような処理が1つの箱に混ざると、部品の主目的がぼやけます。説明しにくく、どこまで直せばよいかの境界も曖昧になります。
この状態では、「ユーザー更新の関数」と説明したつもりでも、中で通知や表示整形までやっているため、実際には何でも屋になっています。差分レビューでも、どこが本題でどこがついでの処理かが読み取りにくくなります。
更新なら更新、通知なら通知、と役割ごとに箱が分かれていると、1つひとつの目的が説明しやすくなります。差分レビューもしやすく、AIに依頼する単位としても扱いやすくなります。
役割が分かれていれば、「この差分は通知だけ」「このテストは更新だけ」と切り分けやすくなります。高凝集は、説明のしやすさそのものが品質になる設計です。
図解の違いを、実際のコードに落とすともっと分かりやすくなります。ここでは Before / After で、責務が混ざった関数と、役割ごとに分けた例を見比べます。
次の例では、DB更新、メール送信、監査ログ、表示用整形が1つの関数に詰め込まれています。何をテストすればいいか分かりにくく、失敗時の切り分けもしにくく、AIも一度に広い範囲へ手を出しやすくなります。
// Before: 低凝集な例
function updateUserProfile(PDO $db, array $input, Mailer $mailer): array
{
$stmt = $db->prepare('UPDATE users SET name = ?, email = ? WHERE id = ?');
$stmt->execute([$input['name'], $input['email'], $input['id']]);
$mailer->send(
$input['email'],
'プロフィールを更新しました',
$input['name'] . 'さん、更新が完了しました。'
);
file_put_contents(
__DIR__ . '/audit.log',
date('c') . ' user updated: ' . $input['id'] . PHP_EOL,
FILE_APPEND
);
return [
'id' => (int) $input['id'],
'label' => $input['name'] . ' <' . $input['email'] . '>',
'message' => '更新完了',
];
}
この関数は「更新」だけに見えて、通知やログや表示整形まで抱えています。名前と中身がずれ始めると、人間のレビューでもAIの修正でも、どこが本来の責務かを見失いやすくなります。
分割後は、updateUser()、notifyUser()、writeAuditLog()、formatResponse() のように役割を名前で表せます。個別に直せて、テストも切りやすく、AIへの依頼単位としても安全です。
// After: 高凝集な例
function updateUser(PDO $db, array $input): array
{
$stmt = $db->prepare('UPDATE users SET name = ?, email = ? WHERE id = ?');
$stmt->execute([$input['name'], $input['email'], $input['id']]);
return [
'id' => (int) $input['id'],
'name' => $input['name'],
'email' => $input['email'],
];
}
function notifyUser(Mailer $mailer, array $user): void
{
$mailer->send(
$user['email'],
'プロフィールを更新しました',
$user['name'] . 'さん、更新が完了しました。'
);
}
function writeAuditLog(int $userId): void
{
file_put_contents(
__DIR__ . '/audit.log',
date('c') . ' user updated: ' . $userId . PHP_EOL,
FILE_APPEND
);
}
function formatResponse(array $user): array
{
return [
'id' => $user['id'],
'label' => $user['name'] . ' <' . $user['email'] . '>',
'message' => '更新完了',
];
}
違いは、関数の短さよりも役割の一貫性です。高凝集な分割では、名前だけで責務が分かり、障害時も「更新で失敗したのか」「通知だけ失敗したのか」を切り分けやすくなります。
1つの関数に目的が複数あると、AIは主目的を取り違えやすくなります。「ユーザー更新を直して」と頼んだつもりでも、通知やログや権限処理まで含む関数だと、余計な変更を混ぜやすくなります。説明が長くなり、プロンプトの純度も落ちます。
低凝集なコードは、どこまで影響するかを読み取りにくくします。正常系だけ直って副作用側のテストが漏れたり、表示整形だけ壊れたりと、境界が曖昧なまま不具合が残りやすくなります。
人間同士で説明しにくいコードは、AIにも通じにくいコードです。レビューコメントが「なんとなく責務が広い」のように抽象的になりやすく、結果として「一旦触らない」が増えて負債化しやすくなります。
まずは1関数1目的を意識します。「更新」「通知」「記録」「整形」を同じ場所に混ぜないだけでも、凝集度はかなり改善します。迷ったら、関数名を動詞で言い換えたときに目的が1つに絞れるかを確認すると分かりやすいです。
計算と外部書き込み、判定ロジックと送信ロジックを分けると、テストしやすさが大きく上がります。純粋関数に寄せられる部分は寄せ、副作用は境界へ追い出すと、何を確認すべきかがはっきりします。
名前と中身が一致していること、引数・返り値・副作用が読み取れることが大事です。人間にもAIにも「ここは何をしてよい場所か」が伝わる設計ほど、改修依頼やレビュー依頼が安全になります。
一つの依頼で一つの責務だけ触れる状態が理想です。高凝集なら説明文が短くて済み、修正意図もブレにくくなります。逆に低凝集だと、「この関数のうち更新だけ直して、通知は変えないで」のように注意書きが増えていきます。
自動修正やツール呼び出しの対象として安全かどうかも重要です。関数単位で変更しても事故が広がりにくいなら、AIエージェントに渡す単位として扱いやすくなります。差分確認のしやすさも、この安全性に直結します。
高凝集だとテストケースを切りやすく、期待値も説明しやすくなります。レビューコメントも「この関数は更新だけに寄せたい」のように具体化できます。低凝集だとテスト観点が散らばり、レビューも抽象的になりがちです。
最初は「長い関数=悪い」と思っていました。ところが実際には、短くても責務が混ざっている関数は普通に低凝集な関数でした。数行しかなくても、更新と通知を同時にやっていれば、見るべき観点も修正理由も2つになります。
失敗が目立ったのは、そういう関数をAIにまとめてリファクタリングさせたときです。意図しない変更や修正漏れが出て、直したい本題よりも副作用の差分確認に時間を取られました。結果として、AIに任せて速くなるはずの作業なのに、むしろ人間の確認コストが増えるという本末転倒な状態になってしまいました。
原因は、AIの精度そのものというより、こちらが渡していた「責務の境界」が曖昧だったことにありました。そこで、更新、通知、ログのように役割ごとに分けてから、改めてAIに依頼するようにしました。すると、修正の精度が上がり、テスト観点も切りやすくなり、人間のレビューもかなり速くなりました。今は、長さそのものより「同じ理由で変わる処理だけが集まっているか」を見るようにしています。
教訓:
AIは「文脈(空気)」を読むのが得意そうに見えても、境界が曖昧なコードでは簡単に迷います。だからこそ、人間が先に「どこまでが1つの役割か」をはっきり引いておくことが大切です。関数の短さにこだわる前に、まずは「1つの目的に素直にまとまっているか」を確認しましょう。高凝集なコードは、AIにとっても人間にとっても、意図が伝わりやすい指示書になります!
原則としては高いほうが良いです。ただし、細かく分けすぎて呼び出しの流れが追いにくくなると、本末転倒になることもあります。最終的には「理解しやすいか」「使いやすいか」で判断するのが実務的です。
いいえ。短くても責務が2つ以上あれば低凝集です。大切なのは行数ではなく、目的が一貫しているかどうかです。
まずは凝集度を見ると考えやすいです。内側の責務が整うと、外側の依存関係も整理しやすくなるからです。ただし実務では、最終的に両方をセットで見るのが安全です。
凝集度は、部品の中身を見て「この関数やクラスは何を担当するのか」を確かめる指標です。高凝集であるほど、理解・変更・テストがしやすくなり、AIへの修正依頼や自動化の安全性にも直結します。
だから実務では、高凝集・疎結合が効きます。内側の役割を整え、外側の依存を絞ることで、人間にもAIにも扱いやすいコードに近づけます。
更新・通知・ログ・整形など、性質の違う処理が1か所に混ざっているなら、凝集度が下がっているサインです。
名前は「更新」なのに通知や保存までやっている、というズレがあると、人間にもAIにも誤解されやすくなります。
「この部品は何を確認すれば正しいのか」がすぐ言えないなら、責務が混ざっている可能性があります。
1つの部品に複数の責務があると、AIが文脈を読み違えて、余計な場所まで修正しやすくなります。
計算と保存、判定と送信のような処理が混ざっていると、どこまでが影響範囲か見えにくくなります。
ぱっと見で役割が伝わる部品は高凝集になりやすく、レビューも改修もスムーズです。
あとから機能追加やAIリファクタリングをするとき、素直に分けられる形なら、高凝集な設計に近づいています。
以下の関数またはクラスについて、凝集度の観点でレビューしてください。
見てほしい観点:
1. このコードの責務を列挙してください
2. 責務が複数ある場合、どこが低凝集か指摘してください
3. 入出力・副作用・テスト観点を分けて整理してください
出力形式:
- 主な責務
- 低凝集と判断した理由
- 副作用
- テスト観点
- 凝集度の総評
コード:
[ここにコードを貼る]以下のコードについて、凝集度の観点で診断し、高凝集に近づけるリファクタ案を提案してください。
依頼内容:
1. この関数の責務を列挙してください
2. 責務が複数ある場合は分割案を出してください
3. 副作用・入出力・テスト観点を分けて整理してください
4. 関数名や境界の改善案も提案してください
出力形式:
- 現在の責務一覧
- 低凝集な箇所
- 分割後の関数案
- 各関数の役割
- リファクタ時の注意点
- 追加すべきテスト
コード:
[ここにコードを貼る]経験:Webアプリ・業務システム
得意:PHP・JavaScript・MySQL・CSS
制作・運用中:フォーム生成基盤・クイズ学習プラットフォーム・htmx逆引きレシピ 等
AI時代のエンジニアタイプ診断:CSPF/とろとろみかん
詳しいプロフィールはこちら! もちもちみかんのプロフィール