システム開発の品質指標とは、レビューやテストの量と、見つかった指摘・バグの数を、規模で割って比べられる形にした数値です。 代表はレビュー指摘密度(件/ページ、件/KLOC)、テスト密度(件/KLOC)、バグ密度(件/KLOC)の3つで、「十分に確かめたか」と「どれだけ見つかったか」を組にして読みます。

この記事では、工程別の指標22個を計算式つきの一覧表にまとめ、架空の備品予約システム(規模24.0 KLOC)のデータで計算例を示します。さらに、テスト密度とバグ密度の9ゾーン判定と、信頼度成長曲線による残存バグ数の推定を、コピーして動かせるPythonコードと実行結果で説明します。

確認環境:Ubuntu 24.04、Python 3.11.15、NumPy 2.4.4、SciPy 1.17.1。表計算の数式は LibreOffice Calc 24.2.7 のヘッドレス実行で再計算し、Pythonの結果と同じ値になることを確かめています。記事中の数値はすべて説明用の架空データです。

品質指標の早見表(22個)

KLOC はコード1,000行、「集める時点」はその数値が確定するタイミングです。規模は KLOC の代わりにファンクションポイント(FP)や画面数でも構いません。分母を案件の途中で変えないことが比較の前提になります。

No 工程 指標 計算式 単位 集める時点 読み方
1 設計 レビュー工数密度(設計書) レビュー工数 ÷ 設計書ページ数 時間/ページ レビュー会ごと 低いと確かめる量が足りていない可能性
2 設計 レビュー指摘密度(設計書) 指摘件数 ÷ 設計書ページ数 件/ページ レビュー会ごと No.1と組で読む。工数が少なく指摘も少ないときは追加レビューを検討
3 設計 重大指摘の割合 重大な指摘件数 ÷ 指摘件数 × 100 % レビュー会ごと 誤字より仕様の漏れ・矛盾が見つかっているかを見る
4 設計 指摘修正完了率 修正済み指摘件数 ÷ 指摘件数 × 100 % 工程完了判定の前 100%で次工程へ。残りは課題として引き継ぐ
5 実装 レビュー工数密度(コード) レビュー工数 ÷ 規模 時間/KLOC プルリクエストごと、週次 低いと流し読みになっている可能性
6 実装 レビュー指摘密度(コード) 指摘件数 ÷ 規模 件/KLOC プルリクエストごと、週次 機能や担当チーム間で比べ、離れた値の理由を確認
7 実装 静的解析の警告密度 警告件数 ÷ 規模 件/KLOC ビルドごと 増加に転じたら規約とのずれを確認
8 単体テスト コードカバレッジ 実行された行(分岐)÷ 全行(全分岐)× 100 % ビルドごと 通っていない分岐の一覧を見るのが目的。数値だけを目標にしない
9 単体テスト 単体テスト密度 単体テストケース数 ÷ 規模 件/KLOC 単体テスト完了時 機能間で比べ、少ない機能の観点を確認
10 結合・システムテスト テスト密度 テストケース数 ÷ 規模 件/KLOC テスト設計完了時、完了時 確かめた量。バグ密度と組で9ゾーン判定に使う
11 結合・システムテスト バグ密度 検出バグ数 ÷ 規模 件/KLOC 週次、テスト完了時 見つかった量。高低だけでなくテスト密度との組み合わせで読む
12 結合・システムテスト テスト消化率 実施済みケース数 ÷ 予定ケース数 × 100 % 日次 進み具合。予定線との差を見る
13 結合・システムテスト テスト合格率 合格ケース数 ÷ 実施済みケース数 × 100 % 日次 消化率が高く合格率が低いときは再テストの量を見積もる
14 結合・システムテスト バグ検出率 検出バグ数 ÷ 実施済みケース数 × 100 件/100ケース 日次、週次 終盤に下がっていけば収束の手がかり
15 結合・システムテスト バグ修正完了率 修正完了バグ数 ÷ 検出バグ数 × 100 % 日次 検出に修正が追いついているか
16 結合・システムテスト 未解決バグ数 累積検出数 − 累積修正完了数 件 日次 増え続けるときは修正の体制を見直す
17 結合・システムテスト 再オープン率 再オープンしたバグ数 ÷ 修正完了バグ数 × 100 % 週次 高いときは修正の確認手順を見直す
18 結合・システムテスト 平均修正日数 修正完了までの日数の合計 ÷ 修正完了バグ数 日 週次 重要度別に出すと滞留が見える
19 テスト全体 バグ収束率 累積検出数 ÷ 推定総バグ数 × 100 % テスト後半は日次 信頼度成長曲線で推定した総数に対する割合
20 テスト全体 前工程からの流出率 後工程で見つかった前工程起因のバグ数 ÷ その工程起因の指摘・バグ総数 × 100 % 工程完了時 レビューで受け止めきれなかった割合
21 リリース 欠陥除去率(DRE) リリース前の検出数 ÷(リリース前の検出数 + リリース後の検出数)× 100 % リリース後1〜3か月 出荷前に取り除けた割合
22 リリース リリース後バグ密度 リリース後の不具合数 ÷ 規模 件/KLOC リリース後1〜3か月 次の案件の目標値を決める材料

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「品質管理の指標一覧(システム開発)|バグ密度・レビュー指摘密度の計算式」

