Skip to content
cccskills
FreePublished on GitHub

design-principles

実装前に参照する設計原則。理解容易性 = 読み手の思考量の少なさを基準に、AI が作りがちな失敗4パターン(引数・依存5個以上、コマンド層肥大・ドメイン貧血症、ポリモーフィズム機会の見逃し、トリッキーな実装)と、実装後セルフチェックの7観点(名称・役割・参照・状態・面積・階層・秩序)を言語化。

Use when starting any implementation task, when user says '設計原則', 'design principles', or before writing new entities/commands/repositories.

The English summary is being prepared. Showing the original description.

See how to install

Files included (1)

  • SKILL.md12.8 KB

SKILL.md (original)

Review the instructions the agent will receive before you install.

位置づけ

本スキルはコードを書く前と書いた直後に読む設計原則。レビュー時の機械的検出は Biome(bun run lint)と review-code / review-arch / review-scoring が担い、本スキルは「そもそもそう書かない」ための判断基準を言語化する。

規約の正は docs/ARCHITECTURE.md(層構成・スコアリング/ML パイプライン)と docs/DEVELOPMENT.md(技術スタック・命名・コーディング規約)。本スキルはその2つを前提に、判断の物差しだけを与える。

物差しは一つ: 理解容易性 = 読み手の思考量の少なさ。「初見の人間が上から読んで、追加の思考をどれだけ強いられるか」で書き方を選ぶ。短さ・巧さ・抽象度の高さは、それ自体では価値ではない。

第1部: AI が作りがちな失敗4パターン(書く前に問う)

原則 1: 依存・引数の凝集 — 5個は分割のシグナル

コンストラクタ引数・メソッド引数・オプションオブジェクトの項目が 5個以上になったら、追加する前に立ち止まる。多引数は「このクラスが複数の責務を抱えている」か「引数の一部が概念としてまとまりたがっている」かのどちらかのシグナル。

対処は追加ではなく再編:

  • 引数の一部が常にセットで使われる → その組に名前を付けて値オブジェクトに束ねる(実例: 10個のスコア要素は個別の number として持ち回さず ScoreComponents(src/domain/valueObjects/ScoreComponents.ts)に束ねてある)
  • 組み立てに必要な材料が多い → ビルダーで段階的に組む(実例: Horse.builder(id, name).withDetail().withRaceResults().withCourseStats().withTrackStats().build()。src/domain/entities/Horse.ts)
  • 依存の一部が特定メソッドでしか使われない → そのメソッド群ごと協力クラスに切り出し、依存を分配する

安易な逃げ(オブジェクト1個に全部詰めて実質同じ・any 化)は不可。「数を減らす」のではなく「凝集を上げる」のが目的。

原則 2: ロジックの置き場所 — commands はオーケストレーションのみ

commands 層(CLI コマンド)が肥大し domain が貧血症になるのは、競馬ドメインの語彙で説明できる判定・計算・変換をコマンドの中に直書きするから。本リポジトリはリッチドメインモデルを採る(docs/ARCHITECTURE.md レイヤー構成)。

  • その if / 計算 / 変換をドメインの言葉で一文で言えるか(「直近5戦の着順から直近成績スコアを出す」「騎手のコース別勝率から騎手スコアを出す」)→ 言えるなら domain に置く。実例: スコア計算は Horse / Jockey / Trainer エンティティが内包し、ScoringOrchestrator は取得・組み立て・委譲だけを行う薄い層
  • commands に残してよいのは段取りだけ: リポジトリ/オーケストレータを呼ぶ・結果を保存する・表示する。コマンド本体は名前付きステップの平坦な列挙になる
  • domain の型が フィールドしか持たず判定を持たないなら貧血症の兆候。そのデータへの判定・導出は近くのコマンドに散らばっているはずなので、該当エンティティ/値オブジェクトに引き上げる
  • 定数・重みの唯一の定義箇所を作る。実例: 10要素の重みは src/constants/ScoringConstants.ts の SCORE_WEIGHTS が唯一の正で、スコアリング(ScoreComponents.calculateTotalScore())も ML(MLFeatures extends ScoreComponentsData)も同じ定義を参照する。重みや閾値をコマンドやモデル側に再掲・再定義しない

判断に迷う場合の目安: そのロジックのユニットテストを書くとき DB(テスト用 SQLite)が1つも要らないなら domain 行き、リポジトリ呼び出しの順序や永続化が主題なら commands / services 行き。

原則 3: ポリモーフィズムの判断タイミング — 2箇所目の種別分岐を書く前

