audit-code-quality について
React と TypeScript のコード品質を監査し、問題を限定的に修正するスキルです。アンチパターン、命名、ファイル構成、一貫性を確認し、リポジトリの検証手順で変更を確かめます。
主な機能
- 01React と TypeScript のアンチパターン検出
- 02命名とファイル構成の確認
- 03パターンの一貫性の点検
- 04対象を絞った修正
- 05リポジトリの型チェック・Lint・テスト実行
活用シーン
- 01React と TypeScript のコードベース監査
- 02特定した設計や状態管理の問題修正
- 03変更のマージ前の規約確認
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ kensaurus/ skills --skill audit-code-qualityインストール後、Claude Code で/audit-code-qualityを入力して読み込まれたか確認します。
詳細
コード品質監査
自由度:混在 — パート1~2の判断は[自由度:高]、慣例の grep と検証は[自由度:低 — 正確に実行]です。
監査と修正の例外。 検出した後に修正します。報告して終了ではありません。
報告した問題は、指定したファイル内で修正してください。レポートに記載されていないリポジトリ全体のスタイル変更や名前変更は、別のセッションで行います。ファイル全体を書き換えるのではなく、問題のあるフック、キー、インポートなどを対象に、必要最小限の編集を行ってください。作業中に遭遇した既存のバグは、フォローアップとしてレポートに記載します。タスクで求められている場合、またはリポジトリですでに対象の箇所にテストが用意されている場合に限り、テストを追加または変更してください。単発の grep や一時的なスクリプトはコミットに含めないでください。
リポジトリ全体の React/TypeScript のアンチパターンに加え、命名、構成、パターンの一貫性を監査します。指定された PR/差分 → audit-code-review
推論の進め方
- 確認 — 問題箇所(
file:line)と、git blame/CONTRIBUTING に意図的なものと記されているかを引用します - 解釈 — バグ/パフォーマンスのリスクか、スタイルのみの逸脱かを判断します
- 分類 — アンチパターン/命名/構成/パターンの不一致/意図的なもの
- 重大度 — 実行時バグまたはデータ取得の
useEffectは高、命名のみは低
実例
確認:
components/PaymentForm.tsxに'use server'があり、請求書を取得するuseEffectがあります。並べ替え可能な4つのリストではkey={i}が使われています。 解釈: サーバーアクションが UI に置かれています。再マウント時に取得が再実行されます。並べ替え時に行が再マウントされます。 分類: 構成 + データ取得にuseEffectを使用 + インデックスをキーに使用。 重大度: 高(コンポーネント内のアクション)、中(キー)。 指摘事項:PaymentForm.tsx| 高 | アクションをfeatures/*/server/に移動し、 取得用のエフェクトを TanStack Query に置き換えます
変更前に必ず実施すること [自由度:低 — 正確に実行]
cat CONTRIBUTING.md .cursor/rules/*.md 2>/dev/null | head -100 # existing conventions
git log --oneline -20 | grep -i "convention\|pattern\|style" # recent decisions
「不整合」が意図的なものではないことを確認してください。既存のコードに手を加える前にgit blameを確認してください。
パート1 — アンチパターン [自由度:高]
React
Props のバケツリレー → Context またはコンポジション
// Bad: prop drilled through 5+ levels
<Layout user={user}><Sidebar user={user}><UserMenu user={user}><Avatar user={user} />
// Fix: context for global state
const UserContext = createContext<User | null>(null)
const useUser = () => useContext(UserContext)
// Fix: composition for UI concerns
<Layout><Layout.Sidebar><UserMenu /></Layout.Sidebar></Layout>
useStateで派生状態を管理 → useMemo
// Bad
const [items, setItems] = useState([])
const [filtered, setFiltered] = useState([])
useEffect(() => { setFiltered(items.filter(i => i.active)) }, [items])
// Fix
const filtered = useMemo(() => items.filter(i => i.active), [items])
データ取得にuseEffectを使用 → Server Components または TanStack Query
// Fix (Next.js 16 Server Component)
async function DataDisplay() {
const data = await db.getData()
return <div>{data.name}</div>
}
// Fix (client with TanStack Query)
const { data, isLoading } = useQuery({ queryKey: ['data'], queryFn: fetchData })
依存配列にオブジェクト/配列を指定 → メモ化または分割代入
// Bad: new object every render → infinite loop
useEffect(() => { doSomething(options) }, [options])
// Fix
const options = useMemo(() => ({ page, limit }), [page, limit])
useEffect(() => { doSomething(options) }, [options])
動的リストのキーにインデックスを使用 → 安定した ID
// Bad
{items.map((item, i) => <Item key={i} />)}
// Fix
{items.map(item => <Item key={item.id} />)}
巨大なコンポーネント → コンポジション
// Split by responsibility
export function UserDashboard() {
const user = useUser()
return (
<DashboardLayout>
<UserHeader user={user} />
<UserStats userId={user.id} />
<RecentActivity userId={user.id} />
</DashboardLayout>
)
}
TypeScript
// Bad: any
function processData(data: any) { return data.items.map((i: any) => i.name) }
// Fix: proper types + Zod validation
const DataSchema = z.object({ items: z.array(z.object({ id: z.string(), name: z.string() })) })
function processData(data: z.infer<typeof DataSchema>) { return data.items.map(i => i.name) }
// Bad: enums (runtime object, not tree-shakeable)
enum Status { Pending = 'pending', Active = 'active' }
// Fix: union types
type Status = 'pending' | 'active'
// Fix: exhaustive switch
function handle(s: Status): string {
switch (s) {
case 'pending': return 'Waiting'
case 'active': return 'Running'
default:
const _: never = s
throw new Error(`Unknown: ${_}`)
}
}
状態管理
// Rule of thumb:
// URL state (nuqs) → shareable, bookmarkable
// Global state (Zustand) → cross-component, persisted
// Local state (useState) → component-specific, ephemeral
// Stale closure fix
useEffect(() => {
const id = setInterval(() => setCount(c => c + 1), 1000) // functional update
return () => clearInterval(id)
}, [])
アーキテクチャ
// Circular deps → extract shared code / dependency inversion
// Business logic in components → move to lib/feature/calculations.ts
// God files → feature-based organisation:
lib/
date/format.ts
currency/format.ts
validation/schemas.ts
パート2 — 一貫性の監査 [自由度:高、grep は低]
命名規則
| 要素 | 期待される形式 | 確認 |
|---|---|---|
| コンポーネント | PascalCase | userCard と UserCard |
| フック | use 接頭辞 | fetchData と useFetchData |
| ユーティリティ | camelCase | format_date と formatDate |
| 定数 | SCREAMING_SNAKE | apiUrl と API_URL |
| ファイル | kebab-case | UserCard.tsx と user-card.tsx |
| ブール値の props | is/has/should | loading と isLoading |
rg "export (function|const|class) [a-z]" --type tsx # lowercase component exports
rg "use[A-Z]" --type ts # hook patterns
rg "export default" --type tsx -l | head -20 # mixed default/named exports
ファイル構成(機能スライス型)
src/
features/{name}/
components/ # UI only
hooks/ # custom hooks
server/ # server actions
types.ts
schemas.ts
components/ui/ # shared primitives
lib/ # global utilities
注意すべき点:
- コンポーネントが
/libまたは/utilsにある - サーバーアクションがコンポーネントファイルにある
- 型がコンポーネントファイルに散在している
- Zod スキーマがコンポーネント内にインラインで記述されている
rg "z\.object" --glob "*/components/*" # inline schemas
rg "'use server'" --glob "*/components/*" # actions in wrong place
rg "from '\.\." --type tsx | head -20 # relative imports instead of @/
パターンの一貫性
| 対象 | 確認 |
|---|---|
| サーバー状態 | TanStack Query に統一されていますか、それともuseEffectと混在していますか? |
| フォーム | React Hook Form に統一されていますか、それとも制御コンポーネントの入力も使われていますか? |
| エラー処理 | ActionResult の形式は一貫していますか? |
| スタイル | すべての箇所で cn() を使っていますか?ダークモードは CSS 変数を使っていますか? |
| テスト | *.test.ts と __tests__/ のどちらを使っていますか?モックの方法は一貫していますか? |
整合性レポートのテンプレート
# Code Quality Audit
## Summary
- Verdict: [one line — what blocks, what is drift]
- Critical findings: X
## Anti-patterns found
| Pattern | Files | Severity |
|---------|-------|----------|
| useEffect for data | 3 files | High |
| Index as key | 2 files | Medium |
## Naming convention findings
| Element | Expected | Actual | Files |
|---------|----------|--------|-------|
| Components | PascalCase | Mixed | 4 |
## Organisation findings
- Server actions found in: components/PaymentForm.tsx
- Types scattered in: 6 component files
## Priority fixes
1. Move server actions to features/*/server/
2. Replace useEffect data fetching with TanStack Query
3. Standardise component naming to PascalCase
## Conventions to document
1. Decision on default vs named exports
2. Where Zod schemas live
アンチパターン検出チェックリスト
React の確認項目
- 派生状態に
useEffectを使用していない - 動的リストでインデックスをキーに使用していない
- 依存配列にオブジェクト/配列を指定していない
- コンポーネントが300行未満である
- 2階層を超える props のバケツリレーがない
TypeScript の確認項目
-
anyを使用していない(unknownと検証を使用) - 安全でない型アサーションを使用していない
-
switch文で網羅性を確保している - 外部データすべてに Zod スキーマを使用している
アーキテクチャの確認
- 循環依存がない
- ビジネスロジックが UI から分離されている
- ファイルが 400 行未満である
- モジュールの境界が明確である
修正を適用する前の自己批評 [LOW freedom — do not skip]
- 根拠がある — 「コードベースが雑然としているように感じる」ではなく、
file:lineまたは rg のヒットを示す - 再現可能である — 規約を検索する grep で引き続き検出される
- 重大度が妥当である — High はバグ/パフォーマンスの問題を指し、命名の好みではない
- 担当が適切である — この PR/diff →
audit-code-review - 誤った安全性を主張しない —
git blameを確認し、意図的に動作しているコードには手を加えない
検証 [LOW freedom — run exactly]
- リポジトリ独自のスクリプトで型チェックする(
package.jsonのtypecheck。なければtsc --noEmit) - リポジトリ独自のスクリプトで lint を実行する。汎用的な
eslint src/は使わない - リポジトリのテストスクリプトを実行し、新たな失敗がないことを確認する
- 強制する標準を
CONTRIBUTING.mdまたは.cursor/rules/に記載する