表計算ソフトに貼れるタブ区切り版です(指標名・計算式・単位)。

No  指標  計算式 単位
1   レビュー工数密度(設計書)   レビュー工数÷設計書ページ数  時間/ページ
2   レビュー指摘密度(設計書)   指摘件数÷設計書ページ数    件/ページ
3   重大指摘の割合 重大な指摘件数÷指摘件数×100    %
4   指摘修正完了率 修正済み指摘件数÷指摘件数×100   %
5   レビュー工数密度(コード)   レビュー工数÷規模   時間/KLOC
6   レビュー指摘密度(コード)   指摘件数÷規模 件/KLOC
7   静的解析の警告密度   警告件数÷規模 件/KLOC
8   コードカバレッジ    実行された行÷全行×100   %
9   単体テスト密度 単体テストケース数÷規模    件/KLOC
10  テスト密度   テストケース数÷規模  件/KLOC
11  バグ密度    検出バグ数÷規模    件/KLOC
12  テスト消化率  実施済みケース数÷予定ケース数×100 %
13  テスト合格率  合格ケース数÷実施済みケース数×100 %
14  バグ検出率   検出バグ数÷実施済みケース数×100  件/100ケース
15  バグ修正完了率 修正完了バグ数÷検出バグ数×100   %
16  未解決バグ数  累積検出数−累積修正完了数   件
17  再オープン率  再オープンしたバグ数÷修正完了バグ数×100  %
18  平均修正日数  修正日数の合計÷修正完了バグ数 日
19  バグ収束率   累積検出数÷推定総バグ数×100    %
20  前工程からの流出率   後工程で見つかった前工程起因のバグ数÷その工程起因の総数×100    %
21  欠陥除去率(DRE)  リリース前の検出数÷(リリース前+リリース後の検出数)×100 %
22  リリース後バグ密度   リリース後の不具合数÷規模   件/KLOC

指標は「量」と「結果」を組にして読む

品質指標は1つだけ見ても判断がつきません。確かめた量(工数密度・テスト密度)と、見つかった結果(指摘密度・バグ密度)を必ず組にします。

確かめた量 見つかった結果 まず考えること
多い 少ない 品質が良い可能性が高い。観点の偏りだけ確認
多い 多い よく見つけている。原因が特定の工程や機能に集まっていないか確認
少ない 多い 少し見ただけで多く見つかっている。範囲を広げて確かめる
少ない 少ない 見ていないので見つかっていない可能性。量を増やして確かめ直す

「バグが少ない=品質が良い」と読めるのは、確かめた量が十分なときだけです。後半の9ゾーン判定は、この表を3×3に細かくしたものです。

バグ密度・レビュー指摘密度の計算例

