dev-agent-teamシリーズの12本目。 前回(公式プロンプトガイドが、自作の型を裏打ちしていた)までは 本体の新機能や公式ガイドを取り込む話が続いた。 今回は逆方向——実プロジェクトの通し検証で痛い目を見た知見を、一般化して型に還流させた話。
- 26個のコンポーネントを1個ずつ移行したら、不具合はどの単位にも属さない領域に集中した
- 対策は無主物リスト——分割の設計と同時に「誰のものでもない関心事」を列挙して担当を割る
- データ共有の判断軸は表示ではなく「エンドポイント+パラメータ」。そしてコードの共有と実行の共有は別物
実プロジェクトで、26個のUIコンポーネントを旧フレームワークから新フレームワークへ移行した。 進め方は定石どおり——1個ずつ独立したタスクに分割し、並行で移行して、 個別に検証しながら進めた。個別ケースの検証はほぼすべてPASS。順調に見えた。
移行完了後、リリース前にClaude Code+Playwright MCPで通しのE2E検証を回した (検証もAIにやらせている)。結果、18件の不具合が出た。 その内訳に、はっきりした偏りがあった。
コンポーネント固有の不具合
- 少数
- 個別ケースの検証はほぼ全てPASS
- 単位分割された部分の品質は高かった
横断的な関心事の不具合
- 大半
- 全26コンポーネントに影響するもの
- どのコンポーネントにも属さないもの
単位分割された部分の品質は高かった。落ちたのは、分割の境界に落ちた領域—— これが今回の出発点だ。この改善(PR #27)は、 この知見を固有名詞を外して一般化し、キットのワークフローに組み込んだ記録になる。
1. 何が落ちたか——3つの実例
抽象論に入る前に、実際に落ちたものを3つ挙げる。どれも「あるある」のはずだ。
実例1: 共有データの取得がN重複した
移行前は、1つのAPIが複数コンポーネントに必要なデータをまとめて返す設計だった。 移行後、7つのコンポーネントが同一パラメータで同じAPIを個別に叩いていた。 移行前は1回だったリクエストが7回。総リクエスト数+6、初期ロード+5.3秒。
実例2: 移行元にあった仕組みが丸ごと落ちた
移行元には、リクエストURLの長さ超過を避けるため状態をサーバ側に逃がす仕組みがあった。 これはどのコンポーネントにも属さない。だから誰の担当にもならず、 移行されないまま本番相当環境でHTTP 414(URI Too Long)が発生した。
実例3: 全コンポーネント共通のUI部品の差分が最後まで残った
共通ヘッダのメニュー順・文言、画像出力の挙動など。 1個ずつ検証していると、「共通部分は誰かが見ているだろう」で全員が飛ばす。 結果、いちばん目立つ場所の差分が最後まで残った。
2. 一般化——単位分割は関心事から担当者を奪う
3つの実例は、1つの構造に還元できる。
作業を単位分割すると、その単位に属さない関心事に担当者がいなくなる。
┌─ 単位1 ─┐ ┌─ 単位2 ─┐ ┌─ 単位3 ─┐ ・・・ ┌─ 単位26 ─┐
│ 担当あり │ │ 担当あり │ │ 担当あり │ │ 担当あり │
│ 品質 ◎ │ │ 品質 ◎ │ │ 品質 ◎ │ │ 品質 ◎ │
└─────┘ └─────┘ └─────┘ └──────┘
═══════════════════════════════════════════
データ取得層 / 共通UI部品 / 出力・エクスポート / 状態の永続化 …
↑ どの単位にも属さない = 担当者がいない = 不具合はここに集中 重要なのは、分割自体は正しいということだ。 並行作業ができ、レビュー単位が小さくなり、リリース可能な状態を保てる。 分割をやめるのは解ではない。 問題は、分割の設計時に「どの単位にも属さないもの」を列挙していなかったこと。 つまり足りなかったのは分割の巧拙ではなく、分割の余りを数える工程だった。
3. 対策の中核——無主物リスト
そこでキットの移行サブフロー(workflows/feature-development.md §6.1)に、
「単位分割の設計——無主物リスト」という節を新設した。
複数単位に分割して進める移行・大規模改修では、
最初の1単位に着手する前に次を実施する。
- 無主物リストを作る — 「どの単位にも属さない関心事」を列挙し、それぞれに担当(どの単位が持つか/独立タスクにするか)を割り当てる。列挙されなかった領域は「誰も見ていない」とみなす。
- 移行元の仕組みを載せる — 「理由があってそう書かれている仕組み」(URL長の制約回避、キャッシュ層など)は、単位ごとの移行では誰にも移行されない。着手前に棚卸しして担当を決める。理由が不明な実装を「不要」と判断して落とさない。
- データ取得層の方針を決める — 複数単位がそれぞれデータを取る構成では、共有データの重複取得は発生することが確定している。何もしない/重複排除層を作る/クエリキャッシュを使う、を着手前に決める。
典型的な無主物は、チェックリストとして8項目に定型化した。
典型的な無主物のチェックリスト
──────────────────────────────────────
□ データ取得層(キャッシュ / 重複排除 / 同時実行)
□ 全単位に共通する UI 部品(ヘッダ・メニュー・空状態・ローディング)
□ 出力・エクスポート(印刷 / PDF / 画像 / CSV — 画面表示とは別のコードパス)
□ 状態の永続化(localStorage / Cookie / サーバ側セッション)
□ グローバルな条件変更に対する全単位の追従
□ リクエスト URL 長・ペイロードサイズの制約回避
□ エラー処理・リトライ・認証切れの扱い
□ 計測・ログ・アナリティクス 「決めない」も選べるようにした。データ取得層の方針は判断が難しいこともある。その場合は決めなくてよいが、判断すべき箇所として無主物リストに明示的に記録し、再判断ポイント=「2つ目の単位が同じAPI(同一エンドポイント+パラメータ)を叩いたとき」を明記する。先送り自体は許すが、無自覚な先送りは許さない——という作りだ。
4. 共有の判断軸は「表示」ではなく「エンドポイント+パラメータ」
実プロジェクトからは、派生した気づきも2つ拾えた。1つ目は共有判定の判断軸だ。
「同じデータを見ているか」は、表示や見た目からは判断できない。 実際、同じAPIを使う7コンポーネントの表示は全部バラバラだった。 逆に、見た目が似ている3コンポーネントは同じAPIだがパラメータが違い、 分けて呼ぶのが正しかった。
表示・見た目で判断すると
- 表示がバラバラな7個の「同一取得」を見逃す
- 見た目が似た3個を誤って共通化する
- 両方向に間違える
エンドポイント+パラメータで判断すると
- 同一エンドポイント+同一パラメータ=共有候補
- パラメータが違えば別呼び出しが正しい
- 機械的に判定でき、マトリクスに落とせる
そこで skills/impact-analysis.md に「データ取得の共有関係」という観点を新設し、
エンドポイント→利用箇所のマトリクスをPhase 3(影響分析)の成果物に含めることにした。
作成時の注意も、実際に取りこぼした手順から書き起こしている——
定数名とURL文字列の両方でgrepする(ファイル内で定数が再定義されていると片方しか出ない)、
共有フック・共通コンポーネント経由の間接参照も追う(直接呼び出しだけ数えると漏れる)。
5. コードの共有と実行の共有は別物
2つ目の気づきは、もっと痛い。 このプロジェクトは共有データに気づいていた。共有フックまで作っていた。 コードには「複数単位で共通利用する」とコメントも書かれていた。 それでも7回リクエストが飛んだ。
フックを共有しても、呼び出し箇所ごとに実行される。「共通化した」で満足すると、実行回数を見落とす。
コードの共有(1つの関数を皆がimportする)と、 実行の共有(リクエストが1本になる)は別物だ。 後者には重複排除やキャッシュという、もう一段の仕組みが要る。 だからPhase 5(実装)の追加要件として、 共有データを扱う単位の実装後はリクエスト本数を実測で確認する ——移行元で1本だった取得が移行先でN本に増えていないか——を入れた。 「共有フックを作った」というコードの状態ではなく、 「リクエストが1本になった」という実行の結果で判定する。
6. 型への組み込み——止まる場所と、人間が判断する場所
以上をこのキットの流儀に沿って、Stop ConditionとHuman Decision Pointに落とした。
無主物リスト作成
人間が判断
リクエスト本数を実測
- Stop Condition 2本を追加 — ①無主物リストを作らないまま最初の単位の実装に着手しようとしている、②取得方針が未決定のまま2つ目の単位が同じAPIを叩こうとしている(再判断ポイント)。いずれも複数単位分割の移行・大規模改修時のみ発火し、通常フローやHotfixは重くしない。
- Human Decision Point 1行を追加 — 無主物の担当割当とデータ取得方針は設計判断なので、人間が行う。AIの仕事は無主物候補の列挙——つまり判断材料を出すところまでだ。
ここでも「AIは判断材料、判断は人間」の線は動かしていない。 無主物リストの列挙はAIが得意な仕事(コードを横断して読み、候補を挙げる)で、 担当を誰に割るか・重複取得をどう扱うかはプロジェクトの事情が絡む人間の判断だ。
7. まとめ——分割の余りを数える
- 単位分割の品質は高くなる。不具合は分割の境界——どの単位にも属さない領域に集中する
- 対策は最初の1単位に着手する前の無主物リスト。列挙されなかった領域は「誰も見ていない」とみなす
- 共有の判断軸は表示ではなくエンドポイント+パラメータ。マトリクスにして成果物に含める
- コードの共有と実行の共有は別物。実行回数は実測で確認する
- 先送りは許すが、無自覚な先送りは許さない——再判断ポイントを明記して先へ進む
このシリーズはこれまで、本体の新機能や公式ガイドを「外から取り込む」回が多かった。 今回は初めて、実プロジェクトの失敗を「中に還流させる」回になった。 型の価値は、こういうときに分かる。 痛い目を見た知見をチェックリストとStop Conditionに変換しておけば、 次の移行では同じ穴が仕組みとして塞がれている。 18件の不具合は授業料としては高いが、型に変換した瞬間から、二度払いはなくなる。
リポジトリはこちら: fecot/dev-agent-team