← スキル一覧に戻る
Booleans: Prefix with
Never use

complexity-check
by marcusta
⭐ 0🍴 0📅 2026年1月25日
SKILL.md
name: complexity-check description: Enforce code complexity limits and quality standards in TapScore backend. Use when writing or reviewing backend code to ensure method size limits, nesting depth, naming conventions, and use of domain constants are followed.
TapScore Code Complexity Check Skill
This skill enforces code complexity limits from "Code Complete" principles. Use when writing or reviewing ANY backend code.
Complexity Check Workflow
Copy this checklist and track your progress:
Code Complexity Checklist:
- [ ] Step 1: Check method size limits
- [ ] Step 2: Verify nesting depth (max 3 levels)
- [ ] Step 3: Validate naming conventions
- [ ] Step 4: Replace magic numbers with constants
- [ ] Step 5: Ensure type safety
Step 1: Check Method Size Limits
Rules:
- Logic methods: Max 50 lines (excluding blanks/comments)
- Public API methods: Max 50 lines
- Query methods: No strict limit (SQL can be long)
When too long: Extract helper methods
// ❌ TOO LONG - 80 lines of logic
calculateLeaderboard(participants: Participant[]): LeaderboardEntry[] {
// Complex logic all in one method
}
// ✅ FIX - Extract focused methods
calculateLeaderboard(participants: Participant[]): LeaderboardEntry[] {
const entries = participants.map(p => this.calculateEntry(p, pars));
const sorted = this.sortByScore(entries);
return this.assignRanks(sorted);
}
private calculateEntry(p: Participant, pars: number[]): LeaderboardEntry {
// 15 lines - focused
}
private sortByScore(entries: LeaderboardEntry[]): LeaderboardEntry[] {
// 10 lines - single purpose
}
Step 2: Verify Nesting Depth (Max 3 Levels)
CRITICAL: Maximum 3 levels of nesting. Use early returns.
// ❌ TOO DEEP - 4 levels
for (const participant of participants) { // Level 1
if (participant.player_id) { // Level 2
for (const score of scores) { // Level 3
if (score > 0) { // Level 4 - TOO DEEP!
// ...
}
}
}
}
// ✅ FLATTENED - Max 2 levels
for (const participant of participants) { // Level 1
if (!participant.player_id) continue; // Early exit
for (const score of scores) { // Level 2
if (score <= 0) continue; // Early exit
this.processValidScore(score); // At level 2
}
}
Flattening techniques:
- Early returns:
if (!condition) return; - Early continue:
if (!valid) continue; - Guard clauses at method start
- Extract methods for nested blocks
Step 3: Validate Naming Conventions
Booleans: Prefix with is, has, should, can
// ✅ CORRECT
const isFinished = competition.is_finished;
const hasScores = scores.length > 0;
const shouldCalculateNet = participant.handicap_index !== null;
// ❌ WRONG
const finished = competition.is_finished; // Unclear type
const calculateNet = participant.handicap_index !== null; // Sounds like function
Collections: Use plural nouns
// ✅ CORRECT
const participants: Participant[] = [];
const scores: number[] = [];
// ❌ WRONG
const participant: Participant[] = []; // Singular
const scoreList: number[] = []; // Redundant "List"
Transformed Data: Name describes transformation
// ✅ CORRECT
const competitionWithCourse = { ...competition, course };
const sortedLeaderboard = leaderboard.sort(...);
// ❌ WRONG
const competition = { ...competition, course }; // Name collision
Avoid Generic Names
NEVER use: data, result, item, temp, info, obj
Use descriptive domain names instead.
Step 4: Replace Magic Numbers with Constants
Import and use GOLF constants
import { GOLF } from "../constants/golf";
// ✅ CORRECT - Uses constants
if (holesPlayed === GOLF.HOLES_PER_ROUND) {
const rating = tee.course_rating || GOLF.STANDARD_COURSE_RATING;
const slope = tee.slope_rating || GOLF.STANDARD_SLOPE_RATING;
}
if (par < GOLF.MIN_PAR || par > GOLF.MAX_PAR) {
throw new Error(`Invalid par: ${par}`);
}
if (score === GOLF.UNREPORTED_HOLE) return null;
// ❌ WRONG - Magic numbers
if (holesPlayed === 18) { // What's 18?
const rating = tee.course_rating || 72; // What's 72?
const slope = tee.slope_rating || 113; // What's 113?
}
if (par < 3 || par > 6) { // Why 3 and 6?
throw new Error(`Invalid par: ${par}`);
}
if (score === -1) { // What's -1?
return null;
}
Available GOLF Constants
See src/constants/golf.ts for complete list:
export const GOLF = {
HOLES_PER_ROUND: 18,
FRONT_NINE_START: 1,
BACK_NINE_START: 10,
STANDARD_SLOPE_RATING: 113,
STANDARD_COURSE_RATING: 72,
MIN_PAR: 3,
MAX_PAR: 6,
MIN_COURSE_RATING: 50,
MAX_COURSE_RATING: 90,
MIN_SLOPE_RATING: 55,
MAX_SLOPE_RATING: 155,
MIN_HANDICAP_INDEX: -10,
MAX_HANDICAP_INDEX: 54,
UNREPORTED_HOLE: -1,
} as const;
If new constant needed: Add to src/constants/golf.ts
Step 5: Ensure Type Safety
Never use any
// ❌ WRONG
getCompetitions(tourId: number): any[] {
return this.db.prepare("...").all(tourId);
}
// ✅ CORRECT
getCompetitions(tourId: number): Competition[] {
return this.db.prepare("...").all(tourId) as Competition[];
}
Explicit return types on all methods
// ❌ WRONG - Inferred
calculateRelativeToPar(scores: number[], pars: number[]) {
return scores.reduce(...);
}
// ✅ CORRECT - Explicit
calculateRelativeToPar(scores: number[], pars: number[]): number {
return scores.reduce(...);
}
Type guards before assertions
// ❌ WRONG
const competition = stmt.get(id) as Competition;
// ✅ BETTER
const row = stmt.get(id);
if (!row) return null;
if (!this.isValidCompetition(row)) {
throw new Error("Invalid competition data");
}
return row as Competition;
Validation Checklist
Before merging code:
Method Size
- Logic methods under 50 lines
- Public API methods under 50 lines
- Extracted helpers for complex logic
Nesting Depth
- Maximum 3 levels of nesting
- Used early returns/continue
- Extracted methods to reduce nesting
Naming Conventions
- Booleans:
is*,has*,should*,can* - Collections: plural nouns
- Transformed data: descriptive names
- NO generic names:
data,result,temp
Constants
- Used
GOLFconstants for domain values - NO magic numbers (18, 113, 72, -1, etc.)
- Imported from
src/constants/golf
Type Safety
- NO
anytype - Explicit return types on all methods
- Type guards before assertions
- Proper query result casting
Example: Well-Structured Code
import { GOLF } from "../constants/golf";
class LeaderboardService {
// Under 50 lines, max 2 levels, good names, uses constants
calculateLeaderboard(competitionId: number): LeaderboardEntry[] {
const competition = this.findCompetitionById(competitionId);
if (!competition) return [];
const participants = this.findParticipantsByCompetition(competitionId);
if (participants.length === 0) return [];
const pars = this.parseParsArray(competition.pars);
const entries = participants
.map(p => this.calculateEntry(p, pars))
.filter(e => e.totalScore > 0);
return this.sortByScore(entries);
}
private calculateEntry(participant: Participant, pars: number[]): LeaderboardEntry {
const scores = JSON.parse(participant.score);
const holesPlayed = scores.filter(s => s > 0).length;
if (holesPlayed !== GOLF.HOLES_PER_ROUND) return null;
const totalScore = scores.reduce((sum, s) => sum + s, 0);
const relativeToPar = scores.reduce((rel, shots, i) => {
if (shots === GOLF.UNREPORTED_HOLE) return rel;
return rel + (shots - pars[i]);
}, 0);
return { participantId: participant.id, totalScore, relativeToPar };
}
}
Summary
Every code change must:
- Keep methods under 50 lines (except query methods)
- Maintain max 3 levels of nesting
- Use convention-following names
- Replace magic numbers with GOLF constants
- Avoid
anyand use explicit types
For detailed patterns, see docs/backend/BACKEND_GUIDE.md.
スコア
総合スコア
50/100
リポジトリの品質指標に基づく評価
✓SKILL.md
SKILL.mdファイルが含まれている
+20
○LICENSE
ライセンスが設定されている
0/10
○説明文
100文字以上の説明がある
0/10
○人気
GitHub Stars 100以上
0/15
○最近の活動
3ヶ月以内に更新がある
0/10
○フォーク
10回以上フォークされている
0/5
✓Issue管理
オープンIssueが50未満
+5
✓言語
プログラミング言語が設定されている
+5
○タグ
1つ以上のタグが設定されている
0/5
レビュー
💬
レビュー機能は近日公開予定です