架空の備品予約システム(24.0 KLOC、設計書180ページ)を題材に、早見表のうち15個をまとめて計算します。手計算で追えるよう、主な式も並べます。

指標 式 結果
レビュー指摘密度(設計書) 126件 ÷ 180ページ 0.700 件/ページ
レビュー指摘密度(コード) 168件 ÷ 24.0 KLOC 7.000 件/KLOC
テスト密度(結合) 1,250件 ÷ 24.0 KLOC 52.083 件/KLOC
バグ密度(結合) 31件 ÷ 24.0 KLOC 1.292 件/KLOC
欠陥除去率 331件 ÷(331件 + 2件)× 100 99.399 %

欠陥除去率の331件は、設計レビュー126件、コードレビュー168件、結合テスト31件、システムテスト6件の合計です。

# 品質指標の計算例(架空データ:備品予約システム、規模 24.0 KLOC)
kloc = 24.0
doc_pages, review_hours, review_findings = 180, 36.0, 126
code_review_hours, code_findings = 40.0, 168
it_cases, it_bugs = 1250, 31
st_cases, st_bugs = 410, 6
executed, planned, passed = 1180, 1250, 1121
closed, found = 29, 31
reopen = 2
release_bugs = 2   # リリース後3か月の不具合

rows = [
    ("レビュー工数密度(設計書)", review_hours / doc_pages, "時間/ページ"),
    ("レビュー指摘密度(設計書)", review_findings / doc_pages, "件/ページ"),
    ("レビュー工数密度(コード)", code_review_hours / kloc, "時間/KLOC"),
    ("レビュー指摘密度(コード)", code_findings / kloc, "件/KLOC"),
    ("テスト密度(結合)", it_cases / kloc, "件/KLOC"),
    ("バグ密度(結合)", it_bugs / kloc, "件/KLOC"),
    ("テスト密度(システム)", st_cases / kloc, "件/KLOC"),
    ("バグ密度(システム)", st_bugs / kloc, "件/KLOC"),
    ("テスト消化率", executed / planned * 100, "%"),
    ("テスト合格率", passed / executed * 100, "%"),
    ("バグ検出率(100ケースあたり)", it_bugs / it_cases * 100, "件/100ケース"),
    ("バグ修正完了率", closed / found * 100, "%"),
    ("再オープン率", reopen / closed * 100, "%"),
    ("欠陥除去率(DRE)", (review_findings + code_findings + it_bugs + st_bugs)
        / (review_findings + code_findings + it_bugs + st_bugs + release_bugs) * 100, "%"),
    ("リリース後バグ密度", release_bugs / kloc, "件/KLOC"),
]
for name, value, unit in rows:
    print(f"{name:<22} {value:8.3f} {unit}")

実行結果です。

レビュー工数密度(設計書)             0.200 時間/ページ
レビュー指摘密度(設計書)             0.700 件/ページ
レビュー工数密度(コード)             1.667 時間/KLOC
レビュー指摘密度(コード)             7.000 件/KLOC
テスト密度(結合)                52.083 件/KLOC
バグ密度(結合)                  1.292 件/KLOC
テスト密度(システム)              17.083 件/KLOC
バグ密度(システム)                0.250 件/KLOC
テスト消化率                   94.400 %
テスト合格率                   95.000 %
バグ検出率(100ケースあたり)          2.480 件/100ケース
バグ修正完了率                  93.548 %
再オープン率                    6.897 %
欠陥除去率(DRE)               99.399 %
リリース後バグ密度                 0.083 件/KLOC

単位をそろえるときの注意

迷いやすい点 そろえ方
規模に何を使うか KLOC・FP・画面数のどれかに決め、案件の最初に書き残す。コメント行と空行を含めるかも決める
改修案件の規模 追加・変更した行数を分母にする。流用しただけの部分を含めると密度が小さく見える
バグの数え方 同じ原因の重複報告は1件にまとめる。仕様の問い合わせや環境起因は別の分類にする
指摘の数え方 誤字と仕様の漏れを同じ1件にしない。重要度(重大・中・軽微)を付けて集計する
ページ数 図表だけのページや表紙を含めるかを決める

