コンテンツにスキップ

AIがコードを書く時代、技術負債はどう見張る? — ネットワーク科学に頼ってみる

note.com (https://note.com/yamamoto_ken) 投稿用ドラフト。2026-06-11 作成、同日に文体調整・デモ追加。


はじめに

最近、自分が書いたコードよりAIが書いたコードのほうが多くなってきました。レビューもAIが下読みしてくれるし、正直、以前ほどコードを読まなくなっています。

便利になった一方で、ふと不安になるんですよね。関数ひとつひとつはきれいなのに、システム全体の構造が静かに劣化していたら、誰が気づくんだろう? と。

この記事では、「人間がコードを読む時代」に作られた従来の静的指標がなぜ今つらいのか、そしてその代わりにネットワーク科学の知見(クラス依存度・中心性・伝播コストなど)で技術負債を監視するという話を、代表的な論文と近年の研究を交えて書いてみます。後半では、自分の趣味リポジトリを実際に計測したミニデモもやってみました。

そもそも、静的指標はなぜ使われてきたのか

技術負債の監視といえば、長らくこのあたりが定番でした。

  • コード行数(LOC)・ファイルサイズ
  • 循環的複雑度(Cyclomatic Complexity)
  • Maintainability Index
  • 重複コード率、テストカバレッジ

これらが広く使われてきた理由は、突き詰めるとわかりやすさだと思っています。

  • 指標が1ファイル・1関数に閉じて計算できるので、指摘箇所とコードが1対1で対応する
  • 「この関数の複雑度が15を超えています」と言われたら、人間がそのコードを開いて読んで直せる
  • SonarQube などのツールに組み込みやすく、CIで機械的に閾値判定できる

つまり静的指標は、「人間がコードを読んで直す」というワークフローの入口として最適化された指標だったんですよね。指標の単位(関数・ファイル)と、人間が一度に読める範囲がだいたい一致していたから機能していた。

AIがコードを書くようになって、何が変わったか

その前提が崩れつつあります。コードの大半をAIが書いて、人間は差分を流し読みするだけ、という開発スタイルが現実になりました。

ここで重要なのは、AI時代に劣化するのは「関数の中身」ではなく「システムの構造」だという点です。GitClear が約2.1億行のコード変更を分析した 2025年のレポートに、その兆候がはっきり出ています。

  • コピペ(重複)コード行の割合が 2020年の8.3% → 2024年の12.3% に増加。2024年には計測史上初めて、コピペ行がリファクタリング(移動)行を上回った
  • リファクタリングの割合は 2021年の約25% → 2024年に10%未満まで低下
  • 書かれて2週間以内に書き直される行(チャーン)は 5.5% → 7.9% に増加

理由はシンプルで、AIのコード生成は「既存コードを整理・移動・削除する」のではなく「コードを足す」方向に設計されているからです。システムは設計改善ではなく増築で育ち、重複と依存が静かに積もっていく。

そして厄介なことに、このとき従来の静的指標はほとんど警報を鳴らしません。AIが書く個々の関数は短くて、複雑度も低くて、命名もきれいだから。ファイル単位の指標はぜんぶ緑なのに、システム全体はスパゲッティ化していく — これがAI駆動開発時代の技術負債の typical な姿だと思います。

ソフトウェアは「複雑ネットワーク」だった

じゃあシステム構造の劣化をどう測るのか。ここでネットワーク科学の出番です。

ネットワーク科学は1990年代末に爆発した分野で、出発点になったのが2本の超有名論文です。Watts & Strogatz のスモールワールド・ネットワーク (Nature, 1998) と、Barabási & Albert のスケールフリー・ネットワーク (Science, 1999)。後者が示したのは、Webのリンクや論文の引用、タンパク質の相互作用といった現実のネットワークの多くが「均質」ではなく、ごく少数のハブが接続を独占する偏った構造を持つ、ということでした。

flowchart TB
    subgraph RND["ランダムネットワーク(どのノードも似たような次数)"]
        direction LR
        R1[A] --- R2[B]
        R2 --- R3[C]
        R3 --- R4[D]
        R4 --- R5[E]
        R5 --- R1
        R1 --- R3
    end
    subgraph SF["スケールフリーネットワーク(少数のハブに接続が集中)"]
        direction TB
        Hub((ハブ))
        Hub --- S1[a]
        Hub --- S2[b]
        Hub --- S3[c]
        Hub --- S4[d]
        Hub --- S5[e]
        Hub --- S6[f]
        S5 --- S7[g]
    end

(ランダム側:どのノードも似ていて、平均値が代表性を持つ世界。スケールフリー側:平均値がほぼ無意味で、ハブだけが重要な世界)

で、ソフトウェアです。クラスやモジュールをノード、依存関係(呼び出し・継承・参照)をエッジとみなすと、ソフトウェアも1つのグラフになります。物理学者 Christopher Myers の古典 Software systems as complex networks (Physical Review E, 2003) は、OSSのクラス依存グラフが、まさに後者のスケールフリー・スモールワールド構造を持つことを示しました。

  • 次数分布がべき乗則に従う。つまりごく少数の「ハブ」クラスが大量の依存を集めて、大多数のクラスは1〜2本しか依存を持たない
  • out-degree(依存する側)が大きいクラスほど変更が速い(=変更が集中する)

これが示唆するのは、技術負債は一様に分布しないということ。平均値ベースの指標(平均複雑度とか)は、べき乗則の世界では実態をほぼ捉えられません。見るべきは「平均」ではなく「ハブ」、つまりネットワーク上の位置なんですね。

代表的な論文を3系統おさえる

1. Zimmermann & Nagappan (ICSE 2008) — つながり方は中身より雄弁

この分野の金字塔が、Microsoft Research の Predicting Defects using Network Analysis on Dependency Graphs です。

Windows Server 2003 の依存グラフに約60種類のネットワーク指標(次数中心性、媒介中心性、クラスタ係数など)を計算して欠陥予測モデルを作ったところ、ネットワーク指標のモデルは複雑度指標のモデルより recall が約10ポイント高かった。「コードの中身(複雑度)より、コードのつながり方のほうが欠陥をよく説明する」という結果で、当時かなりのインパクトがありました。

公平のために書くと、追試研究(Premraj & Herzig, 2011)では「優位性はプロジェクト次第」という結果も出ています。万能ではなく、静的指標と組み合わせると性能が上がる、というのが現在の堅実な理解です。

2. MacCormack & Baldwin — 伝播コスト(Propagation Cost)

ハーバード・ビジネス・スクールの MacCormack、Baldwin らは、設計構造マトリクス(DSM)でアーキテクチャを定量化する研究を続けてきました(Management Science 2006Hidden Structure)。

代表的な指標が Propagation Cost(伝播コスト)

ランダムに選んだ1要素を変更したとき、影響が及ぶ要素の平均割合

ポイントは直接依存だけでなく間接依存(A→B→C なら A の変更は C に波及しうる)を推移閉包で数え上げること。「この変更、どこまで波及するかわからない」という開発者の恐怖を1つの数値にしたもの、と言えます。Mozilla の再設計の前後比較で、リファクタリングが伝播コストを劇的に下げることも示されています。

3. Mo, Cai, Kazman — Decoupling Level とアーキテクチャ負債の「返済」

Drexel大の Cai、CMU/SEI の Kazman らは、この流れを「アーキテクチャ負債の特定と定量化」まで進めました。

  • Decoupling Level (ICSE 2016) — システムが「小さく独立に交換可能なモジュール」にどれだけ分割できているかを測る指標。DLが低いシステムほどバグが多く、機能拡張が困難なことを複数プロジェクトで実証
  • 縦断研究 (ICSE-SEIP 2019) — 実プロダクトで負債を特定 → リファクタリング → 保守コスト低下、までを追跡

DL の良いところは、時系列で追えばアーキテクチャの腐敗(architecture decay)を監視できること。ダッシュボードに載せられるアーキテクチャ指標です。

近年はGNNへ

2020年代に入ると、手作りのネットワーク指標からGNN(グラフニューラルネットワーク)で依存グラフの構造そのものを学習する方向に進んでいます。データ依存・呼び出し依存・開発者依存を統合して欠陥予測する DeMuVGN (2024) や、AST/CFG/DFG を統合したマルチレベルGNN (2025) など。「中身はLLMが読む。構造はグラフで学習する」という分業がはっきりしてきた印象で、この記事の主張とも符合します。

ミニデモ:息子の絵本リポジトリを測ってみた

理屈だけだとつまらないので、実際に測ってみます。題材は baby-ehon。1歳の息子のために作ったブラウザ絵本シリーズで、HTML/CSS/JSのみ・ビルドなしの小さなリポジトリです。

ちなみにこのリポジトリ、コードの大半はAIに書かせたうえに、更新自体も LangChain / LangGraph 製のエージェントパイプラインで自動化しています。GitHub Actions 上で、リサーチャーが2日ごとに Issue を調査・週次で新しい絵本の企画を起票し、作成者エージェントが承認済み Issue を実装して Draft PR を出し、「1歳児視点のレビュワーエージェント」が実際にブラウザで触ってレビューコメントを書く。人間(自分)はラベルを貼るのとマージだけ。つまり「AIが書き、AIがPRを出し、人間はほぼコードを読まない」リポジトリでして、この記事の問題意識そのものなんですよね。だからこそ、構造の見張り役が欲しい。

依存グラフを描く

構造は単純で、絵本5冊(densha / hikouki / kuruma / otenki / yorunosora)がそれぞれ index.html + config.js を持ち、共有の shared/ehon.js(絵本エンジン)と shared/baby.js を読み込みます。依存は2種類あります。

  1. <script src> による静的依存(HTMLから機械的に抽出できる)
  2. window.BOOK_CONFIG / window.BABY というグローバル変数契約による暗黙依存ehon.js が各 config.js の定義するグローバルを読む)
