quality-code について
フルスタック TypeScript の開発者向けに、コード作成とレビューの指針を示すスキルです。型による状態の制約、エンドツーエンドの型連携、実サービスを使ったテスト、OpenTelemetry に重点を置きます。
主な機能
- 01ブランド型と判別可能なユニオンで状態を制約
- 02型をエンドツーエンドで共有・導出
- 03オブジェクト引数を優先
- 04Standard Schema による検証
- 05実サービスを使ったテスト
- 06OpenTelemetry による計装
活用シーン
- 01無効状態の少ない API の設計
- 02クライアントとサーバーの型重複を削減
- 03再利用可能な検証スキーマの受け入れ
- 04データベースやローカルクラウドサービスの動作をテスト
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ rhyssullivan/ skills --skill quality-codeインストール後、Claude Code で/quality-codeを入力して読み込まれたか確認します。
詳細
高品質なフルスタック TypeScript を記述する
TypeScript コードを記述またはレビューするときは、以下の原則を適用してください。
表現できない状態を作る
型システムを使って、無効な状態がコンパイル時にエラーになるようにします。到達可能な状態が少ないほど、コードの読み取りや変更が容易になります。
ブランド型
プリミティブ型にブランドを付け、取り違えを防ぎます。境界で一度検証すれば、その後のコードでは型を信頼できます。
type PhoneNumber = string & { __brand: "PhoneNumber" };
function parsePhone(input: string): PhoneNumber {
if (!/^\+?\d{10,15}$/.test(input)) throw new Error(`Invalid: ${input}`);
return input as PhoneNumber;
}
function sendSMS(to: PhoneNumber, body: string) {
/* input is trusted */
}
プロジェクトですでにネイティブのブランド型をサポートするライブラリ(Effect など)を使用している場合は、独自に実装するのではなく、そのライブラリのプリミティブを使ってください。
フラグの集合より判別可能なユニオン
// Don't — invalid combos representable
type State = { loading: boolean; user?: User; error?: string };
// Do — only valid states exist
type State =
| { status: "loading" }
| { status: "success"; user: User }
| { status: "error"; error: string };
型をエンドツーエンドで引き継ぐ
DB スキーマ → サーバー → クライアント間で、手動で重複させることなく型を共有します。プロジェクトですでに採用しているエンドツーエンドの型ツール(tRPC、oRPC、Elysia、TanStack Start)を使ってください。users.email が Email としてブランド付けされていれば、クライアントにもブランド付きのまま届くようにします。
導出できる型を再記述しないでください。新しいインターフェースを書く前に、Pick、Omit、Parameters、ReturnType、Awaited、typeof などを使います。関数の引数は手作業で型を付けるのではなく、定義元から推論してください。
// Don't — duplicate shape, drifts when the row changes
type UserSummary = { id: string; email: Email };
function renderUser(u: UserSummary) {
/* ... */
}
// Do — derive from the source of truth
type User = Awaited<ReturnType<typeof db.query.users.findFirst>>;
function renderUser(u: Pick<User, "id" | "email">) {
/* ... */
}
位置引数ではなくオブジェクトを渡す
// Don't — swap two args, still compiles
sendEmail("Welcome!", "Hi there");
// Do — order-independent, self-documenting
sendEmail({ to: "[email protected]", body: "Hi there" });
パフォーマンスが特に重要なホットパスでは避け、それ以外ではデフォルトで使ってください。
共有バリデーションには Standard Schema を使う
バリデーターを選びたくないライブラリやコードでは、StandardSchemaV1<unknown, T> を受け取るようにしてください。
できる限り実環境に近いテストを行う
実行できるものはモックせず、実際のサービスを起動してください。
- AWS には LocalStack
- Cloudflare Workers には Miniflare
- モック DB ではなく、実際の Postgres/SQLite(例:
bun:sqlite)
テスト環境のないサードパーティサービスだけをモックしてください。
print ログではなく OpenTelemetry を使う
オブザーバビリティを追加するときは、OTel スパンを使って計装してください。ユーザーからリクエスト ID が送られてきたときに、推測ではなく回答できるようになることを考えれば、初期設定のコストはすぐに回収できます。