基準値の決め方

密度の基準値は、言語・規模・数え方で大きく変わります。他の組織の数値をそのまま使うより、自分たちの過去の案件の中央値を出発点にすると、数え方の違いによるずれを避けられます。

  1. 過去の案件(5件以上あると扱いやすい)から、工程ごとの密度を集める。
  2. 平均ではなく中央値を取る。極端に大きい値が1件あると平均は引っ張られるためです。
  3. 中央値の上下に許容幅(例:四分位範囲、または±20〜30%)を取り、基準範囲とする。
  4. 案件の特徴(新規か改修か、難しさ)に合わせて計画時に範囲を調整し、根拠を書き残す。
  5. 案件が終わったら結果を過去データに加え、次の基準範囲を更新する。

過去データがまだ無いときは、同じ案件の中で機能同士を比べます。ほかの機能から離れた値を持つ機能に注目する読み方なら、基準値が無くても始められます。

テスト密度とバグ密度の9ゾーン判定をPythonで行う

テスト密度とバグ密度をそれぞれ「低・範囲内・高」に分けると、3×3で9つの領域ができます。機能ごとにどの領域に入るかを見て、次の手を決める方法です。ゾーンの番号の付け方は資料によって異なるので、この記事では次の配置で番号を振ります。

バグ密度\テスト密度 低 範囲内 高
高 ゾーン1 ゾーン2 ゾーン3
範囲内 ゾーン4 ゾーン5 ゾーン6
低 ゾーン7 ゾーン8 ゾーン9

基準範囲(テスト密度40〜60件/KLOC、バグ密度0.8〜1.6件/KLOC)は架空の値です。自分たちの過去データから決めた範囲に置き換えて使ってください。

# テスト密度×バグ密度の9ゾーン判定
# 基準範囲は「自組織の過去データの中央値±許容幅」を入れる(下の値は架空)
TEST_LOW, TEST_HIGH = 40.0, 60.0   # テスト密度の基準範囲(件/KLOC)
BUG_LOW, BUG_HIGH = 0.8, 1.6       # バグ密度の基準範囲(件/KLOC)

def band(value, low, high):
    return 0 if value < low else (2 if value > high else 1)   # 0=低 1=範囲内 2=高

# ZONE[バグ密度の帯][テスト密度の帯] = (ゾーン番号, 読み方, 次の手)
ZONE = {
    (2, 0): (1, "少ないテストで多くのバグ", "品質に懸念。前工程のレビュー記録を確認し、テストを追加"),
    (2, 1): (2, "標準のテスト量でバグが多め", "バグの原因工程を分類し、集中する機能を追加テスト"),
    (2, 2): (3, "テストもバグも多い", "テストは十分。バグが収束しているかを曲線で確認"),
    (1, 0): (4, "テストが少なくバグは標準", "テスト不足の可能性。未実施の観点を洗い出す"),
    (1, 1): (5, "どちらも基準範囲内", "計画どおり。内容(重要度・原因)を確認して完了判定へ"),
    (1, 2): (6, "テストが多くバグは標準", "良好。テストの重複がないかだけ確認"),
    (0, 0): (7, "テストもバグも少ない", "テスト不足で見つかっていない可能性。観点とケースを追加"),
    (0, 1): (8, "標準のテスト量でバグが少ない", "品質良好の可能性。テストの観点が偏っていないか確認"),
    (0, 2): (9, "多くのテストでバグが少ない", "品質良好。前工程のレビューが効いているかを記録に残す"),
}

def judge(name, kloc, cases, bugs):
    td, bd = cases / kloc, bugs / kloc
    no, reading, action = ZONE[(band(bd, BUG_LOW, BUG_HIGH), band(td, TEST_LOW, TEST_HIGH))]
    return f"{name:<6} テスト密度={td:5.1f} バグ密度={bd:4.2f} ゾーン{no} {reading} → {action}"

