T-CREATOR

Devin でレガシーコードの見える化:メトリクス抽出と負債リスト自動作成

Devin でレガシーコードの見える化:メトリクス抽出と負債リスト自動作成

レガシーコードの保守や改修は、多くの開発チームにとって悩みの種ですよね。どこから手をつければいいのか、どの部分が技術的負債になっているのかを把握するだけでも膨大な時間がかかります。

そんな課題を解決するため、AI エンジニア「Devin」を活用したコードメトリクス抽出と技術的負債リストの自動作成について解説していきます。Devin に任せることで、レガシーコードの可視化作業を大幅に効率化できるでしょう。

背景

レガシーコードとは

レガシーコードとは、長期間運用されてきたシステムのコードで、以下のような特徴を持つものを指します。

  • 古い技術スタックで構築されている
  • テストコードが不足している
  • ドキュメントが整備されていない
  • 複雑度が高く理解が困難

こうしたコードは、新機能の追加やバグ修正の際に大きな障壁となります。

コードメトリクスの重要性

コードメトリクスとは、ソースコードの品質や複雑さを数値化した指標のことです。代表的なメトリクスには以下のようなものがあります。

#メトリクス名説明目安値
1循環的複雑度(Cyclomatic Complexity)関数やメソッドの複雑さを示す指標10 以下が望ましい
2コード行数(Lines of Code)ファイルや関数の行数関数は 50 行以下が理想
3重複率(Code Duplication)同じようなコードの重複度合い5% 以下が目標
4保守性指数(Maintainability Index)コードの保守しやすさを示す総合指標20 以上が良好

これらの指標を測定することで、どの部分が技術的負債になっているかが明確になります。

Devin による自動化の可能性

Devin は、コーディングからデバッグ、テストまでを自律的に行える AI エンジニアです。単純な作業の自動化だけでなく、複雑な分析タスクも実行できますね。

レガシーコードの分析においても、Devin は以下のような作業を自動化できます。

  • コードベース全体のスキャン
  • 各種メトリクスの計算
  • 問題箇所の特定とレポート作成
  • 改善提案の生成

以下の図は、Devin を活用したレガシーコード分析の全体フローを示しています。

mermaidflowchart TB
    legacy["レガシーコード<br/>ベース"] -->|解析指示| devin["Devin AI"]
    devin -->|スキャン| scan["コード全体を<br/>スキャン"]
    scan -->|メトリクス抽出| metrics["メトリクス<br/>計算"]
    metrics -->|分析| analysis["負債箇所<br/>特定"]
    analysis -->|生成| report["負債リスト<br/>レポート"]
    report -->|フィードバック| team["開発チーム"]

図で理解できる要点:

  • Devin がコードベース全体を自動でスキャンし、メトリクスを抽出します
  • 抽出したメトリクスを基に技術的負債を特定し、レポートを生成します
  • 開発チームは生成されたレポートを基に改善計画を立てられます

課題

手動メトリクス抽出の限界

従来の手動によるコードメトリクス抽出には、いくつかの大きな課題がありました。

時間と労力のコスト

大規模なコードベースでは、全ファイルを手動で分析するのに数週間から数ヶ月かかることもあります。専任のエンジニアを配置しても、その間は他の開発タスクが停滞してしまいますね。

分析の一貫性の欠如

複数のエンジニアが分担して分析すると、評価基準にばらつきが生じます。ある人は厳しく評価し、別の人は甘く評価するといった不一致が発生しがちです。

ツール導入の障壁

既存の静的解析ツールを導入する場合でも、以下のような問題があります。

  • 設定ファイルの作成が複雑
  • 出力結果の解釈に専門知識が必要
  • 複数ツールの結果を統合する手間

以下の図は、手動分析における課題を整理したものです。

mermaidflowchart LR
    manual["手動メトリクス<br/>抽出"] -->|課題1| time["膨大な時間<br/>コスト"]
    manual -->|課題2| inconsist["分析基準の<br/>不一致"]
    manual -->|課題3| skill["専門知識<br/>の必要性"]
    manual -->|課題4| integration["ツール統合の<br/>困難さ"]

    time --> result["開発遅延"]
    inconsist --> result
    skill --> result
    integration --> result

