takt/builtins/ja/policies/ai-antipattern.md
nrslib ba8e90318c feat(builtins): add API client generation consistency rules
生成クライアント(Orval等)が存在するプロジェクトで手書きAPI呼び出しとの混在を検出するナレッジとポリシーを追加
2026-02-16 09:33:22 +09:00

12 KiB
Raw Blame History

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は「念のため」の防御コードを追加しがちだが、呼び出し元の制約を考慮すると到達不能な場合がある。構文的には到達可能でも、呼び出しチェーンの前提条件により論理的に到達しないコードは削除する。

// 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. 変更差分で ??||= defaultValuecatch を 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の決定ログが妥当か検証する。

確認項目 質問
決定が文書化されている 自明でない選択は説明されているか?
理由が妥当 理由は理にかなっているか?
代替案が検討されている 他のアプローチは評価されたか?
仮定が明示されている 仮定は明示的で合理的か?