modules = [  # (機能, KLOC, テストケース数, バグ数)
    ("予約登録", 6.0, 330, 7),
    ("予約検索", 4.5, 150, 9),
    ("承認",   5.0, 320, 11),
    ("備品台帳", 3.5, 120, 1),
    ("通知",   2.0, 130, 1),
    ("集計出力", 3.0, 200, 3),
]
for m in modules:
    print(judge(*m))

実行結果です。

予約登録   テスト密度= 55.0 バグ密度=1.17 ゾーン5 どちらも基準範囲内 → 計画どおり。内容(重要度・原因)を確認して完了判定へ
予約検索   テスト密度= 33.3 バグ密度=2.00 ゾーン1 少ないテストで多くのバグ → 品質に懸念。前工程のレビュー記録を確認し、テストを追加
承認     テスト密度= 64.0 バグ密度=2.20 ゾーン3 テストもバグも多い → テストは十分。バグが収束しているかを曲線で確認
備品台帳   テスト密度= 34.3 バグ密度=0.29 ゾーン7 テストもバグも少ない → テスト不足で見つかっていない可能性。観点とケースを追加
通知     テスト密度= 65.0 バグ密度=0.50 ゾーン9 多くのテストでバグが少ない → 品質良好。前工程のレビューが効いているかを記録に残す
集計出力   テスト密度= 66.7 バグ密度=1.00 ゾーン6 テストが多くバグは標準 → 良好。テストの重複がないかだけ確認

読み取りのポイントは次のとおりです。

  • 備品台帳(ゾーン7)はバグ密度が0.29と低い一方、テスト密度も34.3と低いため、良好とは言い切れません。観点を追加して確かめる候補です。
  • 予約検索(ゾーン1)は少ないテストで多くのバグが出ています。設計レビューの記録に戻り、同じ原因が残っていないかを確認します。
  • 承認(ゾーン3)はテストもバグも多い機能です。次の節の成長曲線で、検出が落ち着いてきたかを確かめます。

判定は「合否」ではなく、どの機能から話を聞きに行くかを決める道具として使うと活きます。バグの重要度や原因分類とあわせて最終的な判断をします。

信頼度成長曲線の作り方(ゴンペルツ曲線・ロジスティック曲線)

信頼度成長曲線は、横軸にテストの経過(日数や消化ケース数)、縦軸に累積バグ検出数を取ったグラフです。テストが進むと新しいバグが見つかりにくくなり、曲線は横ばいに近づきます。この形にS字の曲線を当てはめると、最終的な総バグ数Kを推定でき、「K − 現在の累積」が残存バグ数の見積もりになります。

曲線 式 形の特徴
ゴンペルツ曲線 y = K × exp(−b × exp(−c × t)) 立ち上がりが早く、後半がゆっくり伸びる。Kの約37%の高さで傾きが最大
ロジスティック曲線 y = K ÷ (1 + b × exp(−c × t)) 左右対称のS字。Kの50%の高さで傾きが最大

K は推定総バグ数、b と c は曲線の位置と伸びの速さを決める係数、t は経過日数です。

手順

  1. 日次でバグの検出数を記録し、累積を出す(下のCSVひな形)。
  2. 累積の折れ線を描き、S字の後半(傾きがゆるむところ)に入っているかを目で見る。
  3. 2つの曲線を当てはめ、K と当てはまりの良さ(RMSE:実測と曲線の差の大きさ)を出す。
  4. 日数を変えて推定し直し、K が安定しているかを確かめる。
  5. 残存バグ数と、目標の検出率に届く日を出し、テスト完了判定の材料の1つにする。

日次バグ集計のCSVひな形

列は、テスト日(day)、当日の実施ケース数(tests_run)、当日の検出数(found)、累積検出数(cum_found)、累積修正完了数(cum_closed)、未解決数(open)です。このまま daily_bugs.csv として保存すると、次のコードで読み込めます。30日分の架空データです。