flowchart TD
    Top[index.html トップページ]
    subgraph Densha[densha 電車]
        DenshaP[index.html]
        DenshaC[config.js]
    end
    subgraph Hikouki[hikouki 飛行機]
        HikoukiP[index.html]
        HikoukiC[config.js]
    end
    Others[kuruma / otenki / yorunosora<br>他3冊も同じ構造]
    subgraph Shared[shared 共有モジュール]
        Ehon[ehon.js 絵本エンジン]
        Baby[baby.js 赤ちゃん情報]
    end
    Top --> Baby
    DenshaP --> DenshaC
    DenshaP --> Ehon
    DenshaP --> Baby
    HikoukiP --> HikoukiC
    HikoukiP --> Ehon
    HikoukiP --> Baby
    Others --> Ehon
    Ehon -.->|window.BOOK_CONFIG| DenshaC
    Ehon -.->|window.BOOK_CONFIG| HikoukiC
    Ehon -.->|window.BABY| Baby

(実線 = <script src> の静的依存、点線 = グローバル変数契約の暗黙依存)

40行で計測する

networkx を使えばこのくらいで書けます。

"""baby-ehon の依存グラフを抽出してネットワーク指標を計算するデモ."""
import re
from pathlib import Path
import networkx as nx

ROOT = Path.home() / "baby-ehon"
G = nx.DiGraph()

