Skip to content

本当に効くのか

「hotspot から直せば投資対効果が最大」は仮説です。このページは、それを反証しようとした記録です。対象は誰でも確認できる公開リポジトリです。

条件

成熟した TypeScript リポジトリ 8 件、計 202,604 コミット

リポジトリ履歴ファイルtest 期間の fix 対象
vuejs/core2018–478185
jestjs/jest2014–931111
vitejs/vite2020–560220
microsoft/playwright2019–1398657
nrwl/nx2017–47122200
angular/angular2014–5604371
nestjs/nest2017–167621(除外)
storybookjs/storybook2016–398622(除外)

手法: 分割日以前の 12 か月(dowsing の既定期間)で学習し、直後の 12 か月で評価します。この分割が重要で、同一期間で測ると fix 自身が churn に入り、指標が自分の入力を予測しているだけになります。

欠陥ラベルは、conventional commit の fix: 接頭辞 かつ issue 参照を要求しました。nest と storybook は評価から除外しています —— どちらも squash merge で規約が守られておらず、厳格なラベルでは fix 対象が 30 件を切ったためです。これは結果ではなく制約です。

負ける場所: 全体の順位

露出量の交絡を除くため「test 期間に触られたファイル」を母集団とし、AUC で並べます。

リポジトリhotspotchurnLoC
vuejs/core0.6410.5890.685
jestjs/jest0.6050.5940.625
vitejs/vite0.6790.6480.719
microsoft/playwright0.6370.6360.722
nrwl/nx0.6410.6290.684
angular/angular0.7000.6740.724

8 件中 7 件で、LoC 単独が hotspot を上回りました。 欠陥予測の研究ではファイルサイズが「打ち破れない基準線」として繰り返し報告されており、既知の現象です。train 期間を 20 年に広げても、ラベルを緩めても変わりませんでした。

リポジトリ全体の順位しか見ないのであれば、wc -l は十分に競争力のある道具であり、dowsing を入れる理由は薄いということです。

勝つ場所: 上位

しかし AUC は順序全体を測る指標で、実際の使われ方ではありません。実際には「上位 10〜20 件を見る」ので、そこを測ります。基準率がリポジトリごとに 0.4〜41% と大きく違うため、リフト(基準率の何倍当てたか)で揃えます。

リポジトリ基準率hotspotchurnLoC
vuejs/core38.7%2.3x1.9x2.2x
jestjs/jest11.9%4.2x4.6x4.2x
vitejs/vite37.1%2.6x2.4x2.7x
microsoft/playwright37.8%2.6x2.6x2.4x
nrwl/nx41.2%2.3x2.4x1.2x
angular/angular5.7%10.4x10.4x5.2x
平均4.1x4.1x3.0x

上位 20 件では hotspot が LoC を明確に上回ります(4.1x vs 3.0x)。差が最大なのは nx(2.3x vs 1.2x —— LoC はほぼランダム)と angular(10.4x vs 5.2x)です。サイズは「大きいが安定したファイル」を上位に積み上げますが、変更頻度を掛けるとそれが落ちます。

相関は全体にあり、価値は上位にあります。 だから評価指標は AUC ではなく precision@K であるべきです。

複雑度は何に効いているのか

このデータでは churn 単独が hotspot と同点(4.1x vs 4.1x)で、複雑度を掛ける意味が観測できません。これは中心となる式そのものを否定します。

原因は正例の定義でした。何を欠陥と数えるかを変えると:

正例の定義hotspotchurnLoC
1 回以上 fix された4.1x4.1x3.0x
3 回以上 fix された(慢性)14.4x12.2x7.7x
5 回以上 fix された16.3x15.5x8.4x

慢性ファイルでは、5 件中 4 件で hotspot が churn を上回ります(vue +2.0 / vite +1.4 / playwright +0.4 / angular +8.5、負けは nx の −1.0 のみ)。

読み方はこうです。複雑度は「壊れるかどうか」ではなく「一度で直しきれるかどうか」に効いています。 単純なファイルは 1 回直して終わり、複雑なファイルは同じ場所を何度も直すことになる。これは churn × complexity という式が何のためにあるかについての、検証可能な具体的主張であり、このツールが存在意義を持つ条件でもあります。

使うときに何を意味するか

  • 順位ではなく上位を読む。 このデータが支持するのは「上位 10〜20 件は見る価値がある」であって、「400 番目は 380 番目より安全」ではありません。
  • 狙うのは慢性ファイル。 繰り返し直されていて、まだ直され続けているなら、そこで複雑度がコストになっています。1 回壊れただけのファイルは、このツールを使う理由になりません。
  • 全体の相関は導入理由になりません。 それは LoC でほぼ得られます。LoC にできないのは、何度も戻ってくるファイルを見つけることと、依存関係がないのに一緒に変わるパッケージを見つけることです。

この検証の限界

  • 採点対象は 6 リポジトリで、いずれも人気の OSS ライブラリ・フレームワークです。アプリケーションのコードベースや非公開のモノレポでは違う挙動になる可能性があります。
  • fix: + issue 参照は「ここに欠陥があった」の代理指標にすぎません。規約に従わずに直された欠陥は数えられず、欠陥でない fix は数えてしまいます。
  • 分割日はリポジトリごとに 1 つだけで、複数の期間での交差検証はしていません。
  • ランキングに従って行動した結果、実際に改善したかは、ここでは一切測っていません。それには変更の前後を測る必要があり、dowsing diff がその役目です。

dowsing の閾値と重みはすべて仮説であり、そう明示しています。再現して違う答えが出たなら、それは有用な結果です —— issue を立ててください

MIT ライセンスで公開されています