day,tests_run,found,cum_found,cum_closed,open
1,19,3,3,0,3
2,19,4,7,0,7
3,18,3,10,3,7
4,21,3,13,5,8
5,21,3,16,8,8
6,20,9,25,15,10
7,23,5,30,21,9
8,18,5,35,27,8
9,19,9,44,35,9
10,25,3,47,38,9
11,23,7,54,45,9
12,19,7,61,52,9
13,23,5,66,56,10
14,23,8,74,63,11
15,24,5,79,68,11
16,23,7,86,74,12
17,24,4,90,79,11
18,19,3,93,83,10
19,19,5,98,89,9
20,23,4,102,92,10
21,25,5,107,97,10
22,23,0,107,98,9
23,25,4,111,102,9
24,23,4,115,106,9
25,23,0,115,107,8
26,20,5,120,113,7
27,18,0,120,114,6
28,19,0,120,114,6
29,19,0,120,116,4
30,25,1,121,118,3

当てはめのコードと実行結果

SciPy の curve_fit は、指定した関数の係数を、実測との差の二乗和が最小になるように求めます。p0 は探索の出発点で、K には現在の累積より少し大きい値を入れておくと収束しやすくなります。

# 信頼度成長曲線:ゴンペルツ曲線とロジスティック曲線を当てはめて残存バグ数を推定
import csv
import numpy as np
from scipy.optimize import curve_fit

def gompertz(t, K, b, c):
    return K * np.exp(-b * np.exp(-c * t))

def logistic(t, K, b, c):
    return K / (1 + b * np.exp(-c * t))

with open("daily_bugs.csv", encoding="utf-8") as f:
    rows = list(csv.DictReader(f))
t = np.array([int(r["day"]) for r in rows], dtype=float)
y = np.array([int(r["cum_found"]) for r in rows], dtype=float)

def fit(name, func, p0, t, y):
    popt, pcov = curve_fit(func, t, y, p0=p0, maxfev=20000)
    K, b, c = popt
    se_K = np.sqrt(np.diag(pcov))[0]
    rmse = np.sqrt(np.mean((y - func(t, *popt)) ** 2))
    print(f"{name}: K={K:6.1f} (標準誤差 {se_K:4.1f})  b={b:7.3f}  c={c:.4f}  RMSE={rmse:.2f}"
          f"  残存={K - y[-1]:5.1f}件  検出率={y[-1] / K * 100:5.1f}%")
    return popt

print(f"データ {len(t)}日分 / 累積検出 {int(y[-1])}件")
g = fit("ゴンペルツ  ", gompertz, [y[-1] * 1.2, 5, 0.1], t, y)
l = fit("ロジスティック", logistic, [y[-1] * 1.2, 30, 0.2], t, y)

print("\n途中の日までのデータで推定したK(推定の安定を見る)")
for n in (15, 20, 25, 30):
    kg = curve_fit(gompertz, t[:n], y[:n], p0=[y[n-1] * 1.5, 5, 0.1], maxfev=20000)[0][0]
    kl = curve_fit(logistic, t[:n], y[:n], p0=[y[n-1] * 1.5, 30, 0.2], maxfev=20000)[0][0]
    print(f"  {n}日目まで: 累積={int(y[n-1]):3d}  ゴンペルツK={kg:6.1f}  ロジスティックK={kl:6.1f}")

print("\nこの先の予測(ゴンペルツ)")
for d in (35, 40, 45):
    print(f"  {d}日目: 累積 {gompertz(d, *g):6.1f}件")
target = 0.98
K, b, c = g
day98 = -np.log(-np.log(target) / b) / c
print(f"  検出率{target:.0%}({K * target:.1f}件)に届く日: {day98:.1f}日目")

実行結果です。

データ 30日分 / 累積検出 121件
ゴンペルツ  : K= 130.8 (標準誤差  1.0)  b=  3.977  c=0.1385  RMSE=1.26  残存=  9.8件  検出率= 92.5%
ロジスティック: K= 122.1 (標準誤差  1.3)  b= 16.320  c=0.2276  RMSE=2.37  残存=  1.1件  検出率= 99.1%