# 1. <script src> による静的依存(HTML → JS)
for html in ROOT.rglob("index.html"):
    page = str(html.relative_to(ROOT))
    for m in re.finditer(r'<script src="([^"]+)"', html.read_text()):
        target = (html.parent / m.group(1)).resolve().relative_to(ROOT)
        G.add_edge(page, str(target))

# 2. グローバル変数契約による暗黙依存
G.add_edge("shared/ehon.js", "shared/baby.js")  # window.BABY
for config in ROOT.rglob("config.js"):
    G.add_edge("shared/ehon.js", str(config.relative_to(ROOT)))  # window.BOOK_CONFIG

# 媒介中心性・SCC・Propagation Cost
bc = nx.betweenness_centrality(G)
sccs = [c for c in nx.strongly_connected_components(G) if len(c) > 1]
tc = nx.transitive_closure(G)
n = G.number_of_nodes()
pc = tc.number_of_edges() / (n * (n - 1))

このデモで出てくる指標を、先に一言ずつ。

  • fan-in / fan-out — fan-in は「そのファイルに依存している数(入ってくる矢印)」、fan-out は「そのファイルが依存している数(出ていく矢印)」。fan-in が大きいほど、多くの場所から使われる中心的なモジュール
  • 媒介中心性(betweenness centrality) — そのノードが、他のノード同士をつなぐ最短経路の上にどれだけ乗っているか。高いほどモジュール間の「関所」で、ここが詰まると全体に影響が及ぶ(Zimmermann らの研究で欠陥との相関が示された代表格)
  • 循環依存(SCC=強連結成分) — 互いに依存し合って輪になっているノードの塊。大きいほど分離・テスト・交換が難しく、構造が腐り始めているサイン
  • Propagation Cost(伝播コスト) — 前述のとおり、ランダムに選んだ1要素を変えたとき波及する要素の平均割合。直接依存だけでなく間接依存(推移閉包)も数える

