221 lines
12 KiB
Markdown
221 lines
12 KiB
Markdown
# AI Antipattern 検出基準
|
||
|
||
## 仮定の検証
|
||
|
||
AIはしばしば仮定を行う。それを検証する。
|
||
|
||
| 確認項目 | 質問 |
|
||
|---------|------|
|
||
| 要件 | 実装は実際に要求されたものと一致しているか? |
|
||
| コンテキスト | 既存のコードベースの規則に合っているか? |
|
||
| ドメイン | ビジネスルールは正しく理解されているか? |
|
||
| エッジケース | AIは現実的なエッジケースを考慮したか? |
|
||
|
||
危険信号:
|
||
- 実装が異なる質問に答えているように見える
|
||
- コードベースの他の場所にないパターンを使用
|
||
- 特定の問題に対して過度に汎用的な解決策
|
||
|
||
## もっともらしいが間違っている検出
|
||
|
||
AIは正しく見えるが間違っているコードを生成する。
|
||
|
||
| パターン | 例 |
|
||
|---------|-----|
|
||
| 構文は正しいが意味が間違っている | 形式をチェックするがビジネスルールを見落とすバリデーション |
|
||
| 幻覚API | 使用しているライブラリバージョンに存在しないメソッドの呼び出し |
|
||
| 古いパターン | 学習データからの非推奨アプローチの使用 |
|
||
| 過剰エンジニアリング | タスクに不要な抽象化レイヤーの追加 |
|
||
| 過小エンジニアリング | 現実的なシナリオのエラーハンドリングの欠如 |
|
||
| 配線忘れ | 機構は実装されているが、エントリポイントから渡されていない |
|
||
|
||
検証アプローチ:
|
||
1. このコードは実際にコンパイル/実行できるか?
|
||
2. インポートされたモジュール/関数は存在するか?
|
||
3. このライブラリバージョンでAPIは正しく使用されているか?
|
||
4. 新しいパラメータ/フィールドが追加された場合、呼び出し元から実際に渡されているか?
|
||
- AIは個々のファイル内では正しく実装するが、ファイル横断の結合を忘れがち
|
||
- `options.xxx ?? fallback` で常にフォールバックが使われていないか grep で確認
|
||
|
||
## コピペパターン検出
|
||
|
||
AIは同じパターンを、間違いも含めて繰り返すことが多い。
|
||
|
||
| 確認項目 | アクション |
|
||
|---------|----------|
|
||
| 繰り返される危険なパターン | 複数の場所で同じ脆弱性 |
|
||
| 一貫性のない実装 | ファイル間で異なる方法で実装された同じロジック |
|
||
| ボイラープレートの爆発 | 抽象化できる不要な繰り返し |
|
||
|
||
## コンテキスト適合性評価
|
||
|
||
コードはこの特定のプロジェクトに合っているか?
|
||
|
||
| 側面 | 検証 |
|
||
|------|------|
|
||
| 命名規則 | 既存のコードベースのスタイルに一致 |
|
||
| エラーハンドリングスタイル | プロジェクトのパターンと一貫性 |
|
||
| ログ出力アプローチ | プロジェクトのログ規則を使用 |
|
||
| テストスタイル | 既存のテストパターンに一致 |
|
||
|
||
確認すべき質問:
|
||
- このコードベースに精通した開発者ならこう書くか?
|
||
- ここに属しているように感じるか?
|
||
- プロジェクト規則からの説明のない逸脱はないか?
|
||
|
||
## インテグレーションパターンの一貫性
|
||
|
||
同じ種類のAPI接続(REST呼び出し等)がプロジェクト内で異なる方式で実装されていないか確認する。
|
||
|
||
| パターン | 例 | 判定 |
|
||
|---------|-----|------|
|
||
| 生成クライアントと手書きクライアントの混在 | A画面はOrval生成フック、B画面はaxiosInstance直接 | REJECT |
|
||
| 同じデータ取得パターンの異なる実装 | A画面はuseQuery+axios、B画面は生成フック | REJECT |
|
||
| データ型の定義方式の混在 | A画面は生成された型、B画面は手書きの型 | REJECT |
|
||
|
||
検証アプローチ:
|
||
1. 変更差分のAPI呼び出し方式を確認
|
||
2. 同じ目的の既存コードがどの方式で書かれているか grep で確認
|
||
3. プロジェクトにAPI生成設定(orval.config.ts等)があるか確認
|
||
4. 不整合がある場合、プロジェクトの標準パターンへの統一を指摘する
|
||
|
||
## スコープクリープ検出
|
||
|
||
AIは過剰に提供する傾向がある。不要な追加をチェック。
|
||
|
||
| 確認項目 | 問題 |
|
||
|---------|------|
|
||
| 追加機能 | 要求されていない機能 |
|
||
| 早すぎる抽象化 | 単一実装のためのインターフェース/抽象化 |
|
||
| 過剰設定 | 設定可能にする必要のないものを設定可能に |
|
||
| ゴールドプレーティング | 求められていない「あると良い」追加 |
|
||
| 不要なLegacy対応 | 明示的な指示がないのに旧値のマッピング・正規化ロジックを追加 |
|
||
|
||
最良のコードは、問題を解決する最小限のコード。
|
||
|
||
Legacy対応の判定基準:
|
||
- 明示的に「Legacy値をサポートする」「後方互換性を保つ」という指示がない限り、Legacy対応は不要
|
||
- `.transform()` による正規化、`LEGACY_*_MAP` のようなマッピング、`@deprecated` な型定義は追加しない
|
||
- 新しい値のみをサポートし、シンプルに保つ
|
||
|
||
## デッドコード検出
|
||
|
||
AIは新しいコードを追加するが、不要になったコードの削除を忘れることが多い。
|
||
|
||
| パターン | 例 |
|
||
|---------|-----|
|
||
| 未使用の関数・メソッド | リファクタリング後に残った旧実装 |
|
||
| 未使用の変数・定数 | 条件変更で不要になった定義 |
|
||
| 到達不能コード | 早期returnの後に残った処理、常に真/偽になる条件分岐 |
|
||
| 論理的に到達不能な防御コード | 呼び出し元の制約で絶対に通らない分岐 |
|
||
| 未使用のインポート・依存 | 削除された機能のimport文やパッケージ依存 |
|
||
| 孤立したエクスポート・公開API | 実体が消えたのにre-exportやindex登録が残っている |
|
||
| 未使用のインターフェース・型定義 | 実装側が変更されたのに残った古い型 |
|
||
| 無効化されたコード | コメントアウトされたまま放置されたコード |
|
||
|
||
論理的デッドコードの検出:
|
||
|
||
AIは「念のため」の防御コードを追加しがちだが、呼び出し元の制約を考慮すると到達不能な場合がある。構文的には到達可能でも、呼び出しチェーンの前提条件により論理的に到達しないコードは削除する。
|
||
|
||
```typescript
|
||
// REJECT - 呼び出し元がTTY必須のインタラクティブメニュー経由のみ
|
||
// TTYがない環境からこの関数が呼ばれることはない
|
||
function showFullDiff(cwd: string, branch: string): void {
|
||
const usePager = process.stdin.isTTY === true;
|
||
// usePager は常に true(呼び出し元がTTYを前提としている)
|
||
const pager = usePager ? 'less -R' : 'cat'; // else節は到達不能
|
||
}
|
||
|
||
// OK - 呼び出し元の制約を理解し、不要な分岐を排除
|
||
function showFullDiff(cwd: string, branch: string): void {
|
||
// インタラクティブメニューからのみ呼ばれるためTTYは常に存在する
|
||
spawnSync('git', ['diff', ...], { env: { GIT_PAGER: 'less -R' } });
|
||
}
|
||
```
|
||
|
||
検証アプローチ:
|
||
1. 防御的な分岐を見つけたら、grep でその関数の全呼び出し元を確認
|
||
2. 全呼び出し元が既にその条件を満たしている場合、防御は不要
|
||
3. 変更・削除されたコードを参照している箇所がないか grep で確認
|
||
4. 公開モジュール(index ファイル等)のエクスポート一覧と実体が一致しているか確認
|
||
5. 新規追加されたコードに対応する古いコードが残っていないか確認
|
||
|
||
## フォールバック・デフォルト引数の濫用検出
|
||
|
||
AIは不確実性を隠すためにフォールバックやデフォルト引数を多用する。
|
||
|
||
| パターン | 例 | 判定 |
|
||
|---------|-----|------|
|
||
| 必須データへのフォールバック | `user?.id ?? 'unknown'` | REJECT |
|
||
| デフォルト引数の濫用 | `function f(x = 'default')` で全呼び出し元が省略 | REJECT |
|
||
| null合体で渡す口がない | `options?.cwd ?? process.cwd()` で上位から渡す経路なし | REJECT |
|
||
| try-catch で空値返却 | `catch { return ''; }` | REJECT |
|
||
| 多段フォールバック | `a ?? b ?? c ?? d` | REJECT |
|
||
| 条件分岐でサイレント無視 | `if (!x) return;` で本来エラーをスキップ | REJECT |
|
||
|
||
検証アプローチ:
|
||
1. 変更差分で `??`、`||`、`= defaultValue`、`catch` を grep
|
||
2. 各フォールバック・デフォルト引数について:
|
||
- 必須データか? → REJECT
|
||
- 全呼び出し元が省略しているか? → REJECT
|
||
- 上位から値を渡す経路があるか? → なければ REJECT
|
||
3. 理由なしのフォールバック・デフォルト引数が1つでもあれば REJECT
|
||
|
||
## 未使用コードの検出
|
||
|
||
AIは「将来の拡張性」「対称性」「念のため」で不要なコードを生成しがちである。現時点で呼ばれていないコードは削除する。
|
||
|
||
| 判定 | 基準 |
|
||
|------|------|
|
||
| REJECT | 現在どこからも呼ばれていないpublic関数・メソッド |
|
||
| REJECT | 「対称性のため」に作られたが使われていないsetter/getter |
|
||
| REJECT | 将来の拡張のために用意されたインターフェースやオプション |
|
||
| REJECT | exportされているが、grep で使用箇所が見つからない |
|
||
| OK | フレームワークが暗黙的に呼び出す(ライフサイクルフック等) |
|
||
|
||
検証アプローチ:
|
||
1. 変更・削除されたコードを参照している箇所がないか grep で確認
|
||
2. 公開モジュール(index ファイル等)のエクスポート一覧と実体が一致しているか確認
|
||
3. 新規追加されたコードに対応する古いコードが残っていないか確認
|
||
|
||
## 不要な後方互換コードの検出
|
||
|
||
AIは「後方互換のために」不要なコードを残しがちである。これを見逃さない。
|
||
|
||
削除すべき後方互換コード:
|
||
|
||
| パターン | 例 | 判定 |
|
||
|---------|-----|------|
|
||
| deprecated + 使用箇所なし | `@deprecated` アノテーション付きで誰も使っていない | 即削除 |
|
||
| 新APIと旧API両方存在 | 新関数があるのに旧関数も残っている | 両方に使用箇所がある場合を除き、旧を削除 |
|
||
| 移行済みのラッパー | 互換のために作ったが移行完了済み | 削除 |
|
||
| コメントで「将来削除」 | `// TODO: remove after migration` が放置 | 今すぐ削除 |
|
||
| Proxy/アダプタの過剰使用 | 後方互換のためだけに複雑化 | シンプルに置換 |
|
||
|
||
残すべき後方互換コード:
|
||
|
||
| パターン | 例 | 判定 |
|
||
|---------|-----|------|
|
||
| 外部公開API | npm パッケージのエクスポート | 慎重に検討 |
|
||
| 設定ファイル互換 | 旧形式の設定を読める | メジャーバージョンまで維持 |
|
||
| データ移行中 | DBスキーマ移行の途中 | 移行完了まで維持 |
|
||
|
||
判断基準:
|
||
1. 使用箇所があるか? → grep/検索で確認。なければ削除
|
||
2. 新旧両方に使用箇所があるか? → 両方が現在使われているなら、後方互換ではなく並存設計の可能性がある。呼び出し元を確認
|
||
3. 外部に公開しているか? → 内部のみなら即削除可能
|
||
4. 移行は完了したか? → 完了なら削除
|
||
|
||
AIが「後方互換のため」と言ったら疑う。本当に必要か確認せよ。
|
||
|
||
## 決定トレーサビリティレビュー
|
||
|
||
Coderの決定ログが妥当か検証する。
|
||
|
||
| 確認項目 | 質問 |
|
||
|---------|------|
|
||
| 決定が文書化されている | 自明でない選択は説明されているか? |
|
||
| 理由が妥当 | 理由は理にかなっているか? |
|
||
| 代替案が検討されている | 他のアプローチは評価されたか? |
|
||
| 仮定が明示されている | 仮定は明示的で合理的か? |
|