AI開発ツール活用 2025年12月20日

AI支援開発成功事例③:チーム生産性向上

本章では、AIコーディングツールを活用した複雑なバグ調査と修正の実践的なワークフローを紹介します。 **注記**: 本章はAI活用テクニックを解説するためのチュートリアルです。具体的な生産性向上の数値については、実在企業の調査データ(Vol.6 第8章参照)をご確認ください。

川西智也

合同会社ロイヤルピース 代表

実践例③:複雑なバグの調査と修正

本章では、AIコーディングツールを活用した複雑なバグ調査と修正の実践的なワークフローを紹介します。

注記: 本章はAI活用テクニックを解説するためのチュートリアルです。具体的な生産性向上の数値については、実在企業の調査データ(Vol.6 第8章参照)をご確認ください。


よくある問題シナリオ

問題例: 本番環境で断続的に発生するメモリリーク

症状:

  • 1-2週間ごとにサーバーがOOM(Out of Memory)でクラッシュ
  • 再現条件が不明
  • 従来の調査では原因特定に至らず

環境例:

  • Node.js 18 + Express
  • PostgreSQL
  • Redis
  • 本番トラフィック: 1,000 req/min

調査フェーズ

11.2.1 Claude Codeで段階的調査

プロンプト:
Node.jsアプリケーションで断続的なメモリリークが発生しています。

状況:
- 1-2週間ごとにOOMでクラッシュ
- 特定のエンドポイントとの相関は見つかっていない
- ヒープダンプ分析では明確なリーク元が不明

段階的に調査してください:

Step 1: よくあるメモリリークパターンを列挙
Step 2: このプロジェクトのコードで該当しそうな箇所を特定
Step 3: 各候補について詳細に分析
Step 4: 最も可能性が高い原因を特定

11.2.2 AIが提示する調査結果の例

## Step 1: Node.jsでよくあるメモリリークパターン

1. イベントリスナーの未解除
2. クロージャによる参照保持
3. グローバル変数への蓄積
4. キャッシュの無制限成長
5. タイマー(setInterval)の未クリア
6. Promiseの未処理
7. DB接続プールの不適切な管理

## Step 2: プロジェクト内の候補箇所

分析結果、以下の3箇所が疑わしいです:

1. src/services/cache.ts - インメモリキャッシュ
2. src/websocket/handler.ts - WebSocketイベントリスナー
3. src/jobs/scheduler.ts - 定期実行ジョブ

## Step 3: 詳細分析

### 3.1 cache.ts(最も疑わしい)
```typescript
// 問題のあるコードパターン
const cache = new Map<string, any>();

export function setCache(key: string, value: any) {
  cache.set(key, value);  // TTLなし、サイズ制限なし
}

このキャッシュには:

  • エントリの有効期限がない
  • 最大サイズの制限がない
  • 古いエントリの削除ロジックがない

時間とともに無制限に成長します。


---

## 原因特定

### 11.3.1 証拠の収集

プロンプト: cache.ts がメモリリークの原因である可能性を検証します。

以下を確認してください:

  1. このキャッシュを使用している箇所
  2. キャッシュに格納されるデータの種類
  3. キャッシュエントリの推定成長率
  4. 1週間でどの程度のメモリを消費するか推定

### 11.3.2 分析結果の例

```markdown
## 使用箇所の分析

cache.set() の呼び出し箇所:
1. src/api/users.ts:45 - ユーザーセッション情報
2. src/api/products.ts:78 - 商品検索結果
3. src/api/analytics.ts:23 - 分析データ

## 成長率の推定

- ユーザーセッション: 1セッション ≒ 2KB
- 1日のユニークユーザー: 約10,000
- 1日の新規キャッシュエントリ: 約10,000 × 2KB = 20MB

## 1週間の累積

- 7日 × 20MB = 140MB(セッションのみ)
- 商品検索・分析データを含めると: 推定 500MB/週

## 結論:押さえておくべきポイント

10日〜14日で約1GBに達し、
4GBのコンテナメモリ上限に到達する計算と一致。

**原因確定: TTLなしのインメモリキャッシュ**

修正フェーズ

11.4.1 修正方針の検討

プロンプト:
インメモリキャッシュのメモリリーク問題を修正します。

選択肢:
A. TTLとLRUを追加(node-cache等のライブラリ使用)
B. Redisに移行(既にRedis接続あり)
C. 自前でTTL付きキャッシュを実装

各選択肢のメリット・デメリットを分析し、
推奨案を提示してください。

制約:
- 本番環境への影響を最小化
- 既存のAPIを維持(後方互換性)

11.4.2 AIの推奨例

## 推奨: B. Redisに移行

### 理由
1. 既にRedis接続が存在(追加インフラ不要)
2. 分散環境でもキャッシュを共有可能
3. TTL、LRU、メモリ制限が標準機能
4. 将来のスケールアウトに対応

### 移行戦略
1. 新しいRedisキャッシュサービスを作成
2. 既存のcache.tsのインターフェースを維持
3. Feature Flagで段階的に切り替え
4. 問題なければ完全移行

11.4.3 実装

プロンプト:
Redisベースのキャッシュサービスを実装してください。

要件:
- 既存のcache.tsと同じインターフェース
- TTL対応(デフォルト1時間)
- エラーハンドリング(Redis障害時はキャッシュなしで動作)
- 型安全性を維持

@src/services/cache.ts
@src/lib/redis.ts

11.4.4 生成されたコード例

// src/services/redis-cache.ts
import { redis } from '../lib/redis';

const DEFAULT_TTL = 3600; // 1時間

