Linearのissue粒度を揃える:INVEST原則と縦割りスライスの実践
TL;DR
issueの粒度がバラバラでBacklogが混沌とする原因は、1つのissueに「設計判断」「DB変更」「UI実装」「テスト」が混在していることにある。INVEST原則で各issueの品質を検証し、縦割りスライスで分割し、Definition of Readyで着手前にスクリーニングすると、1 issue = 1〜3日 = 1 PRのリズムが生まれる。
粒度がバラつく根本原因
前回の記事で「課題とタスクを分離する3層構造」を紹介した。Project + Document + Issueの3層に分ければ、「ADHDの克服」のような課題がissueに混入する問題は解消する。
しかし、タスクとして正しく切り出したissueの中でも粒度がバラつく問題が残る。私が開発中の業務管理ツールのBacklogを見ると、こんな状態だった:
PROJ-01データ分割:profile追加による対応と検証PROJ-02担当者の複数割り当て対応とDB検証PROJ-03データ構造の分け方でいいのか — 設計判断
一見するとどれも「タスク」に見える。だが冷静に分析すると、それぞれの中に複数の関心事が混在している:
- 設計判断(どのDB構造にするか)
- マイグレーション実装(ALTER TABLE)
- seedデータの更新
- UIの実装
- E2Eテスト
これは「課題 vs タスク」の分離とは別次元の問題で、タスク同士の粒度とスコープが揃っていないという状態だ。
INVEST原則:issueの品質チェックリスト
issueの粒度を揃えるための最も実用的なフレームワークがINVEST原則だ。元々はユーザーストーリーの品質基準として提案されたものだが、Linearのissueにもそのまま適用できる。
| 原則 | 意味 | チェック |
|---|---|---|
| Independent | 独立している | 他のissueに依存せず単独で着手できるか |
| Negotiable | 交渉可能 | 実装方法ではなくアウトカムで記述されているか |
| Valuable | 価値がある | 完了時にユーザー or 開発者にとって明確な価値があるか |
| Estimable | 見積可能 | スコープが明確で作業量が想像できるか |
| Small | 小さい | 1〜3日で完了できるか |
| Testable | テスト可能 | 完了/未完了がpass/failで判定できるか |
先ほどのPROJ-02をINVESTで検証してみる:
- I: DB変更とUI実装が混在 → 独立していない ❌
- N: 「DB検証」という実装手段で記述 → アウトカム指向でない ❌
- V: 価値はある ✅
- E: スコープが広すぎて見積もれない ❌
- S: DB + UI + テストで1週間以上 ❌
- T: 「検証」の完了条件が曖昧 ❌
6項目中5つが不合格。これでは着手しても終わらないし、終わったかどうかもわからない。
縦割りスライス:分割の方向を間違えない
issueが大きいとわかったら分割するわけだが、ここで分割の方向が重要になる。
横割り(Horizontal Slice)— やりがちだが危険
graph LR
A["DB変更"] --> B["バックエンド API"] --> C["フロントエンド UI"] --> D["テスト"]
レイヤーごとに切る方法。「まずDBだけ」「次にAPIだけ」「最後にUI」と進める。一見合理的だが、問題がある:
- DB変更だけ完了してもユーザーに提供できる価値がゼロ
- UIを作る段階でDBの設計ミスが発覚しても手戻りが大きい
- 各issueが前のissueに依存するので、並列作業ができない
縦割り(Vertical Slice)— 推奨
graph LR
subgraph S1["Slice 1: DBを拡張"]
A1["DB: カラム追加"] --> B1["Seed: 互換確認"]
end
subgraph S2["Slice 2: 新属性の表示"]
A2["UI: 一覧に表示"] --> B2["E2E: 表示確認"]
end
subgraph S3["Slice 3: 複数担当者"]
A3["UI: 追加ボタン"] --> B3["API: 作成"] --> C3["E2E: 複数担当"]
end
S1 ~~~ S2 ~~~ S3
ユーザー価値の単位で縦に切る方法。各スライスが単独でデプロイ可能で、完了時に何かしらの価値を提供する。
実際にPROJ-02を縦割りで分解すると:
| 分解後のissue | Type | サイズ |
|---|---|---|
| テーブルに属性カラムを追加する | Task | 0.5日 |
| 詳細画面に新属性を表示する | Feature | 1日 |
| 属性の追加・削除UIを実装する | Feature | 1〜2日 |
| 属性単位で担当者を割り振れるようにする | Feature | 1〜2日 |
最初のTaskは既に完了済みで、実際に0.5日で終わった。残りの3つはそれぞれ独立して着手でき、各issueが完了するたびにユーザーに価値を提供できる。
Issue型の使い分け
粒度を揃えるもう一つの道具が、issueの**型(type)**を明確に区別することだ。
| Type | 定義 | サイズ目安 | タイトルの書き方 |
|---|---|---|---|
| Feature | ユーザー価値を提供する機能追加 | 1〜3日 | 「〜できるようにする」 |
| Task | 明確なステップの実行 | 0.5〜1日 | 「〜を追加する」「〜を設定する」 |
| Bug | 既存機能の不具合修正 | 0.5〜2日 | 「〜が〜になる問題を修正する」 |
| Spike | 不確実性の解消(time-boxed) | 0.5〜1日 | 「〜の方式を調査・決定する」 |
重要なのはSpikeとFeature/Taskを混ぜないことだ。「複数担当のDB設計を決める」(Spike)と「テーブルにカラムを追加する」(Task)は別のissueにすべきで、Spikeの結果を受けてTaskが生まれるという依存関係になる。
先ほどの例で言えば:
graph LR
S["🔍 Spike: 複数担当の<br/>DB設計を決定する<br/>(PROJ-03)"] --> T["🔧 Task: テーブルに<br/>カラム追加<br/>(PROJ-02の一部)"]
T --> F1["✨ Feature: 新属性を<br/>表示する"]
T --> F2["✨ Feature: 属性の<br/>追加・削除UI"]
F1 --> F3["✨ Feature: 属性単位で<br/>担当者を割り振る"]
F2 --> F3
SpikeのPROJ-03が完了して設計判断が固まったからこそ、後続のTask/Featureのスコープが明確になった。この順序を守らないと「設計も実装も全部入りの巨大issue」が生まれる。
Definition of Ready:着手前のスクリーニング
issueを分解した後、スプリントに入れる前の最終チェックとしてDefinition of Ready(DoR)が有効だ。全項目を毎回チェックするのは重いので、最低限の4項目に絞る:
- アウトカムが1文で書ける — 「〜が〜できるようになる」
- 完了条件が3項目以内 — 受け入れ基準が明確
- 1〜3日で終わる — 超えるなら分割
- 依存issueが明示されている — ブロッカーが未解決なら着手しない
この4項目に1つでも引っかかるissueは、refinementを続けるか、Spikeとして調査issueを切り出す。
「1 issue = 1 PR」原則
粒度が揃ったissueは、自然と「1 issue = 1 PR」の関係になる。これはコードレビューの効率にも直結する。Googleのエンジニアリングプラクティスでも「小さなCL(Change List)」が推奨されているが、その前提として「issueが適切な粒度であること」が必要だ。
逆に言えば、1つのPRが複数のissueを閉じている場合、それはissueの粒度が細かすぎるか、PRに無関係な変更が混入しているサインだ。
まとめ
issueの粒度を揃える道具は3つ:
- INVEST原則 — 各issueの品質を6項目で検証する
- 縦割りスライス — レイヤーではなくユーザー価値の単位で分割する
- Definition of Ready — 着手前に4項目でスクリーニングする
そしてissueの型(Feature / Task / Bug / Spike)を区別し、特にSpikeと実装issueを混ぜないことが重要だ。
粒度の一貫性は一度決めたら終わりではなく、定期的なふりかえりで「3日を超えたissueはなかったか」「carry-overしたissueはなかったか」を確認し続ける必要がある。ツールの設定ではなくチームの共通認識として定着させることが、結局は一番効く。
以上。