coding-discipline について
Coding Discipline は、コーディングエージェントがコードを変更・レビューする際の原則とワークフローです。リポジトリの把握、検証可能な目標の設定、変更範囲の抑制、リスクに応じたデータやエラーの扱いを重視します。
主な機能
- 01コード、テスト、規約の事前確認
- 02検証可能な成功基準の設定
- 03正しさを保つ最小変更の優先
- 04システム構造と公開契約の維持
- 05リスクに応じたデータとエラーの処理
- 06有用な設計意図のみを記録
活用シーン
- 01変更計画と前提の明確化
- 02実装案やテストの十分性のレビュー
- 03API や設定変更のリスク管理
- 04問題の診断と修正結果の検証
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ alannkl/ skills --skill coding-disciplineインストール後、Claude Code で/coding-disciplineを入力して読み込まれたか確認します。
同梱ファイルreferences/behavior-first-testing.mdreferences/diagnosing-bugs.md
詳細
コーディング規律
中核となる指針
以下を強いデフォルトとして適用します。明示的なユーザー指示、リポジトリのルール、セキュリティ/コンプライアンス要件、本番インシデント時の制約、または確立されたローカルの慣習と矛盾する場合は、より優先度の高い要件に従い(明示的なユーザー指示は確立された慣習より優先されます)、トレードオフを明示してください。
- コーディングの前に考える:コードを変更する前に、前提、曖昧な点、トレードオフを明らかにします。
- シンプルさを優先する:将来の予測ではなく、現在の要件(実際のデータ量、障害時のコスト、契約の安全性)に対する、最小限で正しい解決策を実装します。現在の要件で求められていない機能、抽象化、保護策、設定項目など、推測に基づく仕組みは追加しません。洗練とは、無駄を省くことです。
- 変更を必要箇所に限定する:依頼に必要な箇所だけに手を加え、既存のスタイルに合わせます。変更によって生じた問題のみを修正します。
- 判断を示す:実質的に優れたアプローチ、捉え方、設計を、明確な推奨とともに提案します。より良い案を伝えないことも、過剰に構築するのと同じく失敗です。制限するのは独断での実行であり、提案ではありません。
- 目標に基づいて進める:成功基準を定め、独立して検証できるだけの強さを持たせ、検証が完了するまで繰り返します。
これらの原則が互いに矛盾する場合は、正確性と契約の安全性を、シンプルさや差分の最小化より優先します。範囲を追加する場合は、黙って拡大せず、その旨を明示してください。
プロセスはリスクに応じて調整します。基準となるのは変更の分類ではなく、実際のコストと元に戻しやすさです。小規模で低リスクな変更では、前提、成功基準、引き継ぎメモはそれぞれ一文でよく、明白な場合は省略してもかまいません。契約に関わる作業、複数ステップの作業、または元に戻すのが難しい作業に限り、十分な厳密さを適用します。
ワークフロー
成果物を変更するときは、以下の手順に従ってください。レビューでは同じ原則を評価基準として使用し、リスクに応じて指摘の重み付けを行います。
-
コーディングの前に読む:ファイル、テスト、ドキュメント、慣習を確認します。
- 構造に関わる判断をする場合は、配置を決める前に、ツリー、インポート/依存関係のルール、ビルドファイル、テスト、その領域の代表的なファイルを確認します。
- マニフェスト、ロックファイル、CI 設定はドキュメントより優先します。これらと矛盾するドキュメントがあれば、明示してください。
- 動作上の影響範囲を特定します。入力、出力、副作用、永続化、外部呼び出し、ユーザーに見える契約を確認します。
- 実装やリスクに影響する前提は明示し、理解が曖昧なままにしないでください。
- 依頼で手段が指定されている場合(「ここにリトライループを追加する」など)、その指定がコードと矛盾する場合、または症状しか示されていない場合は、計画を立てる前に、その手段が解決しようとしている目標を言い換えてください。その目標により適した方法があれば推奨します。
- 実質的に異なる解釈が複数ある場合は、黙って一つを選ばず、明確な推奨とともに提示します。
- 大幅にシンプル、または高機能な設計がある場合は、トレードオフと推奨を提示します。実装するのは、ユーザーがその選択を承認した場合に限ります。
- バグ修正では、報告内容を原因ではなく症状として扱います。変更するコードの呼び出し元をたどり、報告された経路だけを修正して同じ問題を持つ他の呼び出し元を残すのではなく、共通の原因を一度で修正します。誤った箇所への最小限の変更は、二つ目のバグです。
- 原因がすでに明らかでない場合は、修正の前に
references/diagnosing-bugs.mdを読み、原因を特定してください。
-
検証可能な最小限の計画を定義します。
- 依頼を具体的な成功基準に置き換えます。それぞれ、曖昧な目標ではなく、実行するコマンド、期待される出力、アサーションなど、観察可能な確認事項として記述します。(「不足しているユーザーを処理する」よりも「
GET /users/:idは存在しない ID に対して 404 を返す」のほうが適切です。) - 新しい動作や変更された動作、再現可能な回帰、テスト作業、テストの妥当性のレビューを行う場合は、実装や実行可能なテスト本文に着手する前に
references/behavior-first-testing.mdを読み、Behavior-First Testing ワークフローに従います。 - 必要な基準を満たしつつ、システムの構造を悪化させない、最も範囲の狭い変更にとどめます。
- 曖昧さによって解決策が大きく変わる、リスクが生じる、または成功基準の検証が不十分になる場合は、質問します。
- 回答してくれる人がいない場合は、最も元に戻しやすい解釈を選び、その前提を引き継ぎ事項で明確に示して作業を進めます。
- 実装の途中で、合意した計画と矛盾することが判明した場合は、計画の意図を尊重する最も元に戻しやすい選択肢を取り、逸脱をノートファイルに記録します(コミットメッセージや PR の説明よりもノートファイルを優先します。引き継ぎの要約は記録にはなりません)。そのうえで、引き継ぎ事項でも明示します。
- 発見した事実によって計画の前提が無効になる場合は、無理に進めず、作業を止めて計画を立て直します。
- 複数のステップがある作業では、各ステップとその検証方法を明示し、次のステップを始める前に検証を実行します。同種の編集をまとめて行う場合も、編集を一括して最後に一度だけ検証するのではなく、各編集を適用するたびに検証します。
- 依頼を具体的な成功基準に置き換えます。それぞれ、曖昧な目標ではなく、実行するコマンド、期待される出力、アサーションなど、観察可能な確認事項として記述します。(「不足しているユーザーを処理する」よりも「
-
システムの構造を維持します。
- コードベースの既存構造、つまりシステムの構造や規約は、好みを示す証拠として扱いますが、絶対的な根拠とはみなしません。通常はそれに合わせますが、ユーザーの意向や最近の作業によってパターンが変わりつつある場合は、古い多数派ではなく、移行先のパターンを拡張します。現在の構造が変更を妨げる場合や、これらの原則と衝突する場合は、理由を添えて改善案を提示します。
- リポジトリですでに決まっている場合を除き、技術的な役割ごとに分散させるより、ドメインや機能ごとに所有範囲をまとめることを優先します。
- システムがレイヤー化されている場合は、責務を分離します。既存のシステムですでに責務が混在していて変更が小さい場合を除き、境界アダプターは検証と変換を行い、コアロジックは判断を行い、永続化や統合のコードは I/O を実行します。
- 小さなスクリプトや単一ファイルのプログラムでは、レイヤーや間接化を新たに導入しません。
- 依存関係は安定したコアの動作に向け、変化しやすい境界から遠ざけます。隠れたグローバル、暗黙の状態、制御しにくい副作用よりも、明示的な協調関係を優先します。
- 依存関係の循環を避けます。循環があると、独立した部分がひとまとまりとして変更されることになります。
- 公開契約を保護します。API、スキーマ、マイグレーション、イベント、設定、CLI フラグ、シリアライズ形式、永続化データについては、互換性、段階的な展開、ロールバックに配慮する必要があります。
-
明確なコードを書きます。
- 最善のコードは、書かずに済むコードです。コードを追加する前に、必要か(YAGNI)、リポジトリにすでに用意されていないか、標準ライブラリやプラットフォームで対応できないか、すでにインストール済みの依存関係で対応できないかを順に確認し、当てはまる最初の段階で立ち止まります。それから、動作する最小限のコードだけを書きます。小さいことは、脆弱であることを意味しません。同じくらい小さな選択肢が二つあるなら、エッジケースも正しく扱える方を選びます。
- 意図が伝わる名前を付け、副作用がある場合は名前から分かるようにします。
- 関数とモジュールは一つの目的に集中させ、ガード節を使ってネストを減らします。
- 重複、リスク、契約、複雑さから必要性が明らかになるまでは、先回りした仕組みを追加しません。「メールを1通送る」要件に必要なのは関数呼び出しであり、リトライ設定付きの差し替え可能な
NotificationProviderインターフェースではありません。 - ライブラリや公開 API のコードでは、公開された契約で約束されている拡張ポイントは、先回りした仕組みではなく必須要件です。
- 浅い受け渡し用のラッパーより、意味のあるモジュールと、簡潔で誠実なインターフェースを優先します。
- 「レビューで過剰に複雑だと思われないか」と自問します。そう思われる場合、あるいは問題の規模に比べて解決策が大幅に大きくなっている場合は、立ち止まって簡素化します。
- 失敗だけでなく、摩擦にも目を向けます。同じ仕組みに三つ目の特別扱いが加わる、if/else の連鎖が長くなる、最初のブール値と同期させる必要がある二つ目のブール値が増える、修正に独自の修正が必要になる、既存の設計を活用せず回避する、といった兆候は、それぞれの手順がうまくいっていても、進むべき道が間違っていることを示しています。問題を一段上の視点、つまり現在の仕組みではなくコードが実現する成果として捉え直し、より簡単な方法がないか確認します。多くの場合、構造(ステートマシン、ルックアップテーブル、判別共用体など)を使えば、分岐の山を置き換えられます。そのまま進むなら、既定の選択ではなく、明示的な判断として決めます。
- 型付き言語では、部分性が現れる箇所で、不正な状態を表現できないようにします(
completed: booleanと省略可能なcompletedAtを併用すると、あり得ない組み合わせを許してしまいます)。キャストや型チェックを黙らせる手段で型システムを欺いてはいけません。部分性が現れる箇所に限って型を強化し、それ以上は広げません。 - 公式または標準的なドメイン型や API が利用できる場合は再利用し、信頼できる情報源とずれていくローカルな別名を独自に作らないようにします。
- 複雑さ、リスク、保守負担を減らせるなら、高品質な依存関係の提案または追加を検討します。採用前に、セキュリティ、ライセンス、バンドルサイズ、互換性、リポジトリとの適合性を考慮します。
-
データ、エラー、並行処理を慎重に扱います。
- 信頼できない入力は境界で検証し、外部のデータ形式を各コンテキスト内で使う標準モデルに変換します。検証済みの境界を越えた後は型を信頼し、呼び出しチェーンの深部で冗長な再検証を行いません。「このデータは今、システム境界を越えているか」を判断基準にします。ただし、金銭や認可など重要度の高い不変条件には、内部アサーションが有効な場合もあります。
- 認可、インジェクションのリスク、シークレット、個人データ、最小権限アクセスは、後付けではなく設計の一部として扱います。
- 保存データ、設定、キュー、外部との契約を変更するときは、旧データ、バージョンが混在する状況、マイグレーション、デフォルト、ロールバックの挙動を考慮します。
- 原因を診断できるだけの文脈を含め、対処につながるエラーを出します。
- シークレット、認証情報、トークン、秘密鍵、個人データ、機微なペイロード全体をログに記録したり、コミットしたりしてはいけません。
- 適切なログ、メトリクス、トレース、監査イベントを使い、診断が難しくなるシステムの本番環境での挙動を可観測にします。
- 意図的で、文書化され、可観測である場合を除き、隠れた並行処理や、結果を待たずに実行する処理を避けます。
- ホットパスでは、データ量、アルゴリズムの計算量、インデックス、バッチ処理、キャッシュ、N+1 のリスクを考慮します。広範な最適化の前に計測します。
-
ファイルの整理は慎重に行います。
- 新しいコードは、新しく参加した人が置かれていると予想する場所に配置します。そのコードと一緒に読み、変更するコードやテストの近くに置きます。ディレクトリツリー上の距離は、概念上の距離に対応させます。
- 行数ではなく、変更する理由ごとに分割します。通常と異なるサイズは、隠れた境界を探すきっかけと捉え、それだけを理由に分割してはいけません。互いに理解し合う必要のある断片より、まとまりのある長いファイルのほうが適切です。
- 新しい単位に活用できる実質的なインターフェースがある場合に限って分割し、中継だけのレイヤーや、ナビゲーションのためだけの抽象化は避けます。
- 新しい単位は、まとまりのあるモジュール群を含むディレクトリでもかまいません。
routes.tsをroutes/に移し、ドメインごとにモジュール(users、admin)を置くのは、各モジュールが実質的な境界である場合は妥当ですが、断片が恣意的な分割だったり、フォルダーが再エクスポートのためだけに存在したりする場合は妥当ではありません。 - 型、定数、ヘルパー、ドキュメントは、共有利用を正当化できる場合を除き、主な利用箇所の近くに置きます。
- 共有コードの抽出は、実際に再利用されてから行い、まとまりのあるテーマと所有者ごとにグループ化し、あらゆるものを詰め込むヘルパーやユーティリティの置き場を避けます。
- 重要な役割を果たす命名、所有権、依存関係、境界のルールだけを文書化するか、適用を徹底します。
-
有用な意図だけを文書化します。
- まず名前と構造でコードを明確にし、コメントは自明でない理由、つまり不変条件、トレードオフ、順序、再試行、冪等性、フォールバック、セキュリティ上の制約、ビジネスルールを説明する場合にのみ追加します。
- 上限が分かっている、より単純な実装(粗いロック、O(n²) の走査、単純なヒューリスティックなど)を意図的に選ぶ場合は、その上限と、より高度な実装に切り替える根拠となる条件をコメントに記します。そうすることで、その簡略化が見落としではなく意図的な判断だと伝わります。
- 自明な構文や単純な制御フローにはコメントを付けません。また、分かりにくいコードにはコメントを付けるのではなく、コード自体を簡潔にします。
-
検証して、クリーンな状態で完了します。
- 最も関連性の高いフォーマッター、リンター、型チェック、テスト、ビルド、または対象を絞ったスモークチェックを実行します。
- 全体チェックのコストが高い場合や実行できない場合は、意味のある範囲で最も絞ったチェックを実行し、残る課題を明記します。
- 自動、手動、実際のモデルを使ったものを問わず、すべてのチェックで、制御できる時間は待ち続けずに早送りします。クロックを注入する、タイムスタンプを古くする、実行期間を短く設定するなどの方法を使い、チェックに必要な最短時間だけ待ちます。経過時間そのものが根拠となる場合(長時間の安定性確認やレイテンシ測定など)、または制御できない外部要因の期間が関わる場合は、待ち時間に上限を設け、短縮したチェックでは何を証明できなかったかを明記します。チェックの実行時間は最適化すべきコストです。単一テストは数秒、全テストスイートは数分での完了を目指します。
- リスクの高い変更では、安全性が依存する唯一の事実を特定し、主張ではなくコードの実行によってその事実を検証します。書面での説明は、正しいかどうかにかかわらず説得力があるように読めてしまいます。シンボル検索で見つからない影響範囲も確認します。シリアライズされたデータ、ワイヤーフォーマット、他言語のリーダー、機能フラグなどが対象です。検索結果が何もなくても、記録する価値のある回答です。
- 差分をレビューします。変更した各行が、依頼内容または変更に伴う整理に対応していることを確認します。
- レビューしやすい変更にします。無関係な変更は避け、すぐには明らかでないトレードオフを説明し、契約や移行への影響を明示します。
- 編集で追加した未使用のインポート、変数、関数、分岐を削除します。
- 既存の無関係な問題には手を加えず、必要に応じて別途言及します。
- 課題を報告する際は、好みではなく実質的な問題を重視します。リポジトリ独自の規約から外れている場合は重要な指摘ですが、単に好みのスタイルと異なるだけなら重要な指摘ではありません。
- 足場として追加した各シナリオに、実行可能なテスト本体があること、またはスキップしたままにする理由が文書化されていることを確認します。
- コミットを提案する場合は、提案するコミットメッセージも示し、1回の承認で両方を許可できるようにします。
- コードを保守するエンジニアに向けて引き継ぎを記します。呼び出し元、運用担当者、保存データにとって何が変わるのかを説明します。重大な変更では、その仕組みと、今後の変更でも維持すべき不変条件を説明します。
- どの成功基準を満たしたか、チェックで確認できなかったことは何かを明記します。テストの合格が示すのは、そのテストで実行した動作だけです。前提、計画からの逸脱、スキップしたテストや見直した足場の結果とその理由、意図的に対象外とした事項、実際に残っている対応を含めます。
- 引き継ぎの詳しさは変更の影響に応じて調整します。名前の変更なら1文で済みますが、トランザクション境界を変更した場合は、原因と結果を説明する必要があります。1文の引き継ぎであっても、動作の変更点と、何を検証したかを示します。