結果(13ノード・22エッジ)

ノード fan-in fan-out 媒介中心性
shared/ehon.js 5 6 0.152
shared/baby.js 7 0 0.000
各 config.js 2 0 0.000
各 index.html 0 3 0.000
  • 循環依存(SCC): なし
  • Propagation Cost: 26.9%
  • 変更時の波及ファイル数: shared/baby.js → 7、各 config.js → 6、shared/ehon.js → 5

13ノードの玩具プロジェクトでも、指標はちゃんと構造を言い当ててくれました。

  • ehon.js だけが媒介中心性を持つ唯一のハブ。「ここを触るときだけは慎重に」が定量的に出てくる。さっき見たスケールフリー性(少数のハブに依存が集中)のミニチュア版です
  • 面白かったのは config.js の波及数が6と、ハブ本体より大きいこと。window.BOOK_CONFIG という暗黙のグローバル契約を通じて、1冊の設定変更が(契約レベルでは)エンジン経由で全絵本に波及しうるため。ファイル単位の静的指標では絶対に見えない「隠れた結合」で、MacCormack らの言う Hidden Structure の最小例です
  • なおこの波及数は到達可能性ベースの保守的な過大評価(実行時は自分の絵本にしか影響しない)。とはいえ「契約を変えたら全員に影響しうる」という警告としては正しく機能します

実際、AIエージェントは <script src> を増やすことはあっても、グローバル契約の結合を整理してはくれませんでした。それをコードを読まずに検知できるのが、この40行のグラフ分析というわけです。

そして、こうした指標は1回測って終わりにするより、コミットやリリースのたびに記録して時系列のデータとしてウォッチするといいかもしれません。絶対値そのものより「ハブの媒介中心性がじわじわ上がっていないか」「Propagation Cost が悪化していないか」という変化のほうが、構造劣化のサインとしては効くからです。AIが増築を続けるリポジトリほど、この計器を回し続ける価値がありそうです。

まとめ

  • 静的指標(LOC・循環的複雑度)は「人間がコードを読んで直す」時代に最適化された指標だった
  • AI駆動開発で劣化するのは関数の中身ではなくシステム構造。GitClear のデータ(重複増・リファクタ減・チャーン増)はその兆候
  • 依存グラフはスケールフリーで、負債は「ハブ」に偏在する(Myers 2003)。平均ではなくネットワーク上の位置を見る
  • ネットワーク指標は複雑度指標より欠陥をよく予測し(Zimmermann & Nagappan 2008)、Propagation Cost や Decoupling Level は負債の監視・返済の実績がある。近年はGNNへ発展
  • 13ノードの絵本リポジトリでも、40行のグラフ分析でハブと「グローバル変数契約の隠れ結合」が浮かび上がった
  • 指標は1回測って終わりにせず、コミット・リリースごとに記録して時系列でウォッチすると、構造劣化を変化として早めに捉えられる

コードを読まなくなったからこそ、コードのつながり方を見る。まずは手元のリポジトリの依存グラフを40行のスクリプトで眺めてみるところから、いかがでしょうか。


参考文献


作成: 2026-06-16 / 最終更新: 2026-06-27