「AIで11万行書いた」とは言えなかった話

33日間の実績をgitから出したら、こういう数字になった。

コミット      129件
追加         118,443行
削除          15,350行
触れたファイル  1,960件

11万行。1日あたり3,600行。個人開発でこの数字が出ると、つい「AIすごい」と書きたくなる。実際そういう記事はよく見る。

内訳を出したら、書けなくなった。

37%しか手で書いていない

33日間でgitに入った追加行 118,443行の内訳

パスごとに分けると、こうなった。

種類 行数 割合
手で書いたソース 44,234 37%
別プロジェクトのビルド成果物 32,240 27%
このリポジトリの生成物 20,341 17%
その他のデータファイル 21,628 18%

27%は他のプロジェクトのビルド出力だった。別リポジトリで開発しているReactアプリを、公開用にビルドしてこのリポジトリの public/ へコピーしている。バンドルされたJavaScriptなので、1ファイルで29,062行ある。これがコミットに含まれると、行数は一瞬で膨らむ。

17%は自分のリポジトリの生成物。Markdownから生成した記事HTMLと、スクリプトで作ったプリセットのJSON。元になっている .md は手で書いているが、生成されたHTMLも同時にコミットしているので、同じ内容が2回数えられている。

残り18%は設定や索引などのデータファイル。

差し引いて手で書いたと言えるのは44,234行、全体の37%だった。これでも1日1,300行あるので少なくはないが、11万行とは3倍近い開きがある

3倍の差はリポジトリの都合でしかない

気持ち悪いのは、この差が開発の中身と何の関係もないことだ。

ビルド成果物をリポジトリに入れるかどうかは、単なる運用の選択でしかない。.gitignore に1行足せば32,240行は消える。デプロイの方法を変えれば、生成HTMLも入らなくなる。作業量は1行も変わらないのに、指標だけが3分の1になる。

つまり「AIで何行書いた」は、リポジトリに何を置いているかを測っているのであって、開発の量を測っていない。他人の数字と比べる意味はほとんど無いし、自分の月ごとの比較すら、途中で運用を変えたら成立しない。

もうひとつ、この指標を壊すものがある。改行コードだ。Windowsでスクリプトからファイルを書き換えると、LFCRLF に変換されて1行の修正が全行の変更として記録されることがある。500行のファイルなら、1文字直しただけで500行の差分になる。私は実際にこれを踏んで、レビュー不能なコミットを作った。行数を指標にするなら、この事故は「生産性の向上」として記録される。

では何を測ればいいのか

代わりの指標をいくつか考えたが、どれも決め手に欠けた。

コミット数(129件)は、まだましだと思う。生成物の量に引きずられない。ただし人によって粒度が違いすぎて、他人と比べる意味はやはり無い。自分の中での比較にしか使えない。

触れたファイル数(1,960件)は、生成物を含む点で行数と同じ弱さがある。

AIの応答回数やツール実行回数は、ログから取れて生成物の影響も受けない。ただしこれは「AIがどれだけ動いたか」であって、成果ではない。同じ結果に23回で辿り着いても100回かかっても、成果物は同じだ。

結局のところ、開発の量をひとつの数字で表す方法は無い、という当たり前のところに戻ってきた。

数字を出すこと自体は無駄ではない

とはいえ、この集計をやってよかったと思っている。理由は指標が得られたからではなく、リポジトリに何が入っているかが分かったからだ。

27%を占めていた別プロジェクトのビルド成果物は、そこにあると知らなければ気づけない。実際この作業の前に、そのディレクトリのHTMLを直接編集して、次のビルドで消えるという事故を起こしている。「ここはビルド出力である」という事実が頭に入っていなかった。

行数の内訳を出すというのは、要するにリポジトリの地図を描く作業だった。どこが手書きで、どこが自動生成で、どこが外から来たコピーなのか。それが分かっていれば、編集してはいけない場所を編集せずに済む。

やり方

パスで絞って --numstat を足すだけなので、数分で出せる。

# 全体
git log --since=2026-06-26 --numstat --format="" \
  | awk '{add+=$1; del+=$2} END {printf "追加 %d / 削除 %d\n", add, del}'

# 特定のパスだけ
git log --since=2026-06-26 --numstat --format="" -- 'scripts/*' '*.md' \
  | awk '{add+=$1} END {print add}'

# どのファイルが行数を稼いでいるか
git log --since=2026-06-26 --numstat --format="" \
  | awk '$1!="-" {a[$3]+=$1} END {for (f in a) print a[f], f}' \
  | sort -rn | head

最後のコマンドが効く。上位に自分が書いた覚えのないファイルが並んでいたら、それがリポジトリに紛れ込んでいる生成物だ。私の場合は1位が29,062行のバンドルファイルで、そこで手が止まった。

「11万行書いた」と言えなくなったのは残念だが、言えないと分かったことのほうが役に立った