イントロダクション:AIで速く作れる時代ほど、壊れるのも速い
「検証だけ直したはずなのに、保存や通知まで壊れた」。
AIリファクタリングで起きやすい事故の多くは、1つのクラスに責務が混ざっていることが原因です。
単一責任の原則(SRP)は、人間とAIの両方に“ここまでが担当範囲”を示す境界線です。
特にカプセル化と組み合わせると、触ってよい入口と変更してよい範囲が揃い、事故率が下がります。
1. SRPとは?責任=変更理由で考える
SRP(Single Responsibility Principle)は、
「1つのモジュールは、1つの変更理由だけを持つべき」という原則です。
- 責任の見分け方:「何の都合で変更するか」を理由で分ける。
- 実務での言い換え:「このクラスは〇〇だけ担当する」と1文で言える状態を作る。
メソッド数よりも、変更理由が混ざっていないかを見るほうが、SRP判断では実用的です。
2. AI時代にSRPが効く3つの理由
2-1. プロンプトの焦点を絞れる
SRPを守ると、AIへの依頼が短くなります。
「OrderValidatorのメール形式チェックだけ修正して」と書ける状態は、
AIの迷いとハルシネーションを減らす設計そのものです。
2-2. AIリファクタリングの影響範囲を封じ込められる
1クラス1責任なら、AIが内部を大きく書き換えても被害はその責任範囲にとどまります。
SRPは、「Aを直したらBが壊れた」を防ぐ防火壁として効きます。
2-3. AIが実装しやすいモジュール粒度を作れる
神クラス(アンチパターン)化の兆候は、「AIが1回の説明で処理を言い切れない」状態です。
人間が責任で分割し、AIへ小分け発注する流れにすると、品質と速度が安定します。
要するに、設計(分割)は人間、実装(中身)はAIの役割分担がSRPの実務形です。
3. 悪い例:責務が混ざった神クラス(アンチパターン)
検証・保存・通知・表示整形が1つに詰まっている例です。変更理由が複数あり、AI修正で巻き込み事故が起きやすくなります。
Before:OrderManagerが何でも担当している
class OrderManager {
constructor(db, mailer) {
this.db = db;
this.mailer = mailer;
}
process(order) {
// 1) 検証責務
if (!order.email || !order.items || order.items.length === 0) {
throw new Error('invalid order');
}
// 2) 永続化責務
const saved = this.db.insert('orders', order);
// 3) 通知責務
this.mailer.send(order.email, '注文完了', 'ありがとうございます');
// 4) 表示整形責務
return {
id: saved.id,
message: `Order #${saved.id} completed`,
totalLabel: `${saved.total.toLocaleString()}円`
};
}
}
// AIに「メール本文だけ敬語にして」と依頼したら、
// process() 全体を触って検証や返却フォーマットまで変えてしまうリスクがある。
壊れ方:通知修正のつもりが、検証条件やレスポンス形式まで巻き込む。
責務境界がないため、影響範囲を事前に縛れません。
4. 良い例:責務ごとに分割して依頼境界を明確化
Validator / Repository / Notifier / Formatter に分けると、AI依頼の単位もそのまま分割できます。
After:1クラス1責任で変更理由を分離
class OrderValidator {
validate(order) {
if (!order.email || !order.items || order.items.length === 0) {
throw new Error('invalid order');
}
}
}
class OrderRepository {
constructor(db) { this.db = db; }
save(order) { return this.db.insert('orders', order); }
}
class OrderNotifier {
constructor(mailer) { this.mailer = mailer; }
sendCompleted(email) { this.mailer.send(email, '注文完了', 'ありがとうございます'); }
}
class OrderPresenter {
toViewModel(saved) {
return {
id: saved.id,
message: `Order #${saved.id} completed`,
totalLabel: `${saved.total.toLocaleString()}円`
};
}
}
class OrderService {
constructor(validator, repository, notifier, presenter) {
this.validator = validator;
this.repository = repository;
this.notifier = notifier;
this.presenter = presenter;
}
placeOrder(order) {
this.validator.validate(order);
const saved = this.repository.save(order);
this.notifier.sendCompleted(order.email);
return this.presenter.toViewModel(saved);
}
}
AI依頼の例:
「OrderValidator.validate の電話番号チェックだけ追加」
「OrderRepository.save をトランザクション対応」
といった小分け発注ができ、不要な巻き込みを防げます。
5. 境界の引き方:実務手順
- 変更理由を列挙する:「検証」「保存」「通知」「表示整形」など、理由単位で分解する。
- 責任ごとに入口を作る:各クラスに1つの主メソッドを置き、役割を1文で説明できる状態にする。
- 調整役を薄く置く:Serviceは流れの組み立てだけ担当し、個別ロジックを持たせない。
- AI依頼を責任単位で切る:「どのクラスの何だけ触るか」を必ず指定する。
- レビューで境界逸脱を止める:依頼外の責任まで変更していないかを最優先で確認する。
6. 図解:責務混在は燃え広がり、分割は防火壁になる
左は1つの箱に責務が混ざった状態、右は責務ごとに箱を分けて影響範囲を局所化した状態です。
責務混在(影響が広がる)
1か所をAIが修正すると、同じ箱の別責務まで巻き込みやすい
責務分割(影響を封じる)
修正対象だけAIに渡せるため、被害をその箱の中に閉じ込めやすい
この図で言いたいことはシンプルで、責務が混ざると影響範囲が読めなくなり、責務を分けると修正範囲を制御できるという点です。
7. AIへの発注テンプレ:責任単位で小分け依頼する
悪い依頼(広すぎる)
注文処理をいい感じに改善して。
必要なら設計も直して。
境界がなく、AIが複数責務を同時に触りやすい依頼です。
良い依頼(責任で限定)
対象: OrderValidator.validate のみ
目的: 電話番号の形式チェックを追加
制約: Repository / Notifier / Presenter は変更しない
出力: 変更理由と差分を箇条書きで提示
責任単位で区切ると、AIの提案精度とレビュー効率が上がります。
8. AI生成コードのレビュー観点(短尺)
SRPレビューの最小チェック
[ ] 変更理由が1つに収まっているか
[ ] 依頼対象外のクラスまで差分が広がっていないか
[ ] Serviceに業務ロジックが再流入していないか
[ ] テストも責任単位で追加されているか
9. 分けすぎ注意:SRPのやりすぎを避ける
SRPは「細かく割ること」ではなく「変更理由で分けること」です。理由が同じ処理を分けすぎると、逆に追跡コストが上がります。
過剰分割の短い例
// どれも「メール本文を作る」という同じ変更理由なのに細切れ
class SubjectBuilder {}
class GreetingBuilder {}
class FooterBuilder {}
class SignatureBuilder {}
変更理由が同じなら1モジュールに寄せたほうが見通しは良くなります。
AIワンポイント
AIに依頼する単位は、そのまま責任の単位です。
1つの責任に絞るほど、生成コードの精度とレビュー速度は上がります。
- 「直す理由が2つあるなら、クラスを分けるサイン」と覚える。
- 依頼前に「どの責任を直すか」を1行で固定してから投げる。
まとめ:SRPはAIを迷わせない設計ガードレール
- SRPは「1モジュール1変更理由」で境界を引き、AIへの指示を短く明確にする原則です。
- 責務分割は、AIリファクタリング時の影響範囲を封じる防火壁になります。
- 神クラス(アンチパターン)を避けて責任単位で小分け発注すると、品質と速度の両立がしやすくなります。
おまけ:コピペで使えるSRPチェックリスト
- このクラスの責任を「〇〇だけ担当」と1文で言える。
- 変更理由が2つ以上あるなら、分割候補として扱っている。
- AI依頼文に「対象クラス」「触らない範囲」「出力形式」を明記している。
- 修正後の差分が、対象責任の範囲内に収まっている。
- Serviceが調整役に留まり、検証・保存・通知を抱え込んでいない。
- 分割しすぎて、同じ変更理由のクラスが散らばっていない。