図で理解できる要点:

  • 手動分析には時間、一貫性、専門知識、統合の 4 つの大きな課題があります
  • これらの課題はすべて開発遅延という結果につながります
  • Devin を活用することでこれらの課題を解決できます

技術的負債の可視化不足

もう一つの大きな課題は、技術的負債が適切に可視化されていないことです。

優先順位付けの困難

どの部分から改修すべきかの判断基準が曖昧だと、効率的なリファクタリング計画を立てられません。影響範囲の大きさ、修正の難易度、ビジネス価値など、複数の観点から総合的に判断する必要があります。

ステークホルダーへの説明不足

経営層や非エンジニアのステークホルダーに対して、技術的負債の深刻さを伝えるのは容易ではありません。数値やグラフで可視化されたレポートがあれば、説得力が増しますね。

進捗管理の難しさ

リファクタリングの進捗を定量的に測定できないと、改善活動が形骸化してしまいます。メトリクスの推移を継続的に追跡する仕組みが必要です。

解決策

Devin を活用したメトリクス抽出

Devin を使うことで、コードメトリクスの抽出作業を完全に自動化できます。

自動スキャンと分析

Devin に以下のような指示を出すだけで、コードベース全体の分析が始まります。

「このリポジトリのすべての JavaScript ファイルについて、循環的複雑度とコード行数を計算し、CSV ファイルにまとめてください」

Devin は自律的に以下の作業を実行します。

  1. リポジトリのクローン
  2. 対象ファイルの特定
  3. 静的解析ツールのインストールと設定
  4. メトリクスの計算
  5. 結果のフォーマットと出力

複数メトリクスの統合

Devin は複数の静的解析ツールを組み合わせて使うこともできます。ESLint、SonarQube、JSComplexity など、目的に応じたツールを選択し、結果を統合したレポートを生成してくれますね。

カスタム指標の作成

標準的なメトリクスに加えて、プロジェクト固有の指標を定義することも可能です。たとえば、特定のアンチパターンの出現頻度や、非推奨 API の使用箇所数などをカウントできます。

以下の図は、Devin によるメトリクス抽出プロセスを示しています。

mermaidflowchart TB
    instruction["エンジニアからの<br/>指示"] -->|自然言語| devin_process["Devin"]
    devin_process -->|1| clone["リポジトリ<br/>クローン"]
    devin_process -->|2| identify["対象ファイル<br/>特定"]
    devin_process -->|3| tool_setup["解析ツール<br/>セットアップ"]
    devin_process -->|4| calculate["メトリクス<br/>計算"]
    devin_process -->|5| format_output["結果の<br/>フォーマット"]

    tool_setup --> eslint["ESLint"]
    tool_setup --> sonar["SonarQube"]
    tool_setup --> complexity["Complexity<br/>ツール"]

    eslint --> calculate
    sonar --> calculate
    complexity --> calculate

    format_output --> csv["CSV レポート"]
    format_output --> json_output["JSON データ"]
    format_output --> html["HTML<br/>ダッシュボード"]

図で理解できる要点:

  • Devin は自然言語の指示だけで複雑な分析フローを実行します
  • 複数の解析ツールを自動的に統合し、一貫性のある結果を出力します
  • CSV、JSON、HTML など、用途に応じた形式でレポートを生成できます

負債リスト自動作成の仕組み

メトリクスを抽出しただけでは不十分です。それらを基に、実用的な技術的負債リストを作成する必要があります。

閾値ベースの問題検出

Devin に閾値を設定することで、基準を超えたコードを自動的に抽出できます。

typescript// 閾値設定の例
const thresholds = {
  cyclomaticComplexity: 10,
  linesOfCode: 200,
  duplicateLines: 50,
  maintainabilityIndex: 20,
};

上記の設定を Devin に渡すことで、これらの基準を超えるファイルや関数を自動的にリストアップしてくれます。

