design-interface
実装の前に、呼び出し側の使い方、型、関数名、モジュールの分け方を決めてユーザーと合意し、合意した形に沿って実装する skill。
SKILL.mdskills/design-interface/SKILL.md
GitHubDesign Interface
この skill は明示的に呼び出された場合のみ使用する。
$design-interface <作るもの>
<作るもの>がなければ、会話から対象を読み取る。読み取れなければ、ユーザーに聞く。
目的
コードを書く前に、形を決めてユーザーと合意する。 形とは、呼び出し側の使い方、型、関数名、モジュールの分け方を指す。
AI が形を決めてから実装すると、ユーザーは「なぜこの名前か」「なぜこの分け方か」を説明できない。 形を決める場にユーザーが加わり、名前と書き方の意図を自分の言葉で説明できる状態にする。
形は、書いたあとに直すほど手間が増える。 呼び出し側が増えるほど、名前や型を変えたときに直す範囲が広がる。
Workflow
- 関わる既存の仕組みを調べる
- 呼び出し側の使い方を書く
- 型と signature を描く
- ユーザーと合意する
- 合意した形に沿って実装する
- 形が合わなければ描き直す
1. 調べる
新しいコードが関わる既存の仕組みを読む。
- 呼び出し元と呼び出し先
- 既存の型、命名、モジュールの分け方
- 入力を検証している場所と、エラーの扱い方
ファイル名を挙げるだけで済ませない。 データが入口から結果までどう流れるかを説明できるまで読む。 周りに既存の仕組みがない新規の作業なら、この手順を省く。
2. 使い方を書く
型より先に、呼び出し側のコードを 2〜3 か所書く。 型と signature は、この使い方から決める。 使い方と型が食い違ったら、使い方に合わせて型を直す。
3. 形を描く
型、関数の signature、モジュールの分け方を描く。本体は書かない。 本体は「未実装」とし、難しい処理だけ疑似コードで書く。
使い方と形は、返信の中で示す。合意を得るまで、ファイルには書かない。 形が却下されたときに、書きかけのコードを残さないため。 型と signature を読むだけで、入力から結果までのデータの流れを追えるようにする。
成り立つ別案があれば、それも描く。 別案は、分け方や型の持ち方が違うものに限る。1 つの形の細部を変えただけのものは、別案に数えない。 成り立つ別案がなければ、無理に作らず、他の形が成り立たない理由を書く。
案は次の基準で比べる。
- 呼び出し元から、主要な判断、実行順序、副作用、結果、失敗時の扱いを追えるか。 1 つの処理を理解するために、関数やファイルを無用に往復させない
- 呼び出し側が内部の実装を読まずに、目的と結果、副作用や失敗の有無を判断できる境界で分けているか。 行数を減らすためだけに分けない
- 同じ意図のものを、1 つの書き方に揃えているか。意図が違うものは、名前や型で違いが見えるか
- 要求に対して作りすぎていないか。今は使わない拡張点、設定、層を入れない
- 変更後のコードが保守しやすいか
変更の大きさだけを、案を選ばない理由にしない。 既存コードの書き換えが多くても、変更後のほうが保守しやすいなら、その案を候補に残す。
名前は、呼び出し側から見た意味で付ける。実装方法を名前に入れない。
名前と書き方は、基本は既存コードに揃える。 既存のやり方が保守性を下げている場合は、揃えない案を論点として出す。 既存コードの書き換えは勝手に行わない。書き換えるかどうかは、ユーザーが決める。
4. 合意する
描いた形をユーザーに見せ、合意を得るまで本体を書かない。
次のものを見せる。
- 呼び出し側の使い方
- 型と signature
- 名前ごとの意図。なぜその言葉を選んだかを 1 文で書く
- 別案と、選ばない理由。別案がなければ、他の形が成り立たない理由
- この形で受け入れるトレードオフ
ユーザーの判断が必要な点は、一覧で一度に聞く。各論点は次の形で書く。
論点: 読み込みの失敗をどう返すか
- A: 成功と失敗を 1 つの型の別の状態として返す → 呼び出し側は、型を見て失敗がありうると分かり、成功と失敗を区別して扱える
- B: 失敗したら例外を投げる → 呼び出し側は成功時だけを書けるが、失敗の扱いを忘れても気づけない
推奨: A。失敗時の動きを呼び出し側から読めることを優先する。既存の読み込み処理も、失敗を結果の型で返しているため
B にしない理由: 成功時だけを書ける手軽さを手放しても、失敗がありうることを型で示すほうが大きいため
- 選択肢ごとに、選んだ結果として何が変わるかを 1 文で書く
- 推奨には、何を優先してそれを選んだかと、根拠になった事実を書く
- 推奨しない選択肢ごとに、その選択肢の利点を手放してよい理由を書く
- 選ばない理由は、変更後の保守のしやすさや追いやすさで書く。変更が大きいことだけを理由にしない
推奨の理由だけでは、他の選択肢を選ばずに何を手放したのかが分からない。 選ばない理由まで示し、ユーザーが自分の判断として説明できるようにする。
ユーザーが形に異論を出したら、異論の範囲に合わせて見直す。
- 形の前提を変える異論: その内容を前提に加えて、手順 1 から見直す
- 名前の変更のように一部だけに関わる異論: 該当する部分だけを直して示す
5. 実装する
合意した形を契約として、本体を書く。
合意した形は、実装中の整理や改善でも変えない。 名前や signature をより良くできると気づいたら、変える案として示し、合意を得てから変える。
実装中に形と食い違ったら、黙って吸収しない。 例えば、合意した signature にない引数が必要になったら、原因が次のどれかを見極める。
- 形が間違っていた
- 要求を見落としていた
- 実装が要求を超えている
呼び出し側から見える部分を変える必要があれば、変える内容と理由を伝え、合意を得てから変える。 呼び出し側から見える部分とは、名前、型、signature、モジュールの分け方を指す。 外から見えない内部の補助関数は、合意を取り直さずに変えてよい。
形以外でも、ユーザーと合意した内容を変える必要が出たら、同じく合意を得てから変える。 合意した内容とは、ユーザーが依頼や会話で明示したか、提案を承認した事項を指す。 対象は、仕様・変更範囲・名前・型・signature・モジュールの分け方・前提とする。 合意を取り直すために止めるときは、書き終えた本体、失敗したままのテスト、途中の変更を報告する。
6. 描き直す
1 回の例外ケースだけで、形を捨てない。 次の兆候が繰り返し現れたら、形が間違っていると判断する。
- 関係のない箇所で、同じ形の回避策が必要になる
- 関係のない例外ケースが、それぞれ特別な分岐を必要とする
- 型を通すために、
any、型の cast、実際には常に値がある optional が必要になる - 呼び出し側が、内部の規則を知らないと正しく使えない
- 合意した形との食い違いが、同じ形で 2 回以上起きる
描き直すときは、新しく分かった制約を最初からの前提として描く。古い形に継ぎ足さない。 描き直した形は、手順 4 から合意を取り直す。 そのとき、古い形で書いた本体をどうするか(消す、新しい形に合わせて書き換える)も示す。
完了時の報告
- 合意した形と、実装中に変えた点、その理由
- 実行したテストや検証と、その結果
Rules
- 合意を得るまで本体を書かない
- 使い方を先に書き、型と signature をそこから決める
- 名前ごとに、その言葉を選んだ意図を示す
- 推奨には、選ぶ理由と、他を選ばない理由を書く
- 変更の大きさだけを、案を選ばない理由にしない
- 既存のやり方が保守性を下げているときは、揃えるかどうかを論点として出す
- 呼び出し側から見える形を変えるときは、合意を取り直す