途中の日までのデータで推定したK(推定の安定を見る)
  15日目まで: 累積= 79  ゴンペルツK= 125.8  ロジスティックK=  92.2
  20日目まで: 累積=102  ゴンペルツK= 131.5  ロジスティックK= 109.2
  25日目まで: 累積=115  ゴンペルツK= 132.0  ロジスティックK= 118.0
  30日目まで: 累積=121  ゴンペルツK= 130.8  ロジスティックK= 122.1

この先の予測(ゴンペルツ)
  35日目: 累積  126.7件
  40日目: 累積  128.7件
  45日目: 累積  129.7件
  検出率98%(128.1件)に届く日: 38.1日目

結果の読み方

見るところ このデータでの値 読み方
推定総バグ数 K ゴンペルツ 130.8、ロジスティック 122.1 曲線の選び方で残存は約10件と約1件に分かれる。1つの数字ではなく幅で受け取る
RMSE 1.26 と 2.37 小さいほうがデータの形に合っている。この例ではゴンペルツ
途中までのデータでのK ゴンペルツは20日目以降131前後で安定、ロジスティックは92→109→118→122と増加中 日を追うごとにKが動く曲線は、まだ推定が定まっていない
検出率98%に届く日 38.1日目 30日目時点であと8日ほどの見込み。テスト期間の延長を検討する材料になる

この例のデータはゴンペルツ型に近い形で作ってあるため、ゴンペルツ曲線のほうがよく合います。実際のデータでどちらが合うかは案件ごとに違うので、両方を当てはめてRMSEとKの安定を比べるのが確実です。

当てはめがうまくいかない例と対処

状況 起きること 対処
累積がまだ直線的に伸びている K が非常に大きい値になる、または日によって大きく変わる 曲がり始めるまで待つ。それまではバグ検出率(No.14)の推移を見る
テストの量が日によって大きく違う 休みの日に横ばいになり、収束と見間違う 横軸を日数ではなく累積の消化ケース数にする
後半に新しい機能のテストを追加した 曲線が再び立ち上がる 機能のまとまりごとに分けて当てはめる
データが10点未満 係数が定まらない 点が増えるまで傾向の確認にとどめる

残存バグ数の推定は、テストの範囲と観点が十分であることを前提にした見積もりです。テスト消化率、未解決バグ数、重要度別の件数とあわせて判断します。

表計算ソフトで検算する数式

Pythonが使えない場面のために、同じ計算を表計算の数式で行う方法です。A列にテスト日、B列に累積検出数(2〜31行)を置き、G2セルに推定総バグ数 K の候補を入力します。ゴンペルツ曲線は、LN(LN(K÷y)) が日数の一次式になる性質を使うと、SLOPE と INTERCEPT で係数を求められます。

セル 内容 数式
C2(31行まで下へコピー) 一次式に直した値 =LN(LN($G$2/B2))
D2(31行まで下へコピー) 曲線の値 =$G$2*EXP(-$G$3*EXP(-$G$4*A2))
G2 K の候補 130.8(手で入力)
G3 係数 b =EXP(INTERCEPT(C2:C31,A2:A31))
G4 係数 c =-SLOPE(C2:C31,A2:A31)
G5 RMSE =SQRT(SUMXMY2(B2:B31,D2:D31)/COUNT(B2:B31))
G6 残存バグ数 =G2-B31
G7 バグ収束率 =B31/G2
G8 検出率98%に届く日 =-LN(-LN(0.98)/G3)/G4

LibreOffice Calc 24.2.7 で再計算した値です。K を変えて RMSE が最も小さくなる値を探します。

K の候補 b c RMSE 残存バグ数
125 4.768 0.1649 2.81 4
130.8 3.953 0.1378 1.26 9.8
135 3.696 0.1262 1.80 14

K = 130.8 のとき b = 3.953、c = 0.1378、RMSE = 1.26、検出率98%に届く日は38.3日目となり、Pythonの結果(b = 3.977、c = 0.1385、38.1日目)とほぼ一致します。わずかな差は、表計算側が一次式に直してから係数を求めているためです。K の値は累積検出数より大きくしてください(等しいか小さいと LN の中が0以下になりエラーになります)。