優先度の自動付与

複数のメトリクスを組み合わせて、各負債項目に優先度を自動計算させることができます。

typescript// 優先度計算のロジック例
interface TechnicalDebt {
  file: string;
  function: string;
  complexity: number;
  lines: number;
  priority: 'high' | 'medium' | 'low';
}

優先度の計算には、以下のような要素を考慮します。

  • メトリクスの閾値超過度合い
  • 変更頻度(Git ログから取得)
  • バグの発生履歴
  • ビジネスロジックの重要度

改善提案の生成

Devin は単に問題を指摘するだけでなく、具体的な改善案も提示できます。

「この関数は循環的複雑度が 25 と高いため、以下のようにリファクタリングすることをお勧めします」といった形で、コード例付きの提案を生成してくれますね。

レポートフォーマット

生成される技術的負債リストには、以下のような情報が含まれます。

#項目説明
1ファイルパス問題のあるファイルの場所
2関数名該当する関数やメソッド名
3メトリクス値循環的複雑度、行数など
4優先度High / Medium / Low
5改善提案具体的なリファクタリング案
6推定工数修正にかかる時間の見積もり

このようなレポートがあれば、チーム全体で技術的負債に関する共通認識を持てます。

具体例

プロジェクトの準備

まずは、分析対象となるレガシープロジェクトを準備しましょう。ここでは Node.js で書かれた古い Express アプリケーションを例とします。

リポジトリの構造

csslegacy-express-app/
├── src/
│   ├── controllers/
│   ├── models/
│   ├── routes/
│   └── utils/
├── package.json
└── README.md

このプロジェクトは 3 年以上運用されており、複数の開発者が関わってきたため、コードの品質にばらつきがあります。

Devin への指示内容

Devin に以下のような指示を出します。自然言語で具体的に要件を伝えることがポイントですね。

