TL;DR
- Cycle Time の改善は、Elite ベンチマーク追従ではなく、自社 Value Stream の境界を定義した瞬間に半分終わる
- Lead Time / Cycle Time / Throughput の言葉を揃えないと、改善対象がチーム内でブレる
- 計測テンプレで Value Stream を 5 ステージに分け、中央値・90 パーセンタイル・前月比 Δ を記録する
- ボトルネックは「最も遅いステージ」ではなく「Δ が最も悪化したステージ」から 1 つだけ改善する
- 自チーム実測ではレビュー待ちが Cycle Time の 52% を占めていた。あなたのチームでも、計測してみないと分からない
「Cycle Time 改善」が空転するチームに共通すること
こんにちは、みねです。
「うちのチーム、Cycle Time を測って改善していこう」。CTO や EM からそう言われたとき、最初の 1 ヶ月で空転するチームがほとんどだ。
理由はシンプルで、Cycle Time の定義がチーム内で揃っていないから。誰かは「PR を Open した瞬間から Merge までの時間」と思っていて、誰かは「In Progress に動いてから Done 列に着くまで」と思っていて、誰かは「最初のコミットから本番デプロイまで」と思っている。これらはすべて違うものを測っている。
そのまま「DORA Elite は 1 日未満らしい、目指せ」とベンチマーク追従を始めると、計測している対象が人によって違うので、Slack 上の数字が一致せず、改善打ち手も合意できない。1 ヶ月後には「Cycle Time の話、なんとなく立ち消えになりましたね」になる。
:::message 社内 Cycle Time と DORA Lead Time for Changes は別指標として扱うこと。前者は「チームがチケット着手から完了まで要した時間」(チーム内フローの健康度を見る用途)、後者は「コミット〜本番反映の時間」(DORA Four Keys のベンチマーク用途)で、起点・終点・対象母集団が異なる。同一グラフ上で重ねて比較してはいけない。本記事は社内 Cycle Time の改善ループを主軸とし、DORA ベンチマーク表は自チーム位置の参考値としてのみ参照する。 :::
私自身、これを 2 回やった。3 回目でようやく学んだのが、**「Cycle Time は定義した瞬間に半分終わる」**ということだ。
本記事では、Cycle Time の定義のすり合わせから、Value Stream の境界設計、計測テンプレ、ボトルネック特定、改善ループまでを、EM・スクラムマスター・CTO 向けに 1 本の流れで整理する。読み終わったあと、自チームの Cycle Time の起点と終点を 1 つ決め、4 週間後に最初の Δ を見られる状態がゴールだ。
なお、開発生産性指標全体の選び方については開発生産性指標の歩き方:チームに合った定規の選び方で別途整理している。本記事は Four Keys のうち Cycle Time / Lead Time for Changes に絞って深掘りする位置づけだ。
Lead Time / Cycle Time / Throughput を揃える
Cycle Time の話をする前に、隣接する 3 指標との混同を解いておく必要がある。ここを揃えないと、改善対象がブレる。
3 指標の定義と視点の違い
| 指標 | 視点 | 起点 | 終点 | 主な用途 |
|---|---|---|---|---|
| Lead Time | 顧客視点 | 顧客が「やってほしい」と依頼した瞬間 | 顧客の手元に価値が届いた瞬間 | プロダクト全体の応答性 |
| Cycle Time | チーム視点 | チームがその課題に着手した瞬間 | チームがその課題を完了した瞬間 | チーム内のフローの健康度 |
| Throughput | 単位時間あたり | 1 日 / 1 週 / 1 スプリント | 同上 | キャパシティと変動の把握 |
ざっくり言えば、Lead Time は 「顧客が待っていた時間」、Cycle Time は 「チームが手を動かしていた区間」、Throughput は 「同じ時間でいくつ流せるか」 だ。
Cycle Time の改善は、Lead Time の一部を縮めることでもあるが、Lead Time の起点(顧客の依頼)はチームから見えにくい。だからまず Cycle Time から手を付ける、というのが実務的な順序になる。
自社で「Cycle Time の起点・終点」を決めるのが先
ここから本題で、Cycle Time の起点と終点には正解がない。チームのフローに合わせて選ぶ。
| 起点候補 | 取得しやすさ | チームの解釈 |
|---|---|---|
| チケットが In Progress 列に動いた瞬間 | 高(Jira / Linear で自動) | 「着手した」 |
| PR Open(Draft 含む) | 高(GitHub Webhook) | 「コードが書き始まった」 |
| 最初のコミット | 中(git log 解析) | 「実装が始まった」 |
| 終点候補 | 取得しやすさ | チームの解釈 |
|---|---|---|
| PR Merge | 高 | 「コードが main に入った」 |
| 本番 Deploy | 中(CI / CD ログ) | 「ユーザーに届いた」 |
| 顧客可達(Feature Flag On) | 低 | 「価値が出た」 |
おすすめは 「チケット In Progress」→「本番 Deploy」 だ。理由は 2 つ。
- PR ベースだと、PR を作る前の「ローカルで悩んでいた時間」が抜け落ちる。In Progress 起点なら、その悩みも含めて改善対象になる
- 終点を Merge にすると、Merge 後の Deploy 待ちで詰まっているケース(金曜マージ → 月曜デプロイなど)を見落とす
ただし、まだ計測基盤がない段階なら 「PR Open → PR Merge」 で始めて構わない。GitHub だけで完結し、最低限の Cycle Time が翌日には可視化できる。
Lean 原典 vs DORA の差異
Lean Software Development(Mary & Tom Poppendieck) の Cycle Time は「1 つの作業項目がプロセスを通過するのに要する時間」という抽象的な定義だ。一方、DORA の Lead Time for Changes は「コミットが本番で動くまでの時間」と工程を限定している。
学術的には別物だが、実務上は DORA 寄りで揃えれば十分だ。Lean 原典まで遡ると合意形成のコストが跳ね上がる割に、改善打ち手は変わらない。チーム内で「うちの Cycle Time は『In Progress 着手 → 本番 Deploy』とする」と Confluence や Notion に 1 行書いて、次の議題に進もう。
Value Stream を 5 ステージに分けて境界を引く
Cycle Time の起点と終点を決めたら、次にその区間をステージに分割する。理由は、合計の Cycle Time だけ見ても改善ポイントが分からないからだ。
Value Stream Mapping の最小版
Value Stream Mapping は元々はトヨタ生産方式由来の手法だが、ソフトウェア開発に持ち込むときは複雑にしすぎないのがコツだ。紙とペンで 30 分でできる最小版を作る。
手順:
- ホワイトボードに、起点(例: In Progress)から終点(例: Deploy)までを左から右に矢印で引く
- その矢印の上に、典型的な PR 1 本がたどる箱(ステージ)を並べる
- 各箱の中で、人が手を動かしている時間(処理時間)と、待っている時間(待ち時間)を分ける
この時点で、多くのチームが気づくのが「ほとんど待ち時間」という事実だ。実際、私のチームでも処理時間 0.7 日に対して待ち時間が 1.6 日で、待ちが 70% を占めていた。
5 ステージの推奨分割
ステージ分割は、チームによって 3 個でも 7 個でも構わないが、計測の運用性を考えると 5 個がちょうどいい。粒度が細かすぎると記録が苦行になり、粗すぎるとボトルネックが見えない。
| # | ステージ名 | 起点(境界) | 終点(境界) |
|---|---|---|---|
| 1 | Discovery | チケットが In Progress に移動 | 仕様確定 / 実装方針合意 |
| 2 | Implementation | 実装方針合意 / first commit | PR が Ready for Review になる |
| 3 | Review | Ready for Review | 全 Approve 取得 |
| 4 | Verification | 全 Approve 取得 / Merge | CI 完走 / QA Pass |
| 5 | Release | CI Pass | 本番 Deploy 完了 |
各ステージの境界をタイムスタンプで取れる位置に引く
5 ステージに分けるとき、境界は必ずシステムが自動でタイムスタンプを取れる位置に引く。これが運用の生命線だ。
具体的には:
- Discovery → Implementation: 「実装方針合意」の境界はチームのファセットによる。Linear や Jira の「Ready for Dev」列に動かすルールがあれば、その遷移時刻を使う
- Implementation → Review: PR が Draft から Ready for Review になった瞬間(GitHub Webhook で取れる)
- Review → Verification: 最後の Approve / または Merge 押下時刻
- Verification → Release: CI Pass 時刻 / Deploy 開始時刻
GitHub PR API(pull_request events)と Linear / Jira の Webhook を組み合わせれば、ほぼすべて自動取得できる。「人間が記入する」運用にすると、3 週間で破綻する。
人が律速になる構造の全体像についてはAI駆動開発で人を律速にしない 5つのボトルネック類型と解消パターンで別途整理している。Cycle Time のステージ分割は、そこで挙げている TOC(制約理論)の最小実装と捉えるとよい。
計測テンプレ:中央値と 90 パーセンタイルで運用する
ステージが決まったら、いよいよ計測する。ここで多くのチームがハマるのが「平均値」を見てしまうことだ。
なぜ平均ではなく中央値+90 パーセンタイル
Cycle Time の分布は、ほぼ確実に右に裾を引く。たまに 30 日かかるチケットが 1 本混じるだけで、平均は跳ね上がる。一方、中央値はその影響を受けない。
しかし中央値だけでも足りない。平均が良くても 90 パーセンタイル(p90)が大きいなら、「10% の PR は地獄を見ている」状態だからだ。チームの体感は中央値ではなく p90 で決まる。
なぜ p90 を見るべきか、もう一段技術的に言えば、経験則として p90 が悪化したチームでは WIP が膨らみ、しばらくして中央値も後追いで悪化することが多い。ただし Little の法則(L = λW)は本来「平均値」に関する関係であり、p90 を直接導く式ではない点に注意してほしい。本記事では p90 を 中央値の補助・先行指標 として扱う、という運用上の合意であり、定理から演繹される必然ではない。
計測テンプレ(記事の中核オファー)
以下が、明日からそのまま使える計測テンプレだ。Spreadsheet にコピーして、月次で更新する。
| ステージ | 中央値(日) | p90(日) | 前月中央値 | 前月 p90 | 中央値 Δ | p90 Δ |
|---|---|---|---|---|---|---|
| 1. Discovery | ||||||
| 2. Implementation | ||||||
| 3. Review | ||||||
| 4. Verification | ||||||
| 5. Release | ||||||
| 合計(Cycle Time) |
運用上のポイント:
- 記録は中央値を整数または小数 1 桁の「日」単位で揃える。時間単位だと比較が直感的でなくなる
- Δ は前月比のパーセンテージで書く(例: -12%, +5%)
- 計測の最低期間は 4 週間 + 直近 2 スプリント分。これより短いと PR 数が少なすぎて中央値の信頼性が落ちる
- 1 ヶ月の PR 数が 10 本未満のチームは、中央値ではなく全件のドットプロットで見る方が精度が高い
この表を手で埋めるのは月次運用ではすぐ破綻する。CSV さえ出力できれば、上表の「中央値・p90・前月比 Δ」は標準ライブラリだけで機械的に算出できる。以下は、ステージごとの所要日数を並べた CSV(stage,days,month の 3 列、month は 2026-04 のような対象月ラベル)を渡すと、当月と前月を突き合わせて上表と同じ並びで出力するスクリプトだ。
import csv
import statistics
import sys
from collections import defaultdict
# 記事のテンプレ表に合わせたステージ順(合計はこの 5 ステージの和で別途算出)
STAGES = [
"1. Discovery",
"2. Implementation",
"3. Review",
"4. Verification",
"5. Release",
]
def load(csv_path):
"""stage,days,month の 3 列 CSV を {month: {stage: [days, ...]}} に集計する。"""
table = defaultdict(lambda: defaultdict(list))
with open(csv_path, newline="", encoding="utf-8") as f:
for row in csv.DictReader(f):
table[row["month"]][row["stage"]].append(float(row["days"]))
return table
def p90(values):
"""90 パーセンタイル。statistics.quantiles は n>=2 が必須なので、
本数が 1 本以下のステージ(記事の「PR 10 本未満はドットプロット」と同じ事情)は
例外にせず単一値/None を返してガードする。"""
if not values:
return None
if len(values) < 2:
return values[0]
# method="inclusive" は記事の「全件を並べた分位点」という説明と整合する補間方式
return statistics.quantiles(values, n=10, method="inclusive")[8]
def delta_pct(current, previous):
"""前月比のパーセンテージ(記事の運用ルール: 例 -12%, +5%)。"""
if current is None or previous in (None, 0):
return "n/a"
return f"{(current - previous) / previous * 100:+.0f}%"
def summarize(per_stage):
"""1 ヶ月分のステージ別データから中央値・p90 を日・小数 1 桁で返す。"""
out = {}
totals = []
for stage in STAGES:
days = per_stage.get(stage, [])
med = round(statistics.median(days), 1) if days else None
p = p90(days)
out[stage] = (med, round(p, 1) if p is not None else None)
if days:
totals.append(statistics.median(days))
out["合計(Cycle Time)"] = (
round(sum(totals), 1) if totals else None,
None,
)
return out
def main(csv_path, this_month, last_month):
table = load(csv_path)
cur = summarize(table.get(this_month, {}))
prev = summarize(table.get(last_month, {}))
print(f"{'ステージ':<22}中央値 p90 中央値Δ")
for stage in STAGES + ["合計(Cycle Time)"]:
med, p = cur[stage]
pmed, _ = prev[stage]
med_s = "-" if med is None else f"{med:.1f}"
p_s = "-" if p is None else f"{p:.1f}"
print(f"{stage:<22}{med_s:>5} {p_s:>5} {delta_pct(med, pmed):>6}")
if __name__ == "__main__":
# 例: python3 cycle_time.py cycle_time.csv 2026-04 2026-03
main(sys.argv[1], sys.argv[2], sys.argv[3])
出力はそのまま上のテンプレ表に転記できる並びにしてある。これで「Spreadsheet を手で更新する」運用が、月次の CSV エクスポート 1 回で回るようになる。
DORA ベンチマーク表の使い方
DORA の Lead Time for Changes ベンチマーク は以下の通り(2024 Accelerate State of DevOps Report より)。
| パフォーマンス層 | Lead Time for Changes |
|---|---|
| Elite | 1 日未満 |
| High | 1 日〜1 週間 |
| Medium | 1 週間〜1 ヶ月 |
| Low | 1 ヶ月以上 |
:::message 注意:上表は DORA 公式の「Lead Time for Changes(コミット〜本番反映)」のベンチマークである。前述の通り、本記事で扱う社内 Cycle Time とは起点・終点が異なるため、自チーム計測値を直接この表に当て込んではいけない。位置確認の参考として参照する。 :::
ここで重要なのは、この表は「目標」ではなく「自チームの位置確認」用ということだ。Elite を目標にすると、Trunk Based Development や Feature Flag を強引に導入して破綻するチームを何度も見てきた。
ベンチマーク表との比較ではなく、自チームの前月比 Δ で改善するのが王道だ。Elite に追いつくのは、Δ を積み上げた結果としてあとからついてくる。
補足:本記事独自再分類(DORA 公式値とは別)
社内 Cycle Time(In Progress 着手 → 本番 Deploy)に対しては、上表をそのまま当てはめられない。チーム内 Δ の運用基準として、本記事独自再分類として以下を提示する。これは DORA 公式値とは別軸であり、自チームの感覚値合わせに使う暫定指標である。
| 自チーム水準 | 社内 Cycle Time(中央値) |
|---|---|
| 良好 | 2 日未満 |
| 標準 | 2〜5 日 |
| 要改善 | 5〜10 日 |
| 危険 | 10 日以上 |
繰り返しになるが、これは DORA 公式の Lead Time for Changes ベンチマークとは別表である。混同しないこと。
ボトルネック特定:制約理論と Δ ベース判定
計測テンプレが 4 週間ぶん埋まったら、ボトルネックを特定する。
制約理論(TOC)の 5 集中ステップ
Eliyahu Goldratt の制約理論(Theory of Constraints, TOC)は、システム全体のスループットは最も遅い工程(制約)に支配されるという考え方だ。Cycle Time の改善にそのまま使える。
5 集中ステップ:
- 特定: ボトルネックがどこか特定する
- 活用: ボトルネックを 100% 活用する(無駄な仕事を流さない)
- 従属: 他の工程をボトルネックに合わせる
- 強化: ボトルネックの能力を上げる(採用 / ツール / 自動化)
- 再特定: 制約が動くので、また 1 に戻る
注意点として、ステップ 4(強化)に飛びつきがちだが、ステップ 2-3 を飛ばすと改善効果が消える。たとえばレビュアーを増やす(強化)前に、レビュー以外の仕事をボトルネックに流していないか(活用)を見直す方が、効果が大きいケースが多い。
Δ ベース判定の手順
ステージごとの中央値・p90 を見ると、つい「最も遅いステージから直そう」となる。これは半分正しいが、もう半分は 「最も悪化したステージを見る」 べきだ。
なぜなら、最も遅いステージが構造的に動かないもの(例: コンプライアンス審査でどうしても 3 日かかる)かもしれないからだ。一方、前月から悪化したステージは、直近で何かが壊れた可能性が高く、原因が新鮮で特定しやすい。
判定手順:
- 各ステージの中央値 Δ と p90 Δ を見る
- p90 Δ が +20% 以上悪化したステージを最優先候補にする
- それが該当しなければ、中央値 Δ が +10% 以上悪化したステージ
- それも該当しなければ、絶対値で最も大きいステージ
自チーム実例:レビュー待ちが Cycle Time の 52%
私のチームの実測データを共有する(2025 年 Q4、PR 約 80 本)。
| ステージ | 中央値(日) | 占有率 |
|---|---|---|
| 1. Discovery | 0.3 | 13% |
| 2. Implementation | 0.5 | 22% |
| 3. Review | 1.2 | 52% |
| 4. Verification | 0.2 | 9% |
| 5. Release | 0.1 | 4% |
| 合計 | 2.3 | 100% |
レビュー待ちが Cycle Time の 52% を占めていた。Implementation を高速化するためにペアプロや AI 補助を増やしても、Cycle Time は 22% のうちの 1〜2 割しか縮まない計算になる。
レビュー待ちの根因はAI生成PRでレビューが詰まる本当の理由 工数ではなく意図復元コストを下げるで深掘りしたとおり、AI 生成 PR が増えてレビュアーの「意図復元コスト」が跳ね上がったことだった。打ち手として、AI チェック層と人間レビューの責務を分離するAI時代のレビュー運用設計を導入した結果、Review ステージの中央値が 1.2 日から 0.7 日に下がった。
ここで言いたいのは、ボトルネックの所在は計測してみないと分からないということだ。私のチームではレビューだったが、別のチームでは CI 待ちかもしれないし、Discovery(仕様確定)かもしれない。
改善ループ:定義 → 計測 → 可視化 → 集中 → 再計測
ここまでで「定義」「計測」「ボトルネック特定」が揃った。次はそれをループにする。
5 ステップの回し方(4 週間 1 サイクル)
| # | ステップ | 期間目安 | アウトプット |
|---|---|---|---|
| 1 | 定義 | 1 日 | Cycle Time の起点・終点・5 ステージ境界の合意 |
| 2 | 計測 | 4 週間 | テンプレに中央値・p90 を記録 |
| 3 | 可視化 | 1 日 | チーム MTG で 1 ページのダッシュボード共有 |
| 4 | 集中改善 | 3〜4 週間 | Δ が最悪のステージに 1 つだけ打ち手を入れる |
| 5 | 再計測 | 4 週間 | 改善前後の Δ を比較し、効いていなければ別の打ち手に切り替える |
ポイントは 「集中改善」で打ち手を 1 つに絞る こと。3 つ同時に変えると、どれが効いたか分からず、次のサイクルの判断ができなくなる。これは制約理論の「集中の原則」と一致する。
AI 導入チームの落とし穴:速くなったと感じても Cycle Time は伸びる
AI コーディングを導入したチームには、特有の罠がある。METR が 2025 年に発表した研究では、AI を使った開発者は実際には 19% 遅くなっていたにもかかわらず、本人たちは 「20% 速くなった」と感じていた。知覚と実測のギャップが 39 ポイントある。
このギャップが、Cycle Time の計測を曖昧にしておくと、致命的に効いてくる。なぜなら、「AI 入れたら速くなったよね」という体感だけが Slack に流れ、実測は誰も見ていない、という状態に陥るからだ。
この構造の詳細はAI生産性のパラドックス なぜ速くなったのにチケットが消えないのかで整理している。Cycle Time をきちんと定義して計測しているチームは、このパラドックスから抜け出せる。
改善打ち手の例(ステージ別)
ボトルネックがどのステージにあるかで、打ち手は変わる。代表的なものを挙げる。
| ボトルネックのステージ | 代表的な打ち手 | 関連記事 |
|---|---|---|
| Discovery | 仕様の事前型化、AC(受け入れ基準)テンプレ化 | (別途) |
| Implementation | ペアプロ運用見直し、コンテキスト設計 | AIペアプロが失敗する理由 |
| Review | AI チェック層導入、レビュー責務分離 | AI時代のレビュー運用設計 |
| Verification | CI 並列化、テスト分割、キャッシュ | CIが遅い原因を分解する |
| Release | 段階デプロイ、Feature Flag | (別途) |
「ペアプロを増やせば Cycle Time が縮む」と短絡しがちだが、Implementation がボトルネックでないチームでは、ペアプロは Cycle Time を伸ばすことすらある。診断あっての処方箋だ。
FAQ
Q1: DORA の Lead Time for Changes と社内 Cycle Time の違いは?
DORA の Lead Time for Changes は「commit から production リリースまで」の客観的な計測指標で、組織横断・他社比較を目的に設計されている。一方、本記事で扱う Cycle Time は「In Progress 着手 → Deploy」を社内 Value Stream に合わせて再定義したもので、改善ループを回す内向きの計測軸だ。両者は別指標であり、同一のグラフで直接比較すべきではない。DORA を北極星、Cycle Time を改善ハンドルとして役割分離する運用を推奨する。
Q2: 計測には最低何件のデータが必要?
経験則として 4 週間以上、かつ最低 20 件が再現可能性の最低ラインだ。中央値の安定性を担保するためで、PR 数が少ないチームでは観測期間を 6-8 週間に伸ばす運用に切り替える。月次サンプルが 10 件未満のチームは、判定閾値(Δ ±20%)を緩めるか、計測単位を四半期に変更してノイズを吸収する。
Q3: スプリント長が違うチーム同士で比較できる?
スプリント長への依存を避けるため、本記事は 絶対時間(時間/日単位)+ 件数ベースで計測することを推奨する。「2 スプリント分」という相対表現は、スプリント長が 1 週・2 週・3 週で揃わないチーム間で比較が崩れるため、固定の「20 件以上 × 4 週間以上」のような絶対条件で揃えるほうが安全だ。
Q4: 中央値と p90 のどちらを優先すべき?
通常は中央値を主指標、p90 を補助指標として併用する。p90 はテール側の遅延を可視化する補助・先行指標で、Little の法則(L = λW)が示すのは平均値の関係であり、p90 は定理から演繹される必然ではない。p90 が悪化した時に「WIP 過多の兆候かもしれない」と仮説を立てる用途で十分だ。
Q5: 改善打ち手の判断閾値はどう決める?
本記事で示した ±20% / ±10% は暫定閾値(要チーム調整) だ。母数が小さいチームでは月次ノイズで容易に超えるため、最低サンプル数(月 20 件以上)を満たした上で、過去 3 サイクル(12 週間)の標準偏差を見て、自チームのノイズ幅より 1.5-2 倍大きい変動を「打ち手検討トリガー」に設定するのが現実的だ。
まとめ:明日から始める 3 ステップ
長くなったので、明日からの行動を 3 つに絞る。
- 定義する: 自チームの Cycle Time の起点・終点を 1 行で書く(推奨は「In Progress 着手 → 本番 Deploy」、難しければ「PR Open → PR Merge」)
- 計測する: Value Stream を 5 ステージに分け、4 週間ぶんの中央値と p90 を本記事のテンプレに記入する
- 集中する: Δ が最悪のステージに 1 つだけ打ち手を入れ、4 週間後に再計測する
このループを 3 サイクル(約 12 週間)回すと、ほぼ間違いなくチームの体感とデータが揃ってくる。「Cycle Time の話、なんとなく立ち消えになりましたね」を、もう繰り返さなくていい。
最後にひとつだけ。本記事の計測テンプレを使って自チームを診断したが、ステージ分割や境界の引き方に迷ったら、X(旧 Twitter)のみね に DM をいただきたい。記事診断という形で、チーム固有の Value Stream に翻訳するお手伝いをしている。
Cycle Time だけでなく経営層への業界比報告まで広げる場合は、DORA / SPACE / DevEx の使い分けを 開発生産性指標 比較:DORAとSPACEの選び方 で整理している。
Cycle Time は、定義した瞬間に半分終わる。残り半分は、Δ を積み上げるだけだ。
