本文へ移動
cccskills
無料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.

インストール方法を見る

含まれるファイル(1)

  • SKILL.md12.8 KB

SKILL.md(原文)

インストールする前に、エージェントに与えられる指示の中身を確認できます。

位置づけ

本スキルはコードを書く前と書いた直後に読む設計原則。レビュー時の機械的検出は 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)を持たない

レビュー

まだレビューはありません。使ってみた感想をお寄せください。

同じリポジトリのスキル

概要と使いどころ

course-analysis

無料日本語概要

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

sogengineer/arima-analy42026年9月21日 更新

db-import

無料日本語概要

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

sogengineer/arima-analy42026年9月21日 更新

empirical-prompt-tuning

無料日本語概要

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

sogengineer/arima-analy42026年9月21日 更新

fetch-data

無料日本語概要

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

sogengineer/arima-analy42026年9月21日 更新

help

無料日本語概要

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

sogengineer/arima-analy42026年9月21日 更新

horse-list

無料日本語概要

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

sogengineer/arima-analy42026年9月21日 更新

sogengineer のスキルをすべて見る

このスキルの問題を報告する