design-interface

実装の前に、呼び出し側の使い方、型、関数名、モジュールの分け方を決めてユーザーと合意し、合意した形に沿って実装する skill。

SKILL.md

GitHub

Design Interface

この skill は明示的に呼び出された場合のみ使用する。

$design-interface <作るもの>
  • <作るもの> がなければ、会話から対象を読み取る。読み取れなければ、ユーザーに聞く。

目的

コードを書く前に、形を決めてユーザーと合意する。 形とは、呼び出し側の使い方、型、関数名、モジュールの分け方を指す。

AI が形を決めてから実装すると、ユーザーは「なぜこの名前か」「なぜこの分け方か」を説明できない。 形を決める場にユーザーが加わり、名前と書き方の意図を自分の言葉で説明できる状態にする。

形は、書いたあとに直すほど手間が増える。 呼び出し側が増えるほど、名前や型を変えたときに直す範囲が広がる。

Workflow

  1. 関わる既存の仕組みを調べる
  2. 呼び出し側の使い方を書く
  3. 型と signature を描く
  4. ユーザーと合意する
  5. 合意した形に沿って実装する
  6. 形が合わなければ描き直す

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 をそこから決める
  • 名前ごとに、その言葉を選んだ意図を示す
  • 推奨には、選ぶ理由と、他を選ばない理由を書く
  • 変更の大きさだけを、案を選ばない理由にしない
  • 既存のやり方が保守性を下げているときは、揃えるかどうかを論点として出す
  • 呼び出し側から見える形を変えるときは、合意を取り直す