密度と9ゾーンの帯を出す数式も同じ方法で確かめています。

求めるもの 数式 結果
バグ密度(31件、24 KLOC) =ROUND(31/24,3) 1.292
テスト密度(1,250件、24 KLOC) =ROUND(1250/24,3) 52.083
バグ密度の帯(G9に密度) =IF(G9<0.8,"低",IF(G9>1.6,"高","範囲内")) 範囲内
テスト密度の帯(G10に密度) =IF(G10<40,"低",IF(G10>60,"高","範囲内")) 範囲内
欠陥除去率 =ROUND(331/(331+2),4) 0.994

数式の引数の区切りにセミコロンを使う設定の場合は、カンマをセミコロンに読み替えてください。

工程ごとの集め方と報告のしかた

工程 集める指標(No) 記録する場所 報告の頻度
要件定義・設計 1〜4 レビュー記録票 レビュー会ごと、工程完了時
実装 5〜7 プルリクエスト、静的解析の結果 週次
単体テスト 8、9 テスト実行の結果 ビルドごと、工程完了時
結合・システムテスト 10〜20 テスト管理表、バグ管理表 日次(集計)、週次(報告)
リリース後 21、22 問い合わせ・不具合の記録 月次、3か月後に確定

集めるときのコツは3つです。

  • 最初から22個すべてを追わない。 レビュー指摘密度、テスト密度、バグ密度、テスト消化率、未解決バグ数の5つから始めると、集計の手間と得られる情報のつり合いが取れます。
  • 数え方の決めごと(規模の単位、バグの定義、重要度の区分)を計画書に書き、途中で変えない。
  • 数値は人の評価ではなく、成果物の状態を知るために使う。指摘が多いレビューは「よく見つけた」と受け止めると、記録が正直に集まります。

よくある質問

Q. バグ密度の目安は何件/KLOCですか? A. 言語や数え方で変わるため、一律の値はありません。自分たちの過去の案件の中央値を出発点にし、上下に許容幅を取った範囲で判断します。過去データが無いうちは、同じ案件の機能同士を比べます。

Q. レビュー指摘密度が高いのは悪いことですか? A. 一概には言えません。レビュー工数密度も高ければ、丁寧に確かめてよく見つけた結果です。工数が少ないのに指摘が多いときは、成果物の作り直しや追加レビューを検討します。

Q. ゴンペルツ曲線とロジスティック曲線はどちらを使えばよいですか? A. 両方を当てはめ、RMSEが小さく、日数を変えても K が安定しているほうを採用します。差が小さいときは、2つの K を幅として報告すると判断しやすくなります。

Q. 行数で測りにくい開発(設定中心、ローコードなど)ではどうしますか? A. 分母を画面数、機能数、FP、ストーリーポイントなどに置き換えます。同じ分母を案件の中で使い続ければ、機能間や時点間の比較は成り立ちます。

まとめ

  • 品質指標は「確かめた量」と「見つかった結果」を組にして読む。バグが少ないだけでは良好と言えない。
  • 密度は件数 ÷ 規模。規模の単位と数え方を最初に決め、途中で変えない。
  • 基準範囲は自分たちの過去データの中央値と許容幅から作る。
  • 9ゾーン判定は、どの機能を先に確認するかを決める道具として使う。
  • 信頼度成長曲線はゴンペルツとロジスティックの両方を当てはめ、残存バグ数を幅で受け取る。
  • K が日によって動くうちは推定が定まっていない。RMSE と K の安定を見る。
  • 最初は5つの指標から始め、数え方を計画書に書いておく。

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「品質管理の指標一覧(システム開発)|バグ密度・レビュー指摘密度の計算式」

株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。

WE ARE HIRING

RJCで一緒に開発しながら、
成長を楽しみませんか?

RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!

  • 127日年間休日
  • 12時間平均残業時間
  • 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!

ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

RJCで一緒に開発しながら、 成長を楽しみませんか?