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を縦割りで分解すると:

分解後のissueTypeサイズ
テーブルに属性カラムを追加するTask0.5日
詳細画面に新属性を表示するFeature1日
属性の追加・削除UIを実装するFeature1〜2日
属性単位で担当者を割り振れるようにするFeature1〜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. アウトカムが1文で書ける — 「〜が〜できるようになる」
  2. 完了条件が3項目以内 — 受け入れ基準が明確
  3. 1〜3日で終わる — 超えるなら分割
  4. 依存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つ:

  1. INVEST原則 — 各issueの品質を6項目で検証する
  2. 縦割りスライス — レイヤーではなくユーザー価値の単位で分割する
  3. Definition of Ready — 着手前に4項目でスクリーニングする

そしてissueの(Feature / Task / Bug / Spike)を区別し、特にSpikeと実装issueを混ぜないことが重要だ。

粒度の一貫性は一度決めたら終わりではなく、定期的なふりかえりで「3日を超えたissueはなかったか」「carry-overしたissueはなかったか」を確認し続ける必要がある。ツールの設定ではなくチームの共通認識として定着させることが、結局は一番効く。

以上。