種別(馬場状態 良/稍重/重/不良、レースクラス、会場、距離カテゴリなど)による分岐を2箇所目に書こうとした瞬間が判断のタイミング。書いてからのリファクタリングではなく、書く前に問う:

  • この軸は今後増えるか(会場・レースクラスのように増える)→ 種別ごとの実装 + レジストリにする
  • レジストリは Map でなく **union の Record({ [K in Kind]: Impl })**で持ち、種別追加時の登録漏れをコンパイラに検出させる
  • 増えない・値域が閉じている(馬場4値のように仕様で固定)→ ポリモーフィズムは過剰。素直な if / exhaustive switch でよい
  • 分岐が1箇所に閉じている exhaustive switch も許容。問題は同じ種別分岐が複数ファイル・複数関数に散った状態(散らばりの気配が出たら定数表・種別実装に寄せる)
  • 分岐の閾値・係数は必ず src/constants/ に出す(マジックナンバーの散在はスコア再現性を壊す)

原則 4: トリッキーな実装の禁止 — 退屈で明示的に書く

「短い・巧い・一行で済む」は価値ではない。初見の読み手が10秒で意図を取れない書き方は、動いていても書き直す(驚き最小の原則)。

  • 型ジャグリング(型を通すためだけのチェーン・条件付き型の曲芸・as での黙らせ)をしない。型が素直に付かないのは設計側の歪みのシグナル。any は使わない(docs/DEVELOPMENT.md コーディング規約)
  • 偶然の一致による再利用をしない(「たまたま同じ形」の2つの概念を1つの関数・型で兼用しない)
  • インデックス演算・ビット演算・正規表現の技巧で「賢く」書かない。for-of + push、名前付き述語、素朴な分解で書く。HTML 抽出(src/utils/HorseDataExtractor.ts)のように正規表現が本質的に必要な箇所は、意図をコメントで残す
  • 統計・ML の式(L2正則化ロジスティック回帰・標準化・softmax・較正)は式そのものが技巧に見えるので、何を計算しているかと出典・根拠をコメントで残す

第2部: 理解容易性の7観点(実装後セルフチェック)

実装を終えたタイミングで、識別子(変数・関数の名前)と区画(関数・クラスのまとまり)を以下の7問で確認する。

#観点問い本リポジトリでの具体ルール
1名称分かりやすいか?(曖昧→明瞭)命名規約は docs/DEVELOPMENT.md(コマンド=動詞ベース PascalCase、エンティティ=名詞、リポジトリ=<対象><Query|Aggregate>Repository)。マジックナンバーは src/constants/ へ。名前と内容の一致(驚き最小)
2役割複数ないか?(複数→単一)参照系は queries/、更新系は aggregates/ に分ける(両者を1クラスに混ぜない)。取得と計算と表示を1メソッドに混ぜない
3参照広くないか?(広域→局所)スコープは最小に。Database インスタンスは受け取って使う(グローバル接続を関数内で開き直さない)。重み・閾値は SCORE_WEIGHTS 等の単一定義を参照する
4状態変えられなくできるか?(可変→不変)readonly / as const を既定にする(SCORE_WEIGHTS は as const)。エンティティ・値オブジェクトは生成後に書き換えない
5面積大きくないか?(広大→狭小)1ファイル 400 行・1関数 80 行・認知的複雑度 15 が lint の警告線(bun run lint)。既に超えているファイル(MachineLearningModel.ts / Horse.ts / Backtest.ts 等)を追記で太らせない。死にコード・コメントアウト旧コードは消す
6階層深くないか?(多層→単層)ガード節・早期 return を優先(if (!raceRecord) throw のように先に落とす)。ネストが深い処理は関数抽出。コマンド本体は平坦な列挙にする
7秩序整っているか?(雑然→整然)JSDoc で目的・引数・戻り値を書く(既存コードの @remarks / @example の書き方に揃える)。同じ意図の処理は対称に書く。層をまたがせない(commands から SQL を直接書かない)

7観点の物差しはすべて「読み手の思考量を増やすか?」。迷ったら、その書き方が読み手に確認範囲の拡大(名称・参照・状態・面積)か複雑性への対処(役割・階層・秩序)を強いるかを考える。

クエリ層の約束(DB を触るとき)

  • Kysely はリポジトリ層(src/repositories)と DB 層(src/database)だけで使う。組み立てたクエリは .compile() して src/database/QueryRunner.ts の selectRows / selectRow / runStatement で実行する(ビルダーの .execute() 系は DummyDriver のため空の結果が返る)
  • 実行は bun:sqlite の同期 API のまま。トランザクションは DatabaseConnection.runInTransaction。非同期化してこの性質を壊さない

