バーンダウンの理想線は「初回スコープ固定」ではなく日次スコープのスナップショットで引く
イテレーションのバーンダウンチャートを実装するとき、最初に「コミットされたスコープポイント」を1つ凍結して理想線を引くのが素直な発想だが、途中でスコープが増減すると理想線と実線がズレて意味を失う。今回は、スコープ自体も日次スナップショットに含めて、各日の理想線をその日のスコープを基準に引くように変えた。
最初の実装はこんな構造だった。
- イテレーションテーブルに
burndownScopePoints(初回コミットスコープ)を持たせる - 1日1回スナップショットを取り、
remaining_pointsだけを記録する - 理想線は
burndownScopePointsを始点として直線で引く
これだと、途中で「やっぱりこのストーリーも入れる/落とす」が起きたときに、理想線は古いスコープのまま固定されて、現実とまったく合わなくなる。新しい構造はこう。
- スナップショットテーブルに
scope_pointsカラムを追加(migration 0041) - 毎日のスナップショット時に
remaining_pointsとscope_pointsを両方記録する - 当日のスコープが残っていなければ live 集計(
Rejectedを除いた合計)でフォールバック - イテレーション側の
burndownScopePointsは捨てる
// Before: 初回スコープを凍結
await freezeScopeIfUnset(db, iterationId, committedScope);
await db.insert(snapshots).values({
iterationId,
snapshotDate: today,
remainingPoints: remaining,
});
// After: 日次でスコープも記録
await db.insert(snapshots).values({
iterationId,
snapshotDate: today,
scopePoints: scope, // ← 当日のスコープも保存
remainingPoints: remaining,
});
ポイントは、「変動しうる前提値」をスナップショットの外に置かないこと。理想線・実線・スコープ線の3本を同じ時系列ベースで描けるようになるので、後からバーンアップにも自然に拡張できる。
今回やったこと(実施済み)
- マイグレーションでスナップショットに
scope_pointsを追加 - 日次バッチ・API両方で
scope_pointsを保存 - 理想線は「各日のスコープ」を anchor に再計算
- バーンアップタブを追加するときに、同じ
scope_points列をそのまま使えた