「AIで11万行書いた」とは言えなかった話
33日間の実績をgitから出したら、こういう数字になった。
コミット 129件
追加 118,443行
削除 15,350行
触れたファイル 1,960件
11万行。1日あたり3,600行。個人開発でこの数字が出ると、つい「AIすごい」と書きたくなる。実際そういう記事はよく見る。
内訳を出したら、書けなくなった。
37%しか手で書いていない
パスごとに分けると、こうなった。
| 種類 | 行数 | 割合 |
|---|---|---|
| 手で書いたソース | 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でスクリプトからファイルを書き換えると、LF が CRLF に変換されて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万行書いた」と言えなくなったのは残念だが、言えないと分かったことのほうが役に立った。