| name | upstream-fix |
| description | 修正・バグ修正・リファクタリングの依頼を受けたとき、末端パッチではなく上流での根本解決を
「どう直すか」決めるまでを担うスキル(実装フェーズには踏み込まず、方針決定までを担う)。
「これ直して」「バグがある」「修正して」「ここがおかしい」「リファクタリングして」「設計を見直して」
「場当たり的じゃなくちゃんと直して」「根本から直して」「ゼロから設計し直すなら」といった依頼で使う。
コード修正の依頼で発動し、症状の奥に設計上の根本原因が無いかを一度診断する。
smell が無ければ局所修正で止め、smell があれば上流まで遡って直す方針を立てる。
|
Upstream Fix - 上流解決スキル
修正依頼を受けたとき、その場しのぎのパッチではなく、「やりたいことが明確になった今、ゼロから作るならどう設計するか」 を起点に根本原因を解決する方針を立てる。
末端でのパッチは「今この瞬間」は最速に見える。しかし同じ種類の問題が別の場所で再発し、パッチが積み重なってコードベースが複雑化する。上流(データの流れの源流、抽象化のより高いレイヤー、設計の根本)で直せば、下流の問題が一括で消える。修正コストは一度だけ、効果は永続的。
このスキルの役割と境界
- 担うのは「診断 + 設計判断」まで — 何が根本原因で、どこをどう直すかを決める。
- 実装は別フェーズに引き継ぐ — テスト先行・実装・セルフレビュー反復は実装ワークフロー(
autonomous-dev 等の実装スキルがあればそれ)の担当。本スキルで重複して書かない。
- やりすぎない — typo や 1 行修正でも「周辺に smell が無いか」は一度確認する。だが smell が見つからなければ局所修正で止める。すべてを上流改修に膨らませるのは別種の負債(Phase 4 の歯止めを参照)。
- プロジェクトの規約に従う — 型・エラー処理・移行手順・リリース手順などのルールファイルがリポジトリにあれば、その流儀に合わせて適用する。本スキルは特定のスタックや規約に依存しない。
Phase 1: 現状把握
修正に手をつける前に、現状を正確に理解する。
- 症状を額面通り信じない — ユーザーが報告した症状・原因の見立て(「ここで握りつぶしている」「この値が null になる」)を前提にせず、実コードで裏取りする。報告箇所に問題が無い/別物だった/前提の仕組み自体が存在しない場合は、修正に入る前にその食い違いを先に伝える。誤った前提のまま「直す」と、存在しない問題をいじることになる。
- コミット履歴を読む — この領域でどんな変更が積まれてきたか。
legacy / 暫定 / workaround の痕跡は上流問題のサイン。
- 関連コードを広く読む — 報告箇所だけでなく、呼び出し元・呼び出し先・同じデータを扱う兄弟箇所も読む。
- データの流れを追う — 問題のあるデータ/状態が、どこで生まれ、どこを通り、どこで症状を出すかをトレースする。
症状の場所 ≠ 原因の場所 であることが多い。例:
const label = `${user.displayName ?? "ゲスト"} 様`;
Phase 2: 診断
現状把握の結果を整理し、ユーザーに共有する。直接原因で止めず 「なぜそのコードがそうなっているか」を 2〜3 段掘る(因果連鎖を辿り、設計上の根に当てる)。
## 診断結果
### 症状(ユーザーが報告した問題)
- [何が起きているか]
### 直接原因(症状を引き起こしているコード)
- [どのコードが問題か]
### 根本原因(なぜそのコードがそうなっているか — 設計の根まで掘る)
- [責務の配置ミス/データ正規化不足/契約の曖昧さ/抽象化不足 など]
### 影響範囲
- [同じ根本原因から生じている、または生じうる他の問題]
Phase 3: ゼロから再設計する(核心)
「今あるコードをどう直すか」から思考を始めると、既存実装に引きずられて末端パッチに着地する。代わりに 「やりたいことが明確になった今、この領域を白紙から作るならどう設計するか」 を描き、現状との差分を上流修正のスコープにする。
手順
- 本質的な意図と不変条件を抽出する — この機能・コードが達成すべき目的と、常に成り立つべき不変条件(invariant)を、現状実装から切り離して言語化する。「表示名は常に non-empty である」のように。
- 本物の制約と偽の制約を分ける — 残すべきは外部由来の制約だけ。
- 本物: 外部 API が null や欠損を返す/実行時間・リソースの上限/決済や外部サービスの仕様/ビジネスルール。
- 偽: 「今のコードが
string | null を各所に流している」「ここは昔からこう書いてある」——これは捨ててよい。
- 理想形をスケッチする — 本物の制約だけを入力に、型・データフロー・責務配置を白紙から描く。「不正な状態を表現できない型」「正しいデータを 1 箇所で作る源流」を目指す。
- 理想形 ⊖ 既存実装 = スコープ — 理想と既存実装の差分が、今回やるべき上流修正。差分が大きく一気に行けないなら移行経路を段階に切る(後述の expand-contract)。
代表的な上流パターン
理想形を描くとき、次の普遍的な上流パターンに当てはまらないか確認する。プロジェクト固有の規約(型・エラー処理・移行手順のルールファイル)があれば、その流儀に合わせて適用する。
| 症状に出るパターン | 上流での直し方 |
|---|
| 下流の各所で null/型チェック・デフォルト補完が散らばる | Parse, Don't Validate — 境界で 1 度 parse し、以降は正しい型だけを持つ。不正な状態を型で表現不能にする |
| 同じ値・手順・定義が複数箇所にコピーされてズレる | SSOT — 定義を一箇所に置き、他はそこを参照する |
| 例外を握りつぶす/エラーを文字列のまま投げ分けできない | エラーを具体的な型として表現し、網羅的に分岐する(型システム / Result 型 / 例外階層など) |
| スキーマや API の破壊的変更を 1 発でやろうとして詰む | expand-contract(追加 → 移行 → 削除)で段階的に移行する |
| 状態・種別の分岐に if が増殖している | 状態を**直和型(tagged union)**で表し、網羅的な分岐(exhaustive match)で扱う |
「ここまでやれば綺麗になる」が見えたら、一見無関係でも根を辿れば同じ設計問題に繋がる領域まで含めて提案する。見えている改善を黙るのは機会損失。
Phase 4: 提案
修正方針を選択肢として示す。
## 修正方針
### A: フル改善(理想形に寄せる)
- 何をするか: [根本原因の解消 + 周辺の設計改善]
- 影響範囲: [変更が及ぶファイル・機能]
- メリット: [この領域全体がスッキリ、今後の変更も楽になる]
### B: 上流修正(報告された問題の根本解決)
- 何をするか: [根本原因の解消に絞った変更]
- メリット: [根本解決、再発防止]
### C: 局所修正(最小パッチ)
- 何をするか: [症状を直接修正]
- メリット: [変更が小さい、リスクが低い]
- リスク: [再発の可能性、技術的負債の蓄積]
どれを推すか:
- 緊急の本番障害 → C で止血、後日 A or B
- 通常の修正依頼 → B を推奨
- 「ちゃんと直して」「綺麗にして」「ゼロから設計し直すなら」→ A を推奨
- 急ぎでない、または明示的に「ちゃんと直して」と言われている → 提案をスキップして A で進めてよい
A が見えているなら必ず提案に含める。
上流修正をやりすぎない歯止め
上流に倒すバイアスの反作用として、スコープを膨らませすぎる失敗もある。次に当たるなら一段下の方針(A→B、B→C)に落とす:
- YAGNI — 「将来こうなるかも」のための抽象化は今は作らない。現に複数箇所で再発している事実があるときだけ共通化する。
- 変更リスク — 上流ほど影響半径が大きい。広く触る改修はテストの網と回帰確認のコストに見合うか確認する。
- 本番影響 — 本番データ・課金・デプロイなど影響の大きい領域に触れる上流改修は、プロジェクトの運用手順(migration runbook 等)の範囲に収まるか先に確認する。膨らむなら phase を切る。
- 依頼との乖離 — ユーザーが 1 行修正を頼んだのに大改修を黙って始めない。A が見えたら「提案」する(黙って実行しない)。
Phase 5: 実装への引き継ぎ
方針が決まったら、実装ワークフロー(autonomous-dev 等: テスト先行 → 実装 → セルフレビュー反復 → 報告)で実装する。本スキル固有の申し送りは以下:
- テストは根本原因に当てる — 症状の再現テストだけでなく、上流の不変条件を守るテストを書く。症状テストだけだと別経路で再発する。
- コミット粒度 — 1 コミット 1 関心事。リファクタリングと機能修正は分ける。
- WHY を残す — なぜ上流で直したか(どの末端パッチ群を一掃したか)をコミットメッセージに残す。
- リリース手順 — 上流改修がリリースされる成果物の挙動を変えるなら、プロジェクトのバージョニング/リリース手順(changeset 等)に従う。
- 検証の閉ループ — 実装後、Phase 1 で挙げた下流症状が全部消えたか、かつ新しい症状が出ていないかを確認する。残っていれば根本原因の診断(Phase 2)が外れている可能性が高い。
上流修正の判断基準
| シグナル | 上流修正すべきか |
|---|
| 同じ種類の修正が複数箇所にある | はい — 共通の原因がある |
if や特殊ケース分岐が増えている | はい — データの正規化不足 |
| 「ワークアラウンド」「暫定」コメントがある | はい — 本来の解決が別にある |
| 修正箇所がデータの流れの下流にある | はい — 上流で正しいデータを作るべき |
| 同じ定義・値が複数箇所に転記されている | はい — SSOT 違反、一箇所に寄せる |
| 問題が 1 箇所だけで設計に問題がない | いいえ — 局所修正で十分 |
| 再発が観測されておらず将来予測だけ | いいえ — YAGNI、局所修正で止める |
| 影響半径が本番データ・課金・デプロイに及び、依頼は小さい | まず局所修正、上流改修は別途 phase を切って提案 |
| 緊急の本番障害 | まず局所修正、後日上流修正 |
アンチパターン
| やりがち | なぜダメか | 代わりに |
|---|
| 報告された症状を鵜呑みにする | 原因の場所がズレている/前提の仕組みが存在しないことがある | 実コードで裏取りし、食い違いを先に伝える |
| 症状だけ消す | エラー握りつぶし・表示隠し・条件分岐で回避は再発する | 原因を上流で断つ |
| コピペパッチ | 同じ修正を複数箇所に適用 = 共通化すべきサイン | 上流で 1 度だけ正規化 |
| 下流でバリデーション追加 | 本来は上流で正しいデータを保証すべき | 境界で parse、以降は正しい型 |
| 既存コードから設計を考える | 末端パッチに引きずられる | Phase 3 で白紙から理想形を描く |
| 改善の可能性を黙殺する | 「ここまでやれば綺麗」が見えているのに提案しない | A が見えたら必ず提案に含める |
| 何でも上流改修に膨らませる | スコープクリープも負債 | Phase 4 の歯止めで方針を一段落とす |