markdownこのリポジトリ(https://github.com/example/legacy-express-app)について、
以下の分析を実行してください:

1. すべての JavaScript ファイルの循環的複雑度を計算
2. 100 行を超える関数をリストアップ
3. コードの重複箇所を検出
4. 循環的複雑度が 15 を超える関数を「高優先度」、
   10 〜 15 を「中優先度」、それ以下を「低優先度」として分類
5. 結果を CSV と HTML レポートの両方で出力
6. 各高優先度項目について、リファクタリング提案を含める

この指示だけで、Devin は必要な作業をすべて理解し、実行に移してくれます。

Devin の実行プロセス

Devin が実際に行う作業の流れを見ていきましょう。

ステップ 1:環境セットアップ

Devin はまず、分析に必要なツールをインストールします。

bash# Devin が実行するコマンド例
yarn add --dev eslint
yarn add --dev jscomplexity
yarn add --dev jsinspect

これらのツールは、それぞれ以下の目的で使用されます。

  • eslint:コードの静的解析
  • jscomplexity:循環的複雑度の計算
  • jsinspect:コードの重複検出

ステップ 2:設定ファイルの生成

次に、各ツールの設定ファイルを自動生成します。

javascript// .eslintrc.js(Devin が生成)
module.exports = {
  env: {
    node: true,
    es2021: true,
  },
  extends: 'eslint:recommended',
  parserOptions: {
    ecmaVersion: 12,
  },
  rules: {
    complexity: ['error', 15],
  },
};

この設定により、循環的複雑度が 15 を超える関数がエラーとして検出されるようになります。

ステップ 3:メトリクス計算スクリプトの作成

Devin は、複数のツールの結果を統合するためのスクリプトを自動生成します。

javascript// analyze-metrics.js(Devin が生成)
const { execSync } = require('child_process');
const fs = require('fs');
const path = require('path');

/**
 * すべての JavaScript ファイルを再帰的に取得する関数
 * src ディレクトリ配下を対象とする
 */
function getAllJsFiles(dir) {
  const files = [];
  const entries = fs.readdirSync(dir, {
    withFileTypes: true,
  });

  for (const entry of entries) {
    const fullPath = path.join(dir, entry.name);
    if (entry.isDirectory()) {
      files.push(...getAllJsFiles(fullPath));
    } else if (entry.name.endsWith('.js')) {
      files.push(fullPath);
    }
  }

  return files;
}

// メイン処理を実行
const srcDir = './src';
const jsFiles = getAllJsFiles(srcDir);
console.log(`Found ${jsFiles.length} JavaScript files`);

この部分では、分析対象のファイルを再帰的に検索しています。ディレクトリを走査し、すべての .js ファイルを収集する処理ですね。

javascript/**
 * 循環的複雑度を計算する関数
 * jscomplexity ツールを使用
 */
function calculateComplexity(files) {
  const results = [];

  for (const file of files) {
    try {
      const output = execSync(
        `yarn jscomplexity ${file} --format json`,
        { encoding: 'utf-8' }
      );
      const data = JSON.parse(output);
      results.push({
        file,
        complexity: data.complexity,
        functions: data.functions,
      });
    } catch (error) {
      console.error(
        `Error analyzing ${file}:`,
        error.message
      );
    }
  }

  return results;
}

const complexityData = calculateComplexity(jsFiles);

ここでは、各ファイルの循環的複雑度を計算しています。jscomplexity コマンドを実行し、結果を JSON 形式で取得していますね。

javascript/**
 * コードの重複を検出する関数
 * jsinspect ツールを使用
 */
function detectDuplicates(files) {
  try {
    const output = execSync(
      `yarn jsinspect ${files.join(
        ' '
      )} --threshold 30 --format json`,
      { encoding: 'utf-8' }
    );
    return JSON.parse(output);
  } catch (error) {
    console.error(
      'Error detecting duplicates:',
      error.message
    );
    return [];
  }
}

const duplicates = detectDuplicates(jsFiles);

重複コードの検出には jsinspect を使用しています。30 行以上の類似コードを検出対象としています。

javascript/**
 * 優先度を計算する関数
 * 複雑度に基づいて high/medium/low を判定
 */
function calculatePriority(complexity) {
  if (complexity > 15) return 'high';
  if (complexity > 10) return 'medium';
  return 'low';
}

/**
 * 技術的負債リストを生成する関数
 */
function generateDebtList(complexityData, duplicates) {
  const debtList = [];

  for (const fileData of complexityData) {
    for (const func of fileData.functions) {
      if (func.complexity > 10) {
        debtList.push({
          file: fileData.file,
          function: func.name,
          complexity: func.complexity,
          lines: func.lines,
          priority: calculatePriority(func.complexity),
          type: 'high_complexity',
        });
      }
    }
  }

  return debtList;
}

const debtList = generateDebtList(
  complexityData,
  duplicates
);

ここでは、収集したメトリクスデータを基に技術的負債リストを生成しています。循環的複雑度が 10 を超える関数を抽出し、優先度を付与していますね。

javascript/**
 * CSV 形式でレポートを出力する関数
 */
function exportToCsv(debtList, filename) {
  const headers = [
    'File',
    'Function',
    'Complexity',
    'Lines',
    'Priority',
    'Type',
  ];
  const rows = debtList.map((item) => [
    item.file,
    item.function,
    item.complexity,
    item.lines,
    item.priority,
    item.type,
  ]);

  const csv = [
    headers.join(','),
    ...rows.map((row) => row.join(',')),
  ].join('\n');

  fs.writeFileSync(filename, csv);
  console.log(`CSV report exported to ${filename}`);
}

exportToCsv(debtList, 'technical-debt-report.csv');

最後に、生成した負債リストを CSV ファイルとして出力しています。スプレッドシートで開いて分析できる形式ですね。

以下の図は、Devin が実行する分析プロセスの全体像を示しています。

mermaidsequenceDiagram
    participant Engineer as エンジニア
    participant Devin as Devin
    participant Tools as 解析ツール群
    participant Output as レポート

    Engineer->>Devin: 分析指示
    Devin->>Devin: 環境セットアップ
    Devin->>Tools: ツールインストール
    Devin->>Devin: 設定ファイル生成
    Devin->>Tools: ファイルスキャン実行
    Tools->>Devin: メトリクスデータ
    Devin->>Devin: 負債リスト生成
    Devin->>Devin: 優先度計算
    Devin->>Output: CSV 出力
    Devin->>Output: HTML 出力
    Devin->>Engineer: 完了通知

図で理解できる要点:

  • Devin がエンジニアの指示を受けてから完了まで、すべて自律的に実行します
  • 複数の解析ツールを組み合わせてメトリクスを収集します
  • 最終的に CSV と HTML の両方でレポートを出力します

出力されるレポートの例

Devin が生成する CSV レポートは以下のような内容になります。

#FileFunctionComplexityLinesPriorityType
1src/controllers/user.jsvalidateUserInput22145highhigh_complexity
2src/routes/api.jshandleRequest1898highhigh_complexity
3src/utils/formatter.jsformatData1276mediumhigh_complexity
4src/models/order.jscalculateTotal1154mediumhigh_complexity
5src/controllers/product.jsupdateProduct16112highhigh_complexity

このレポートから、src​/​controllers​/​user.jsvalidateUserInput 関数が最も優先度の高いリファクタリング対象であることがわかりますね。

HTML ダッシュボード

CSV に加えて、視覚的に理解しやすい HTML ダッシュボードも生成されます。

html<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <title>技術的負債レポート</title>
    <style>
      /* CSS スタイル定義 */
      body {
        font-family: Arial, sans-serif;
        margin: 20px;
        background-color: #f5f5f5;
      }
    </style>
  </head>
</html>

HTML ファイルの基本構造を定義しています。日本語に対応するため、文字エンコーディングを UTF-8 に設定していますね。

html<body>
  <h1>技術的負債レポート</h1>
  <div class="summary">
    <h2>サマリー</h2>
    <p>総ファイル数: <span id="total-files">128</span></p>
    <p>高優先度項目: <span id="high-priority">15</span></p>
    <p>
      中優先度項目: <span id="medium-priority">28</span>
    </p>
    <p>低優先度項目: <span id="low-priority">45</span></p>
  </div>
</body>

ダッシュボードのサマリーセクションです。全体の統計情報を一目で確認できるようになっています。

html  <div class="chart-container">
    <h2>優先度別分布</h2>
    <canvas id="priority-chart"></canvas>
  </div>

  <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
  <script>
    // グラフ描画用の JavaScript コード
    const ctx = document.getElementById('priority-chart').getContext('2d');
    new Chart(ctx, {
      type: 'bar',
      data: {
        labels: ['高優先度', '中優先度', '低優先度'],
        datasets: [{
          label: '件数',
          data: [15, 28, 45],
          backgroundColor: ['#ff6384', '#ffcd56', '#36a2eb']
        }]
      }
    });
  </script>
</body>
</html>

Chart.js ライブラリを使用して、優先度別の分布を棒グラフで視覚化しています。経営層への報告にも活用できる形式ですね。

リファクタリング提案の例

高優先度項目については、Devin が具体的なリファクタリング提案も生成してくれます。

javascript// 【リファクタリング前】
// src/controllers/user.js の validateUserInput 関数
function validateUserInput(data) {
  if (!data) {
    return { valid: false, error: 'No data provided' };
  }
  if (!data.name) {
    return { valid: false, error: 'Name is required' };
  }
  if (data.name.length < 2) {
    return { valid: false, error: 'Name too short' };
  }
  if (data.name.length > 50) {
    return { valid: false, error: 'Name too long' };
  }
  if (!data.email) {
    return { valid: false, error: 'Email is required' };
  }
  // ... さらに 20 個以上の条件分岐が続く
}

この関数は循環的複雑度が 22 と非常に高く、読みづらくテストもしにくい状態です。

Devin の提案するリファクタリング後のコードは以下のようになります。

javascript// 【リファクタリング後】
// バリデーションルールを定義したオブジェクト
const validationRules = {
  name: {
    required: true,
    minLength: 2,
    maxLength: 50,
    errorMessages: {
      required: 'Name is required',
      minLength: 'Name too short',
      maxLength: 'Name too long',
    },
  },
  email: {
    required: true,
    pattern: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
    errorMessages: {
      required: 'Email is required',
      pattern: 'Invalid email format',
    },
  },
};

まず、バリデーションルールを宣言的に定義します。これにより、ルールの追加や変更が容易になりますね。

javascript/**
 * フィールドの検証を行う汎用関数
 * ルールに基づいて単一フィールドを検証
 */
function validateField(value, rules) {
  if (rules.required && !value) {
    return {
      valid: false,
      error: rules.errorMessages.required,
    };
  }

  if (rules.minLength && value.length < rules.minLength) {
    return {
      valid: false,
      error: rules.errorMessages.minLength,
    };
  }

  if (rules.maxLength && value.length > rules.maxLength) {
    return {
      valid: false,
      error: rules.errorMessages.maxLength,
    };
  }

  if (rules.pattern && !rules.pattern.test(value)) {
    return {
      valid: false,
      error: rules.errorMessages.pattern,
    };
  }

  return { valid: true };
}

各フィールドの検証を行う汎用関数です。ルールベースの設計により、循環的複雑度を大幅に削減できています。

javascript/**
 * ユーザー入力全体を検証するメイン関数
 * リファクタリング後の validateUserInput
 */
function validateUserInput(data) {
  if (!data) {
    return { valid: false, error: 'No data provided' };
  }

  // すべてのフィールドを検証
  for (const [field, rules] of Object.entries(
    validationRules
  )) {
    const result = validateField(data[field], rules);
    if (!result.valid) {
      return result;
    }
  }

  return { valid: true };
}

メイン関数は非常にシンプルになり、循環的複雑度は 4 まで低下します。テストも書きやすく、保守性が大幅に向上しましたね。

このように、Devin は問題の指摘だけでなく、実践的な改善案まで提示してくれるため、リファクタリング作業を効率的に進められます。

継続的モニタリング

一度分析して終わりではなく、継続的にメトリクスを追跡することが重要です。

javascript// monitor-metrics.js(Devin が生成)
const schedule = require('node-schedule');

/**
 * 毎週月曜日の朝 9 時にメトリクスを分析する
 * CI/CD パイプラインに組み込むことも可能
 */
schedule.scheduleJob('0 9 * * 1', async () => {
  console.log('Starting weekly metrics analysis...');

  // メトリクス分析を実行
  const metrics = await analyzeMetrics();

  // 前回との比較
  const comparison = compareWithPrevious(metrics);

  // 改善・悪化の報告
  reportChanges(comparison);
});

このスクリプトを CI/CD パイプラインに組み込むことで、コード品質の推移を自動的に追跡できます。

まとめ

Devin を活用することで、レガシーコードのメトリクス抽出と技術的負債リストの作成を完全に自動化できます。

本記事で紹介した主なポイントは以下の通りです。

  • 手動分析の課題:時間コスト、一貫性の欠如、専門知識の必要性といった問題があります
  • Devin の強み:自然言語の指示だけで複雑な分析フローを実行できます
  • メトリクス抽出:循環的複雑度、コード行数、重複率など複数の指標を自動計算できます
  • 負債リスト生成:優先度付きの実用的なレポートを CSV と HTML で出力できます
  • リファクタリング提案:具体的なコード改善案まで提示してくれます
  • 継続的モニタリング:定期実行により、コード品質の推移を追跡できます

レガシーコードの可視化は、技術的負債を計画的に返済していく第一歩です。Devin を導入することで、分析作業にかかる時間を大幅に削減し、エンジニアはより価値の高いリファクタリング作業に集中できるでしょう。

まずは小規模なプロジェクトで試してみて、効果を実感してから大規模なコードベースに適用していくことをお勧めします。技術的負債の可視化により、チーム全体でコード品質に対する意識が高まり、持続可能な開発体制を構築できますね。

関連リンク