| name | code-conventions |
| description | Defines project-wide code conventions for magic number elimination, constant usage, performance optimization, and coordinate system separation. Use when implementing, testing, or reviewing code to ensure consistency across the codebase. |
| allowed-tools | Read |
コード規約スキル
このスキルは、プロジェクトの共通コード規約を定義し、エージェント定義ファイルから参照されます。
基本原則
1. マジックナンバーの排除
原則: ドメイン知識としてみなせる数値は、必ず定数として定義し、コード中での直接記述を避ける。
定義例:
// ❌ マジックナンバー(避けるべき)
const expectedInnerRadius = mockTransform.physicalDistanceToScreen(7.95);
const expectedOuterRadius = mockTransform.physicalDistanceToScreen(225);
// ✅ 定数インポート(推奨)
import { BOARD_PHYSICAL } from '../../utils/constants';
const expectedInnerRadius = mockTransform.physicalDistanceToScreen(BOARD_PHYSICAL.rings.outerBull);
const expectedOuterRadius = mockTransform.physicalDistanceToScreen(BOARD_PHYSICAL.rings.boardEdge);
ドメイン知識として扱う数値の例:
- ダーツボード物理サイズ(mm): 3.175, 7.95, 99, 107, 162, 170, 225など
- セグメント角度: π/10(18度)
- セグメント番号: 1-20
- 有効な得点: 0-60点の特定値
- ゲーム設定: 501, 701, 301点
2. 定数参照の原則
実装ファイルでの定義:
// src/utils/constants.ts
export const BOARD_PHYSICAL = {
rings: {
innerBull: 3.175, // mm
outerBull: 7.95, // mm
tripleInner: 99, // mm
tripleOuter: 107, // mm
doubleInner: 162, // mm
doubleOuter: 170, // mm
boardEdge: 225 // mm
}
} as const;
テストファイルでの使用:
// src/__tests__/integration/dartboard-rendering.test.ts
import { BOARD_PHYSICAL } from '../../utils/constants';
test('期待される半径でテストする', () => {
const expectedInnerRadius = mockTransform.physicalDistanceToScreen(BOARD_PHYSICAL.rings.outerBull);
// ...
});
メリット:
- 定数値の変更時にテストが自動的に追従する
- コメントで値の意味を説明する必要がなくなる
- タイポや値の誤りを防げる
- ドメイン知識の一元管理が実現される
3. パフォーマンス最適化
原則: 描画関数などで繰り返し使われる共通処理は、ループ外に抽出する。
例:
// ❌ 非効率(20回呼び出し)
SEGMENTS.forEach((_, index) => {
p5.fill(fillColor);
p5.noStroke(); // ← 毎回呼び出し
// ...
});
// ✅ 効率的(1回呼び出し)
p5.noStroke(); // ← ループ外で一度だけ
SEGMENTS.forEach((_, index) => {
p5.fill(fillColor);
// ...
});
4. 座標系の分離
原則: 物理座標(mm)と画面座標(pixel)を厳密に分離する。
実装例:
// ✅ 物理座標で計算してから画面座標に変換
const innerRadius = transform.physicalDistanceToScreen(BOARD_PHYSICAL.rings.outerBull);
const outerRadius = transform.physicalDistanceToScreen(BOARD_PHYSICAL.rings.boardEdge);
// ❌ 避けるべき:物理座標と画面座標の混在
const radius = 225 * scale; // マジックナンバー + スケール計算の混在
5. コメント規約
物理座標定数のコメント例:
export const BOARD_PHYSICAL = {
rings: {
innerBull: 3.175, // mm: インナーブル半径(50点エリア)
outerBull: 7.95, // mm: アウターブル半径(25点エリア)
tripleInner: 99, // mm: トリプルリング内側
tripleOuter: 107, // mm: トリプルリング外側
// ...
}
} as const;
テストコメント例:
// 期待される半径(画面座標)
// BOARD_PHYSICAL.rings.outerBullを画面座標に変換
const expectedInnerRadius = mockTransform.physicalDistanceToScreen(BOARD_PHYSICAL.rings.outerBull);
6. 型安全性の原則
原則: 型アサーション(as)は可能な限り避け、型ガードを使用する。
型アサーションは避ける(非推奨):
// ❌ 型アサーション(避けるべき)
const typedPersistedState = persistedState as { config?: Partial<PracticeConfig> };
if (typedPersistedState.config) {
// 実行時エラーのリスクがある
}
型ガードを使用(推奨):
// ✅ 型ガード(推奨)
if (
typeof persistedState === 'object' &&
persistedState !== null &&
'config' in persistedState
) {
const config = persistedState.config;
if (config && typeof config === 'object') {
// 型安全に処理できる
}
}
ヘルパー関数で型ガードを抽出(より推奨):
// ✅ 再利用可能な型ガード
const isPersistFormat = (
data: unknown
): data is { state: { config: unknown }; version: number } => {
return (
data !== null &&
typeof data === 'object' &&
'state' in data &&
data.state !== null &&
typeof data.state === 'object' &&
'config' in data.state
);
};
// 使用例
if (isPersistFormat(parsed)) {
return parsed; // 型安全
}
メリット:
- 実行時のデータ構造を正確にチェックできる
- 型アサーションによる誤った型推論を防げる
- リファクタリング時の安全性が向上する
- コードの意図が明確になる
7. マジック文字列の定数化
原則: 繰り返し使用される文字列リテラルは定数として定義する。
定数化すべき文字列の例:
// ❌ マジック文字列(避けるべき)
const PRESETS = {
'preset-basic': {
configId: 'preset-basic', // 同じ文字列の重複
// ...
}
};
// ✅ 定数化(推奨)
const DEFAULT_PRESET_ID = 'preset-basic' as const;
const PRESETS = {
[DEFAULT_PRESET_ID]: {
configId: DEFAULT_PRESET_ID,
// ...
}
};
メリット:
- タイポによるバグを防げる
- リネーム時の変更箇所が減る
- コードの意図が明確になる
- IDEの補完が効く
8. エラーハンドリングとロギング
原則: サイレントに失敗する場合でも、デバッグ用にログを残す。
ロギングなし(非推奨):
// ❌ エラーを無視(デバッグが困難)
try {
localStorage.setItem(name, JSON.stringify(value));
} catch {
// エラーハンドリング: 保存失敗時は何もしない
}
ロギングあり(推奨):
// ✅ console.warnでエラーを記録(推奨)
try {
localStorage.setItem(name, JSON.stringify(value));
} catch (error) {
// localStorage容量制限やシリアライズエラーを無視
// アプリケーションの動作には影響しないため、サイレントに失敗
console.warn('Failed to persist config to localStorage:', error);
}
メリット:
- 本番環境でのデバッグが容易になる
- エラーの発生頻度を把握できる
- ユーザー体験を損なわずに問題を追跡できる
ガイドライン:
- アプリケーション動作に影響しないエラー →
console.warn - ユーザーに通知すべきエラー → UIで表示 +
console.error - 開発中のみ必要な情報 →
console.log(本番では削除)
9. 重複コードの排除
原則: 同じロジックが複数箇所に存在する場合は、関数として抽出する。
重複あり(非推奨):
// ❌ 重複したデフォルト設定ロジック
const loadInitialConfig = (): PracticeConfig => {
// ...
return { ...PRESETS['preset-basic'] };
};
const initialState = {
config: loadInitialConfig(), // 初期化用関数
};
const getDefaultConfig = (): PracticeConfig => {
return { ...PRESETS['preset-basic'] }; // 同じロジック
};
重複なし(推奨):
// ✅ 単一の関数に統一
const getDefaultConfig = (): PracticeConfig => {
return { ...PRESETS[DEFAULT_PRESET_ID] };
};
const initialState = {
config: getDefaultConfig(), // 同じ関数を再利用
};
メリット:
- 修正時の変更箇所が減る
- ロジックの一貫性が保たれる
- テストが容易になる
10. ヘルパー関数の配置と命名
原則: ヘルパー関数は使用箇所の前に定義し、意図が明確な名前を付ける。
配置の例:
// 1. 定数定義
const DEFAULT_PRESET_ID = 'preset-basic' as const;
const PERSIST_VERSION = 0 as const;
// 2. データ定義(定数を使用する可能性がある)
const PRESETS: Record<string, PracticeConfig> = {
[DEFAULT_PRESET_ID]: { /* ... */ }
};
// 3. ヘルパー関数(データ定義後に配置)
const isPersistFormat = (data: unknown): data is PersistFormat => { /* ... */ };
const getDefaultConfig = (): PracticeConfig => { /* ... */ };
// 4. メインロジック(ヘルパー関数を使用)
export const useGameStore = create<GameStore>()(/* ... */);
命名のガイドライン:
- 型ガード関数:
is〜Format(例:isPersistFormat,isPracticeConfigFormat) - 取得関数:
get〜(例:getDefaultConfig) - 変換関数:
to〜または〜To〜(例:toScreenCoordinate) - チェック関数:
has〜、can〜(例:hasValidConfig)
チェックリスト
実装時の確認項目
- ドメイン知識としてみなせる数値が定数として定義されているか
- テストファイルでも定数をインポートして使用しているか
- ループ外で実行可能な処理がループ内に含まれていないか
- 物理座標と画面座標が混在していないか
- 定数値の変更時にテストが自動的に対応できるか
- 型アサーション(
as)を避け、型ガードを使用しているか - マジック文字列を定数化しているか
- サイレントエラーにログ出力を追加しているか
- 重複コードを関数として抽出しているか
- ヘルパー関数が適切に配置され、明確な名前が付けられているか
レビュー時の確認項目
型安全性:
- 型アサーション(
as)が使用されている場合、型ガードに置き換え可能か? unknown型のデータを適切に型ガードでチェックしているか?
- 型アサーション(
可読性:
- 複雑な型ガードロジックをヘルパー関数として抽出できるか?
- 関数名が意図を明確に表しているか?
保守性:
- マジック文字列/数値が定数化されているか?
- 重複したロジックが存在しないか?
デバッグ性:
- サイレントに失敗するエラーにログ出力があるか?
- エラーメッセージが問題特定に役立つか?
設計品質:
- ヘルパー関数の配置が依存関係を考慮しているか?
- 単一責任の原則に従っているか?