ドメイン固有の不変条件(スコアリング・ML を触るとき)

  • SCORE_WEIGHTS の合計は 1.0。要素の追加・重みの変更は合計を必ず再確認し、docs/ARCHITECTURE.md / docs/MODELS.md / help スキルの配分表も追随させる
  • スコアリングと ML は同じ10要素を使う(特徴量の統一)。この 10 要素は SCORE_WEIGHTS / ScoreComponentsData / FeatureBuilder の FEATURE_SPECS / MachineLearningModel の複数の手書きリスト / CalculateScore / Backtest に独立して並んでいるので、片方だけに要素を足さない(全箇所は review-scoring 3.2)
  • バックテスト・訓練データは未来情報を使わない。取得メソッドには beforeDate / asOf を通し、集計テーブル(horse_course_stats / horse_track_stats)ではなく as-of 集計版を使う。beforeDate は省略可能な引数なので、渡し忘れても型エラーにならない点に注意する
  • ML の学習・予測は決定論的(L2正則化ロジスティック回帰。本番コードに乱数は無い)。同じ入力なら同じ結果が出る性質を壊す変更をしない。乱数を導入するならシードを固定し、テストから再現できる形にする

これらの機械的検出は review-scoring の守備範囲。本スキルは「書く前に壊さない」ための注意書き。

実装前チェック(4問)

  1. これから作るクラス・関数の引数・依存は5個未満か。超えるなら何を束ね・何を分けるか決めたか
  2. 書こうとしている判定・計算は競馬ドメインの言葉で言えるか。言えるなら domain(エンティティ/値オブジェクト)に置く場所を決めたか
  3. 種別での分岐を書こうとしていないか。この軸は増えるか。閾値・係数を src/constants/ に出したか
  4. いま思いついた書き方は「巧い」から選んでいないか。より退屈な書き方があるならそちらを選んだか

責務境界

  • レビュー時の機械的検出: 層・配置は review-arch、制御フロー・スタイルは review-code、命名は review-naming、テストは review-test、スコアリング/ML のドメイン正しさは review-scoring。層越え import・純粋性・接続生成箇所・.skip/.only・eval 系は Biome が error で落とす(一覧は review-code 2.1)
  • コードスタイルの正は review-code と docs/DEVELOPMENT.md。本スキルは「書く前・書いた直後の判断」に限定し、チェックリスト・判定基準(OK/WARN/FAIL)を持たない

Reviews

No reviews yet. Tell others what you thought.

Comments

No login needed. Share how it worked for you, ask a question, or note where it did not run.

No comments yet. Be the first to write one.

Comments are published right away. Do not include personal information such as email addresses or phone numbers. Up to 2 URLs.

0 / 1,000 characters

This form is protected by reCAPTCHA, and the Google Privacy Policy and Terms of Service apply.

More skills from this repository

会場別のコース適性を分析する。会場を省略すると全会場の成績を表示する。「コース分析」「会場別の適性を見たい」といった依頼で発動する。

The English summary is being prepared. Showing the original description.

sogengineer/arima-analy4Updated Sep 21, 2026

抽出済みJSONファイル(馬・血統・レース出走データ)をSQLiteデータベースにインポートする。「DBインポート」「データベースに保存」「JSONをDBに登録」といった依頼で発動する。

The English summary is being prepared. Showing the original description.

sogengineer/arima-analy4Updated Sep 21, 2026

agent 向けテキスト指示(skill / slash command / task プロンプト / コード生成プロンプト)を、バイアスを排した実行者に動かしてもらい、両面(実行者の自己申告 + 指示側メトリクス)で評価して反復改善する手法。改善が頭打ちになるまで回す。Use when user says 'プロンプトを改善して', 'スキルをチューニング', 'empirical-prompt-tuning', or after creating/heavily revising a skill or prompt.

The English summary is being prepared. Showing the original description.

sogengineer/arima-analy4Updated Sep 21, 2026

JRA公式サイトから特定レースの出馬表HTMLを取得して馬データを抽出する。「データ取得」「JRAからデータを取ってきて」「出馬表を取得して」といった依頼で発動する。

The English summary is being prepared. Showing the original description.

sogengineer/arima-analy4Updated Sep 21, 2026

help

Free

有馬記念分析システムで利用可能なスキル一覧と使い方、スコア配分、基本ワークフローを表示する。「ヘルプ」「使い方を教えて」「何ができるの」といった依頼で発動する。

The English summary is being prepared. Showing the original description.

sogengineer/arima-analy4Updated Sep 21, 2026

登録済みの競走馬を血統情報(父・母・母父)、調教師、馬主付きで一覧表示する。「馬一覧」「登録されている馬を見せて」といった依頼で発動する。

The English summary is being prepared. Showing the original description.

sogengineer/arima-analy4Updated Sep 21, 2026

See all skills by sogengineer

Report a problem with this skill