export async function setCache<T>(
  key: string,
  value: T,
  ttl: number = DEFAULT_TTL
): Promise<void> {
  try {
    const serialized = JSON.stringify(value);
    await redis.setex(key, ttl, serialized);
  } catch (error) {
    console.error('Cache set failed:', error);
    // Redis障害時はエラーを握りつぶす(キャッシュなしで継続)
  }
}

export async function getCache<T>(key: string): Promise<T | null> {
  try {
    const value = await redis.get(key);
    if (!value) return null;
    return JSON.parse(value) as T;
  } catch (error) {
    console.error('Cache get failed:', error);
    return null;
  }
}

export async function deleteCache(key: string): Promise<void> {
  try {
    await redis.del(key);
  } catch (error) {
    console.error('Cache delete failed:', error);
  }
}

// 互換性レイヤー(同期APIを非同期に移行)
export const cache = {
  set: setCache,
  get: getCache,
  delete: deleteCache
};

テストと検証

11.5.1 テスト生成

プロンプト:
redis-cache.ts のテストを書いてください。

カバーすべきケース:
- 正常系(set/get/delete)
- TTL期限切れ
- Redis障害時のフォールバック
- 大量データの格納

モック: ioredis-mock を使用

11.5.2 生成されたテスト例

import Redis from 'ioredis-mock';
import { setCache, getCache, deleteCache } from './redis-cache';

// モックRedisをDI
jest.mock('../lib/redis', () => ({
  redis: new Redis()
}));

describe('RedisCache', () => {
  beforeEach(async () => {
    const { redis } = require('../lib/redis');
    await redis.flushall();
  });

  describe('setCache / getCache', () => {
    it('値を保存して取得できる', async () => {
      await setCache('user:1', { id: 1, name: 'Test' });
      const result = await getCache('user:1');
      expect(result).toEqual({ id: 1, name: 'Test' });
    });

    it('存在しないキーはnullを返す', async () => {
      const result = await getCache('nonexistent');
      expect(result).toBeNull();
    });
  });

  describe('TTL', () => {
    it('TTL後にデータが消える', async () => {
      await setCache('temp', 'value', 1); // 1秒

      const before = await getCache('temp');
      expect(before).toBe('value');

      await new Promise(r => setTimeout(r, 1100));

      const after = await getCache('temp');
      expect(after).toBeNull();
    });
  });

  describe('エラーハンドリング', () => {
    it('Redis障害時もエラーを投げない', async () => {
      const { redis } = require('../lib/redis');
      redis.disconnect();

      // エラーを投げずにnullを返す
      await expect(setCache('key', 'value')).resolves.not.toThrow();
      const result = await getCache('key');
      expect(result).toBeNull();
    });
  });
});

デプロイと監視

11.6.1 段階的ロールアウト

プロンプト:
Feature Flagを使った段階的移行の計画を立ててください。

要件:
- 0%から始めて100%まで
- 各段階でメモリ使用量を監視
- 問題があれば即座にロールバック
- 移行期間: 1週間

11.6.2 監視ダッシュボード設定

プロンプト:
メモリリーク修正後の監視項目を提案してください。

監視ツール: Datadog
アラート条件も含めてください。

AIが特に有効なポイント

  1. パターン認識: よくあるメモリリークパターンの網羅的列挙
  2. コード分析: 大量のコードから疑わしい箇所を迅速に特定
  3. 定量分析: 成長率の推定と原因の確度向上
  4. 修正案の比較: 複数の選択肢のトレードオフ分析
  5. テスト生成: エッジケースを含むテストの自動生成

人間が判断すべき部分

  • 最終的な原因の確定(AIの分析を検証)
  • 修正方針の決定(Redisへの移行を選択)
  • ロールアウト戦略の承認
  • 本番デプロイの実行

デバッグ用プロンプト集

11.9.1 段階的調査

以下のエラー/問題を段階的に調査してください。

問題: [エラーメッセージまたは症状]

Step 1: 考えられる原因を列挙
Step 2: このプロジェクトで該当しそうな箇所を特定
Step 3: 各候補の詳細分析
Step 4: 最も可能性が高い原因を特定
Step 5: 修正案を提示

11.9.2 エラーログ分析

以下のエラーログを分析してください。

[エラーログを貼り付け]


分析してほしい内容:
1. エラーの直接的な原因
2. スタックトレースから推測される問題箇所
3. 関連するコードファイル
4. 修正方法の提案

11.9.3 パフォーマンス問題の調査

以下のパフォーマンス問題を調査してください。

症状: [レスポンス遅延、CPU使用率高騰など]

確認してほしい観点:
1. N+1クエリ
2. 不要な再レンダリング
3. 非効率なアルゴリズム
4. ブロッキング処理
5. メモリ/キャッシュの問題

@[関連ファイル]

AI開発ツールの実際の効果

デバッグにおけるAIコーディングツールの実際の効果については、以下の研究を参照:

出典: Microsoft Research / GitHub

研究結果:
- タスク完了数: 26%増加
- コード品質への悪影響: なし
- 経験の浅い開発者ほど効果大

詳細はVol.6 第8章「導入事例(実在企業)」を参照してください。


まとめ:重要ポイントの振り返り

  • 段階的調査: AIに段階的な調査フローを依頼
  • パターン認識: よくある問題パターンの網羅的チェック
  • 定量分析: 推定と検証で原因を絞り込む
  • 修正比較: 複数の選択肢のトレードオフを分析
  • テスト生成: 修正の妥当性をテストで担保
  • 実際の効果: Vol.6 第8章の実在企業データを参照
  • 教訓:AIは複雑なデバッグの強力なパートナー、ただし最終判断は人間

実践的なスキルを習得しませんか?

ブログで学んだ知識を、研修で実践に変えましょう。