Skip to content

なぜこの指標か

dowse が出す数値はすべて選択の結果で、選択にはそれぞれ出典があります。このページはその出典と、より実用的には各指標がどこから信用できなくなるかをまとめたものです。

先に押さえる 4 点

個々の指標の前に、全体に効く注意が 4 つあります。

  1. 複雑度プロキシは互いに強く相関します。 LoC / 字下げ深さ / cyclomatic / cognitive は、hotspot の第一近似としては似た順位を出します。どれも「理解のしにくさ」の完全な代理ではありません。
  2. 「結合度」には分母の異なる複数の定義があります。 どれを使うかで数字が実質的に変わります。dowse は 1 つに固定し、それを明示します。
  3. これらの多くは欠陥との相関であって因果ではありません。 優先順位付けのヒューリスティックとして使ってください。
  4. 式の精度より、履歴データの汚染のほうが結果を左右します。 追跡されないリネーム、整形コミット、squash。だから 数字を信じてよいか が別ページとして存在します。

Hotspot

発想は Adam Tornhill によるもので、hotspot とは「繰り返し対処せざるを得ない複雑なコード」です。その土台には先行研究があります。

  • Graves et al. (2000, TSE) ―― 過去の変更回数は、行数よりも優れた欠陥予測子である。
  • Nagappan & Ball (2005, ICSE) ―― 相対 churn が欠陥密度を高い精度で予測する。

原典では固定の積ではなく 2 次元の交差として扱われます。dowse が正規化ランクの積を実装しているのは、優先順位付けのツールが最終的にランキングを出す必要があるためです。

LoC は複雑度の代理として妥当か

ファイル単位ではおおむね妥当です。Jay et al. (2009) は 120 万ファイルを調べ、cyclomatic ≈ a·LoC + b、R² ≈ 0.90 を報告しています。

重要な但し書きは、関数単位に正規化すると相関は弱まることです。したがってファイルのランキングには LoC で十分で、重要なファイルが見つかったら関数レベルへ降りるべきです(dowse health --file が関数ごとの数値を出すのはこのためです)。

Change coupling

概念は Gall et al. (1998, ICSM)(論理結合)、それを拡張した Zimmermann et al. (2005, TSE)(相関ルール)、欠陥との関係を調べた D'Ambros et al. (2009, WCRE) に由来します。

|A| を A を変更したコミット数、|A∩B| を両方を変更したコミット数とすると:

流派分母対称
Tornhill / Code Maat / CodeScene平均2·|A∩B|/(|A|+|B|)
Zimmermann(confidence)|A||A∩B|/|A|×
Jaccard和集合|A∩B|/(|A|+|B|−|A∩B|)
minmin(|A|,|B|)|A∩B|/min(|A|,|B|)

dowse は既定で対称の Tornhill / Code Maat 方式を採り、方向性のある confidence も併せて出します。この選択は効きます。同じパッケージ対でも表のどの行を選ぶかで結合度は大きく変わるので、どれを使ったか言わないツールの数字は、そもそも解釈できません。

既定の閾値の出典

出典最小リビジョン最小共有最小結合度巨大コミット除外
Code Maat5530%30 ファイル超
CodeScene101050%50 ファイル超
Zimmermann/ROSEsupport > 1confidence > 0.530 entities 超

dowse は Code Maat の値に、CodeScene の大きめの changeset 閾値を組み合わせています。これらは出発点であって真理ではありません。 30% で何も出なければ下げてください。26% でも共変更 90 回のペアは、何かを語っています。

Cognitive complexity

Campbell / SonarSource。 出発点の主張は、cyclomatic はテスト容易性のための指標であって理解容易性の尺度としては不適だ、というものです。cognitive complexity は「プログラマの直感を数値化する」ことを明示的に狙って設計されています。

3 つのルール:

  1. 複数の文を 1 つに読みやすくまとめる短縮構文は無視する。
  2. 線形フローの中断ごとに +1。
  3. その中断がネストしているほど加点する。
種別ネスト加点ネスト深度を増やす
Structuralありありif, for, while, catch, switch, 三項
Hybridなしありelse if, else
Fundamentalなしなし論理演算子の連鎖、再帰、ラベル付き break

実際上の帰結として、a && b && c は演算子ごとではなく 1 回だけ数え、3 段ネストした if はトップレベルの if よりはるかに高くつきます。読む労力を測ると称する指標には、これが望ましい挙動です。

Code Health

dowse の health は、意図的に Maintainability Index ではありません

MI は回帰多項式で、導出の不透明さを強く批判されています(van Deursen, 2014)。入力がすべてサイズと相関し、平均化がリスクの高い箇所を隠します。係数も資料によって割れています。dowse は使いません。他所で MI 風の単一スコアを見かけたら、それが何でできているかを確認してください。

代わりに:

health = 10 − Σ(weight × severity)

すべての項が見え、重みは設定でき、どの smell が何点引いたかを出力します。MI より単純なモデルですが、要点はあなたが検算できることです。

Ownership / bus factor

ownership は blame ではなく追加行数で決めます。 git blame は行を「最後に触った人」に帰属させるため、ロジックを書いた人より整形を通した人を著者にしてしまいます。履歴全体の追加行数を数えるほうが、誰がそのコードを知っているかの安定した信号になります。

minor contributor は ownership 5% 未満 ―― Bird et al. による。minor contributor が多いことと欠陥の多さに相関があります。

bus factorAvelino et al. (ICPC 2016) に従います。開発者 × ファイルごとに Degree-of-Authorship を計算し、担当ファイル数が最大の開発者から順に除去して、著者を持つファイルが半分を切った時点の人数を取ります。DOA の係数は Fritz et al. (ICSE 2010) 由来です。

DOA(d,f) = 3.293 + 1.098·FA + 0.164·DL − 0.321·ln(1 + AC)

これはチームのリスク指標であって、人事評価ではありません

Tornhill も同じ警告をしています。「bus factor 1」は、そのパッケージの知識が 1 人に集中していることを示すだけです。その人が何か悪いことをしているとは言っていません ―― 普通は逆です。

この数値には 1 つ特有の脆さがあります。開発者が複数のメールアドレスでコミットしていて .mailmap が統合していない場合、dowse は別人として数え、bus factor を過大評価します。パッケージが実際より安全に見えるということです。この失敗は無言で起きるので、明示しておく価値があります。

関連

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