awesome-code-standards について
プロジェクトやモジュールのコーディング規約を統一するためのスキルです。既存の慣習を把握し、それに沿って命名、構造、エラー処理、型、並行処理、ドキュメントを整える支援をします。
主な機能
- 01プロジェクト規約の把握
- 02命名と構造の指針
- 03不変性とエラー処理の実践
- 04型安全性と並行処理の指針
- 05コメントとドキュメントの規約
活用シーン
- 01プロジェクトやモジュールの開始
- 02チーム規約に沿ったリファクタリング
- 03Lint、フォーマット、型チェック規則の設定
- 04オンボーディング用の規約文書化
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ khasky/ awesome-agent-skills --skill awesome-code-standardsインストール後、Claude Code で/awesome-code-standardsを入力して読み込まれたか確認します。
同梱ファイルevals/eval_queries.json
詳細
コーディング規約
一貫した命名、構成、パターンを適用し、チーム全体でコードの読みやすさと保守性を高めます。
適用するタイミング
- 新しいプロジェクトやモジュールを開始するとき
- チームの規約に合わせてリファクタリングするとき
- lint/フォーマット/型チェックのルールを設定または更新するとき
- コードベースで採用する規約を決めるとき(特定の差分のレビューは awesome-code-review の担当です)
- オンボーディング:コーディング規約を文書化または適用するとき
- 命名、フォーマット、構造の一貫性を徹底するとき
基本原則
- 読みやすさを最優先する — コードは書くより読まれることのほうが多いため、気の利いた小技より、わかりやすい名前と構造を優先します。
- KISS — 機能する最もシンプルな解決策を選びます。過剰な設計や早すぎる最適化は避けます。
- DRY — 共通のロジックは関数やモジュールに切り出し、コピー&ペーストを避けます。
- YAGNI — 可能性にすぎない将来のニーズに備えて作り込まず、必要になったときに複雑さを加えます。
- 不変性 —
constを優先し、引数や共有状態の変更を避けます。必要に応じてスプレッド構文やコピーを使います。
作業プロセス(規約を適用する場合)
- プロジェクトの規約を把握する — 既存のコードを調べ、命名規則(camelCase か snake_case か)、ファイル構成、インポート形式、テストのパターンを確認します。CONTRIBUTING、.eslintrc、.prettierrc、editorconfig も確認します。項目ごと(引用符、インデント、命名形式など)に数を数え、最も多い形式を規約とします。少数派は指摘事項とし、同数の場合は判断せずに確認します。
- 違反を特定する — 変更されたコードや新しいコードを、これらの規約および以下のルールと照らし合わせます。
- 具体的な修正を提案する — シンボルの名前変更、関数の切り出し、型の追加、フォーマットの修正を行います。提案ごとに、論理的な編集を1つに絞ります。
- 例外を記録する — プロジェクトに例外(例:「レガシーコードでは any を使う」)がある場合は、背景を考慮せずに修正するのではなく、その例外を記録します。
命名規則
変数と関数
// GOOD: Descriptive, verb-noun for functions
const marketSearchQuery = 'election';
const isUserAuthenticated = true;
async function fetchMarketData(marketId: string) {}
function calculateSimilarity(a: number[], b: number[]) {}
// BAD: Unclear or noun-only for actions
const q = 'election';
const flag = true;
async function market(id: string) {}
function similarity(a, b) {}
定数
- 真の定数には UPPER_SNAKE を使います(例:
MAX_RETRIES、API_BASE_URL)。 - または、プロジェクトの規約に従います(設定オブジェクトに camelCase を使うコードベースもあります)。
型とインターフェース
- PascalCase を使います:
User、OrderItem、ApiResponse<T>。 - 役割がわかりやすくなる場合は、役割を表す接尾辞を付けます:
CreateUserRequest、UserResponse。
ファイル
- コンポーネント:PascalCase(
Button.tsx、UserProfile.tsx)。 - ユーティリティ/フック:camelCase(
formatDate.ts、useAuth.ts)。 - 型:プロジェクトの慣習に応じて、
.typesまたは.dを付けた camelCase(market.types.ts)。
その他の言語
上記の例は TypeScript のものです。ルールは「各言語で公開されている独自の規約に従うこと」であり、その規約はここに示す大文字・小文字の形式とは異なります。ここに示す形式より、各言語のスタイルガイドを優先します。
- Python(PEP 8)— 関数、変数、モジュールには
snake_case、クラスにはPascalCase、定数にはUPPER_SNAKEを使います。先頭の_は非公開を示します。 - Go(Effective Go)—
MixedCapsを使い、アンダースコアは使いません。先頭が大文字の場合、その名前はエクスポート対象です。パッケージ名も名前の一部です。chi.NewChiRouterではなく、chi.NewRouterとします。 - Rust(RFC 430)— 関数、変数、モジュールには
snake_case、型とトレイトにはUpperCamelCase、const と static にはSCREAMING_SNAKE_CASEを使います。 - Java / Kotlin — メソッドとフィールドには
camelCase、型にはPascalCaseを使い、ファイルごとに公開型を1つだけ定義し、その型にちなんだ名前を付けます。 - C# — メソッド、プロパティ、型には
PascalCase、ローカル変数とパラメーターにはcamelCaseを使い、インターフェース名にはIを付けます。
不変性(重要)
// GOOD: Spread and new references
const updatedUser = { ...user, name: 'New Name' };
const updatedArray = [...items, newItem];
// BAD: Direct mutation
user.name = 'New Name';
items.push(newItem);
その他の言語でもルールは同じですが、仕組みが異なります。
- Python — 呼び出し元が引き続き所有する引数を変更してはいけません。また、ミュータブルなデフォルト値(
def f(xs=[])は呼び出し間で同じリストを共有します)を使わず、Noneを受け取り、関数内で生成してください。固定コレクションにはタプルやfrozensetを使い、変更したコピーにはdataclasses.replace(obj, field=…)を使います。 - Go — 保存したスライスは呼び出し元のバッキング配列を保持するため、後から
appendするとその配列に書き込まれる可能性があります。保持する前にコピーしてください(slices.Clone)。変更しないメソッドには値レシーバーを使います。 - Rust — バインディングはデフォルトで不変であり、借用チェッカーによって強制されます。
mutは正当な理由がある場合に使う例外であり、デフォルトで記述するものではありません。 - Java — データを保持する型には
recordを使い、境界では防御的コピーとしてList.copyOf/Map.copyOfを使います。 - C# —
record型とwith式を使います。共有コレクションにはImmutableArray/ImmutableListを使います。
エラー処理
// GOOD: add context, keep the cause; log once at the boundary
async function fetchData(url: string) {
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
return await response.json();
} catch (error) {
throw new Error(`Failed to fetch ${url}`, { cause: error });
}
}
// BAD: No handling or swallowed errors
async function fetchData(url: string) {
const response = await fetch(url);
return response.json();
}
その他の言語でも形式は異なりますが、「コンテキストを追加し、決して握りつぶさない」という原則は同じです。
- Python — 具体的な例外型を送出し、裸の
except:は使わないでください(KeyboardInterruptまで捕捉してしまいます)。例外チェーンを保持して再送出します:raise ParseError(...) from err。 - Go — エラーは値です。すべてのエラーを確認し、
%wを使ってコンテキストを付けてラップし、errors.Is/errors.Asが引き続き機能するようにします(fmt.Errorf("fetch %s: %w", url, err))。_ =でエラーを破棄する場合は、安全な理由をコメントに記載します。 - Rust —
Result<T, E>を返して?で伝播させます。ライブラリの境界では型付きエラー列挙型(またはthiserror)を使い、anyhowはバイナリでのみ使います。ライブラリコード内のunwrap()/expect()は、出荷したパニックです。 - Java — 失敗の種類ごとに例外型を用意し、空の
catchは使わないでください。原因を保持します(throw new X(msg, err))。 - C# — 具体的な例外を捕捉し、裸の
throw;で再送出します(throw ex;はスタックトレースをリセットするため使いません)。
非同期処理と並行処理
// GOOD: Parallel when independent
const [users, markets, stats] = await Promise.all([
fetchUsers(),
fetchMarkets(),
fetchStats(),
]);
// BAD: Sequential when unnecessary
const users = await fetchUsers();
const markets = await fetchMarkets();
const stats = await fetchStats();
その他の言語でも、独立した処理は並行して実行し、並行処理の各単位にキャンセル手段を用意します。
- Python — 独立した await には
asyncio.gather(*coros)を使い、失敗時に兄弟タスクもキャンセルする場合はasyncio.TaskGroup(3.11+)を使います。ブロッキング呼び出しはrun_in_executorに渡し、コルーチン内で直接実行してはいけません。 - Go — 一つの単位として失敗させるファンアウトには
errgroup.Group(golang.org/x/sync)を、そうする必要がない場合はsync.WaitGroupを使います。ブロックする可能性のある処理ではcontext.Contextを最初のパラメーターとし、構造体に保存せず、引き渡していきます。 - Rust — 独立した future には
tokio::join!を、最初のエラーで中断する場合はtry_join!を使います。終了処理にはCancellationTokenまたはJoinHandleの破棄を使います。 - Java —
CompletableFuture.allOf(...)、または利用可能な場合は構造化並行処理(StructuredTaskScope)を使います。 - C# —
Task.WhenAll(...)を使い、すべての非同期シグネチャにCancellationTokenを引き渡します。
型安全性
// GOOD: Explicit types, no any
interface Market {
id: string;
name: string;
status: 'active' | 'resolved' | 'closed';
}
function getMarket(id: string): Promise<Market> { /* ... */ }
// BAD: any or untyped
function getMarket(id: any): Promise<any> { /* ... */ }
その他の言語でも目標は同じです。無効な値を表現できないようにし、すべての呼び出し箇所ではなく境界で検証します。
- Python — すべての公開シグネチャに型注釈を付け、CI で
mypy/pyrightを厳格モードで実行します。何もチェックしない型ヒントはコメントと同じです。API と解析の境界ではTypedDict/dataclass/Pydantic モデルを使い、生のdict[str, Any]は使いません。 - Go —
anyより具体的な型を優先します。インターフェースを受け取り、構造体を返します。シグネチャ内のanyは解析境界であり、直ちに型スイッチまたはerrors.Asで処理します。 - Rust — 基本型をそのまま使うのではなく、新しい型(
struct UserId(u64)。u64ではありません)を使い、引数の取り違えをコンパイラーで検出できるようにします。文字列で表現する状態には列挙型を使います。 - Java/Kotlin — Kotlin の nullable 型を使うか、Java では
Optionalと null 許容アノテーションを使います。公開シグネチャにObjectを使ってはいけません。 - C# — プロジェクト全体で nullable 参照型を有効にし(
<Nullable>enable</Nullable>)、警告をエラーとして扱います。
型チェッカーのない動的型付け言語(素の JS、Ruby、PHP、Lua)では、実行時に同じ役割を果たします。信頼境界でスキーマを使って検証し、内部呼び出しではガードを省きます。
コメントとドキュメント
- 何をするかではなく、なぜそうするかを説明します。「API に過剰な負荷をかけないよう、指数バックオフを使います」と書き、「再試行回数を増やします」とは書きません。コードを言い換えただけのコメントはノイズです。削除するか、不要になるようコードの名前を変更します。
- ASCII の句読点を使います。コメントは開発者が入力するように書きます。
—ではなく-、…ではなく...を使い、引用符は半角にします。装飾的な記号や絵文字は使いません。コメント中の装飾的な記号は、AI 生成の兆候であり、プロジェクトのスタイルではありません。(対象はコメント文のみです。文字列リテラル、識別子、データには適用しません。) - コメント文では平易な動詞を使います。平易な表現がある場合、借用語彙の閉じたリストに含まれる表現をコメントで使ってはいけません。リストと置き換え表現は
awesome-humanize-en/references/lexicon.mdの「Borrowed diction」セクションにあり、語句の一つが製品固有の語彙であるドメインについても記載されています。そのスキルがなくても、このルールは適用されます。より平易な動詞がある場合、コメントではそれを使います。(対象はコメント文のみです。文字列リテラルや製品データ内では、その語はコンテンツです。) - コードで宣言されている値を書きません。コメントでは値が何のためにあるかを説明し、値そのものは書きません。
border-radiusの省略記法の長さ、引用符で囲んだmax-height、色、タイムアウト、定数と同じ数値だけでなく、ソースにあるセレクター、クラス名、ストレージキーも書きません。コメントは、名前変更の一括処理が届かない唯一の複製です。リポジトリ内で宣言されていない値(ベンダーの上限やプロトコルで固定された幅など)は、それを定めているものと合わせて記載できます。 - セクション区切り線は使いません。
// --- HTTP、// ==== Helpers ====なども同様です。ブロックの区切りには空行を使います。目次が必要なファイルは分割します。 - コメント内のリストは左揃えにします。マーカーの後はスペースを一つ空け、折り返した行はマーカーと同じ位置から始めます。
//のような字下げはしません。文書化する実際のネストを表すインデントは内容の一部なので、そのまま残します。 - 選択が意図的だったという主張はしません。
deliberately、intentionally、by designは、理由を示さずに意図だけを主張する語です。その語を削除し、理由を書きます(lexicon.mdの「Borrowed diction」を参照)。 - ドキュメントコメントには、型だけでは分からない契約を記載します。発生するエラー、副作用、単位、順序、特別な値(
null、0、空が何を意味するか)、呼び出し方が分かりにくい場合の例を含めます。公開ライブラリや SDK のインターフェースには詳しく記載し、内部ヘルパーや単純なデータ型にはほとんど記載しません。@paramと@returnsは、名前や型を超える情報がある場合にのみ使います。シグネチャを繰り返すだけのブロックはノイズです。言語固有の形式を使い、プロジェクトのスタイルに合わせます。JSDoc/TSDoc、Python の docstring(PEP 257、およびプロジェクトで採用している Google/NumPy/reST 形式)、シンボル名から始める Go のドキュメントコメント、# Examplesセクションを含む Rust の///、Javadoc、C# の XML ドキュメントコメントなどです。 - コメントアウトされたコードは残しません。削除するか、チケットで理由を説明します。履歴の管理にはバージョン管理を使います。
これは、現在新しく書くコードや変更するコードに適用する基準です。既存のコメントをリポジトリ全体で見直し、何を削除、簡潔化、修正するかを判断する場合は、誤検出を防ぐ境界と動作を変えない検証手順を定めたその手順を担当する awesome-code-cleanup を使います。
ファイルとプロジェクトの構成
- 既存のレイアウトに従います(例:
src/app/、src/components/、src/lib/)。 - プロジェクトでバレルファイルや index による再エクスポートを使っている場合を除き、1 ファイルにつきメインのエクスポートは一つにします。
- インポートは標準ライブラリ → サードパーティ → ローカルの順にグループ化します。並び順はプロジェクトに合わせ、アルファベット順またはパス順にします。
バックエンドのレイヤーと境界(該当する場合)
- 3つのモデルに分けます — DTO/APIモデル、ドメインモデル、永続化/ORMモデルを分離します。1つの
Userオブジェクトをトランスポート、ビジネスロジック、ストレージ間で使い回すと、APIの形状がテーブルの形状に引きずられ、リファクタリングのたびにすべてのレイヤーに変更が及びます。 - レイヤー配置の目安 — HTTPステータスコードが必要ならエッジ/コントローラー、ビジネスルールが必要ならサービス、テーブル/インデックス/ORMが必要ならリポジトリに配置します。処理の流れは一方向にします:
controller -> service -> repository -> gateway。 - 横断的関心事はエッジで一度だけ扱います — 認証、バリデーション、レート制限、リクエストID、ログ記録はHTTPパイプライン(グローバルミドルウェア/フック、またはルート単位の設定)に置き、各ハンドラーに手作業でコピーしません。ルールは、すべてのエンドポイントでポリシーを手作業で繰り返さないことです。
- エラーはトランスポートを認識しません — サービスとリポジトリはドメインエラーをスローし、1つのグローバルハンドラーがそれらをステータスコードにマッピングします。
- コントラクトファースト — OpenAPI(または同等のもの)をリクエスト/レスポンスの形状に関する唯一の信頼できる情報源とします。そこから型付きクライアントを生成し、仕様とのずれがあればCIを失敗させます。
// GOOD: service throws a domain error; the global handler maps NotFoundError -> 404
throw new NotFoundError('market', id);
// BAD: business logic reaches into HTTP transport
return res.status(404).json({ error: 'not found' });
このセクションではレイヤーの配置のみを扱います。エラーエンベロープ、HTTPステータスのマッピング、リトライポリシーの詳細設計はawesome-error-standardsの担当です。エラーコントラクト自体が課題の場合は、そちらを参照してください。
修正すべきコードの臭い
| 臭い | 対応 |
|---|---|
| 関数が約50行を超える | 明確な名前を付けた小さな関数に分割します |
| 深いネスト(5階層以上) | 早期リターンを使うか、関数を切り出します |
| マジックナンバー | 名前付き定数に切り出します(例:MAX_RETRIES、DEBOUNCE_MS) |
| パラメーターリストが長い | オプションオブジェクトを使うか、小さな型に分割します |
| 2か所に重複したロジックがある | 共通関数またはモジュールに切り出します |
上記の臭いは、どの言語にも当てはまります。数値のしきい値は出発点であり、絶対的なルールではありません。文書化されたリポジトリの規約が常に優先されます。また、リンターや型チェッカーがすでに検出するものを再度指摘しないでください。パターンを新たに作る前にコードベースを検索してください。問題はすでにどこかで解決されていることが多いため、同じことをする方法をもう1つ追加するのではなく、既存のものを再利用します。
フレームワーク固有の正しさ(該当する場合)
上記の普遍的な臭いとは別に、どのフレームワークにも、正しそうに見えて実行時に失敗する落とし穴がいくつかあります。他のフレームワークの一覧がそのまま当てはまると考えず、対象のフレームワークに応じた一覧を把握してください。判断基準となるのは、そのフレームワーク独自のドキュメントです(ReactのRules of Hooksと「You Might Not Need an Effect」、Vueのリアクティビティに関する注意事項、Angularの変更検知ガイド、Svelteのストアとリアクティビティに関する説明など)。
実例としてのReact:
| 臭い | 対応 |
|---|---|
JSX内の {count && <X/>} | 偽と評価されたときにリテラルの 0/NaN が描画されます。明示的な三項演算子 count > 0 ? <X/> : null を使います |
| コンポーネントを別のコンポーネント内に定義する | 外に移動します。ネストされた定義はレンダーごとに新しい型となって再マウントされ、状態が失われます |
props/stateから導出できる状態を useState+useEffect に保持する | レンダー中に導出するか、キー付きリセットを使います。effectは不要です |
他のフレームワークにも当てはめられるのは、3つの行そのものではなく、問題の形です。偽の値が表示内容として描画されること、レンダーごとにIDが変わって状態がひそかに破棄されること、導出すべき状態を重複して保持することです。プロジェクトで使われているフレームワークにおいて、こうした形の問題を探してください。
フロントエンドのレンダリングとモーション(該当する場合)
- コンポジターのプロパティ(
transformとopacity)だけをアニメーションさせます。レイアウトプロパティ(width、height、top、margin)はアニメーションさせないでください。フレームごとにリフローが発生するため、位置の変更には FLIP テクニックを使います。 - 1 つのフレーム内でレイアウトの読み取りと書き込みを交互に行わないでください。レイアウトのスラッシングを避けるため、書き込みの前に読み取り(
getBoundingClientRect、offsetWidth)をまとめます。 - スクロールに連動するアニメーションには、JS のスクロールイベントリスナーよりも
animation-timeline: view()/scroll()を優先します。will-changeは必要な箇所だけに使い、使い終わったら削除します。 - 妥当なデフォルト:見出しには
text-balance、本文にはtext-pretty、数値列にはtabular-nums、h-screenよりもh-dvh、インタラクションは約 200ms 未満、ビューごとにアクセントカラーは 1 色、z-index のスケールは固定。
フロントエンドのアーキテクチャ(該当する場合)
- 機能を軸にフォルダーを構成します。製品コード(ページ、機能コンポーネント、状態、機能スコープの API アダプター、テスト)は、機能/モジュールのフォルダーにまとめます。共有フォルダーには、アプリ間で共通するプリミティブのみを置きます。デザインシステム、アプリシェル、ルーティングのブートストラップ、グローバル設定、i18n などです。アンチパターン:境界のない
shared/common/utilsの寄せ集め、巨大なグローバルcomponents/、使い捨ての 1 ファイルだけのフォルダー。 - まとめて配置します。重要なコンポーネントやフックには、テスト、ストーリー、スタイル、
index.tsの再エクスポートを隣接させます。 - パフォーマンス改善には、ルートレベルのコード分割をデフォルトで使います。ルートのチャンクを遅延読み込みし、ランディングページで不要なら、ダッシュボード規模の依存関係を初期バンドルに含めないようにします。コンポーネントを手作業で最適化する前にプロファイリングします。(上記のコンポジター/アニメーションのルールとは別で、こちらはフレーム予算ではなくバンドルの構成に関するものです。)
- API コントラクトを利用し、型を重複定義しないでください。リクエスト/レスポンスの形状を手作業で複製するのではなく、バックエンドが管理するものと同じ OpenAPI 仕様から TypeScript の型を生成します。サーバー状態ライブラリ(例:TanStack Query)を使い、読み込み/エラー/再試行の処理を統一します。エラーのマッピングは一度だけ行い(単一の
parseApiError)、エラー UI にサーバーのrequest_idを表示して、ユーザーからの報告をサーバーログと照合できるようにします。
チェックリスト(適用時)
- 命名がプロジェクトの規約に合っている(camelCase/PascalCase/snake_case)
- 引数や共有状態を直接変更していない
- エラーを適切に処理し、コンテキストを付けて伝播している
- 不要な
anyを使わず、境界では型を明示している - 公開 API のドキュメントを記述している(言語のドキュメントコメント形式、またはプロジェクト標準)
- ファイルと構成が既存のレイアウトに合っている
- マジックナンバーを使わず、定数に名前を付けている
- Lint とフォーマットのルールに準拠している(プロジェクトに設定がある場合)
アンチパターン
| アンチパターン | より良い方法 |
|---|---|
| 「ちょっとしたスクリプトだから」 | 同じ命名と構成のルールを適用します。将来読む人に感謝されます |
| 「あとで使うため」にコードをコメントアウトする | 削除します。Git の履歴やチケットを使います |
| 対象範囲のファイルだけを修正する | パターンに手を加える場合は、プロジェクト全体での規約化や後続対応を提案します |
| ツールを使わずにスタイルルールを追加する | 言語のフォーマッターとリンターにルールを設定し、フォーマットを自動化します(gofmt、ruff/black、rustfmt+clippy、ktlint、dotnet format、ESLint/Prettier)。さらに、言語をまたいで適用する項目には .editorconfig を使います |
統合
- プロジェクトにスタイルガイド、CONTRIBUTING、または Lint/フォーマット設定がある場合は、まずそれに合わせます。ユーザーから明示的に依頼された場合に限り、上書きします。
- ルールを列挙するだけでなく、具体的な編集(名前の変更、関数の抽出、型の追加など)を提案します。