code-quality について
AI エージェント向けに、テスト、コード構成、エラー処理、命名の基準を示すスキルです。チェックリストと詳細なルールで実装やリファクタリングを支援します。
主な機能
- 014 層のテスト検証
- 02テスト駆動開発のサイクル
- 03コード構成と命名規則
- 04具体的なエラー処理の指針
- 05実装・リファクタリング用チェックリスト
活用シーン
- 01実装前のテスト計画
- 02コード品質上の問題確認
- 03一貫性のないコードのリファクタリング
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ alexanderstephenthompson/ claude-hub --skill code-qualityインストール後、Claude Code で/code-qualityを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
assets/docstring-templates.mdtdd-checklist.mdcode-review-checklist.mdreferences/naming-reference.mderror-handling-reference.mdtesting-reference.mdscripts/check_naming.pycheck_complexity.py詳細
コード品質基準
妥協できないコード品質基準です。好みの問題ではなく、必須要件です。
問題点
AI エージェントが動作するコードを生成しても、次第に方向性がばらつきます。各セッションで、設計によるものではなく、独立して作業するために、名前の付け方、ネストのパターン、エラー処理の方法が少しずつ異なります。時間が経つにつれ、このずれは積み重なり、エラー処理の方法が3通り、命名規則が4通りになり、ほぼ同じなのに微妙に異なるマジックナンバーがあちこちに散らばるようになります。これらの基準によってそのばらつきをなくし、リファクタリングが小規模かつ低頻度に保たれるようにします。
活用方法
- ビルダー: コードを書く前に
## Builder Checklistを読んでください。チェックリスト項目が曖昧な場合は、説明のセクションを参照してください。 - リファクタリング担当者:
## Enforced Rulesを課題リストとして使用してください。修正方法については、説明のセクションを読んでください。 - 両者: 説明のセクションが正式な基準です。チェックリストとルール表は、同じ内容を簡潔にまとめたものです。
テスト駆動開発:4層の検証フレームワーク
第1層 — ユニットテスト(ロジックは正しく動作するか?)
個々の関数、計算、状態変換、データ整形を分離してテストします。入力と出力だけを対象に、「この入力を与えたとき、この出力が得られるか」を検証します。テスト対象の周辺にあるものはすべてモック化します。テストは高速で低コストなため、最も数が多くなるはずです。正常系、実装対象の具体的なケース、エッジケースを網羅します。
第2層 — 統合テスト(各要素は連携するか?)
複数の要素がどのように連携するかをテストします。API 呼び出しが期待する形式のデータを返すこと、コンポーネントが正しい props でレンダリングされること、データベースクエリが適切なデータを返すこと、フックと状態管理が正しく連携すること、モジュール間でデータが正しく受け渡されることを検証します。「単体では動くが、連携させると壊れる」問題を検出できます。
第3層 — 振る舞いテスト(フローは正しく動作するか?)
Playwright などの自動化ツールを使い、ユーザーが実際に利用するフローをエンドツーエンドでテストします。クリックしたら期待する結果が得られること、目的の画面に移動できること、フォームが正しく送信されること、データが表示されるべき場所に現れることなど、実際の操作を再現します。隣接するフローもテストして、デグレがないことを確認します。テストに時間がかかり、壊れやすくもなりますが、ほかの層では見つけられない問題を検出できます。
第4層 — 人による検証(実際に使い心地はよいか?)
自動テストでは文字どおり判断できないすべての点を確認します。見た目は正しいか、アニメーションは滑らかか、UX は直感的に理解できるか、画面上のデータは現実の期待に合っているか、実際のデバイスと画面サイズで動作するか、などです。第1〜3層に合格したら、汎用的な確認項目ではなく、変更内容に応じた具体的な手動チェックリストを作成します。作業でチャートコンポーネントに手を加えた場合は、人にチャートを目視で確認してもらいます。API 連携に手を加えた場合は、実際のデータが流れることを確認してもらいます。レイアウトに手を加えた場合は、実際の画面幅で確認してもらいます。
TDD の原則
真の TDD では、実装コードを書く前に第1〜3層のテストを記述します。すべてのテストが最初は失敗する必要があります。それが重要な点です。テストによって、何を作るのかを定義します。その後、テストに合格するまでコードを書きます。第4層は、ほかのすべてのテストが成功した後の最終確認です。
あらゆる変更でのサイクル:
- レッド — 失敗するテストを書く
- グリーン — テストに合格させるための最小限のコードを書く
- リファクタリング — 振る舞いを変えずにコードを整理する
- ユニットが完成するまで第1層で繰り返し、その後、第2層、第3層へ進む
- 自動テストのすべての層が成功した後、第4層を最終確認とする
迷ったときは: 焦らず、テストを書き、可能な限り小さな変更を加えてください。
優れたテストとは
すべてのテストを Arrange-Act-Assert の構成にします:
def test_apply_discount_reduces_total():
# Arrange — set up the scenario
cart = Cart(items=[Item(price=100)])
discount = Discount(percent=20)
# Act — perform the action under test
cart.apply_discount(discount)
# Assert — verify the outcome
assert cart.total == 80
1つのテストにつき1つの概念。 テスト名に「and」が含まれている場合は、2つのテストに分けます。
実装ではなく、振る舞いを説明するテスト名を付けます:
| 悪い例 | 良い例 |
|---|---|
test_calculate | test_calculate_total_sums_item_prices |
test_error | test_negative_quantity_raises_validation_error |
test_user_service | test_deactivated_user_cannot_place_order |
テストは次の条件を満たす必要があります:
- 独立している — 他のテストの状態や実行順序に依存しません
- 決定論的である — 同じ入力なら同じ結果になります。ランダム性、時刻への依存、ネットワーク呼び出しは避けます
- 高速である — 単体テストはミリ秒単位で実行されます。遅い場合は、単体テストではありません
- 読みやすい — テスト名を見れば、テスト本体を読まなくても何が壊れたか分かります
コード構成
ネストより早期リターン
まずガード節を使い、制御フローを平坦にします。
悪い例 — ネストが深く、追いにくい:
def process(order):
if order:
if order.items:
if order.is_valid:
return calculate_total(order)
return None
良い例 — 平坦で、明快:
def process(order):
if not order:
return None
if not order.items:
return None
if not order.is_valid:
return None
return calculate_total(order)
ネストの深さは最大3階層です。 それ以上になる場合は、関数に切り出します。
関数のサイズ
関数は1つのことだけを行うようにします。関数内の「セクション」を区切るコメントが必要なら、そのセクションは別の関数に分けるべきです。
ガイドライン:
- 関数が約30行を超える場合は、切り出せる箇所を探します
- 関数の引数が3~4個を超える場合は、処理が多すぎる可能性があります
- 関数に分かりやすい名前を付けられない場合は、責務が多すぎます
単一責任
すべての関数、クラス、モジュールには、変更する理由が1つだけあるべきです。
問題の兆候: 「この関数は、検証とフォーマットと保存を処理します。」
改善方法: 3つの関数に分けます — validate、format、save。
インポートの整理
- 使われていないインポートは禁止 — コメントアウトせず、削除します
- ワイルドカードインポートは禁止 —
from x import *では、名前の出所が分かりにくくなります - インポートをグループ化 — 標準ライブラリ、外部ライブラリ、内部モジュールの順に、空行で区切ります
# Bad — unordered, wildcard, unused
from utils import *
import os
import requests
from collections import OrderedDict # unused
# Good — grouped, explicit, no dead weight
import os
import requests
from app.models import User
from app.services import send_welcome_email
デッドコード
呼び出されていないコードは存在しないも同然です。削除しましょう。バージョン管理に記録は残ります。
- コメントアウトしたコードは禁止 —
// old version、# was: ...、/* removed */はノイズです - 到達不能な分岐は禁止 — 無条件の return の後にある不要な
elseや、未使用の変数は削除します - プレースホルダーコメントは禁止 —
// TODO: removedや// no longer neededは、行ごと削除します
小手先の工夫より明示性
簡潔さより読みやすさを優先します。処理は明確な手順に分けます。
悪い例 — 工夫されていますが、デバッグしにくい:
names = [u.name for u in users if u.is_active and u.role in allowed]
良い例 — 意図が明確で、デバッグしやすい:
active_users = filter_active(users)
authorized_users = filter_by_role(active_users, allowed)
names = extract_names(authorized_users)
一行のコードを理解するのに頭の中で解析が必要なら、分割しましょう。書き手ではなく、読み手のために最適化します。
エラー処理
早期に失敗させる
境界で入力を検証します。不正なデータをシステムの深部まで通さないようにします。
def create_user(email, name):
if not email:
raise ValidationError("Email is required")
if not is_valid_email(email):
raise ValidationError("Invalid email format")
return save_user(email, name)
汎用的なエラーより具体的なエラー
想定している例外を捕捉し、想定外の例外は再送出します。except Exception は絶対に記述しないでください — まず実際の失敗原因を特定します。
処理に応じて例外の型を選びます:
| 処理 | 捕捉する例外 | 理由 |
|---|---|---|
| ファイルのオープン/読み込み/書き込み | OSError | FileNotFoundError、PermissionError、IsADirectoryError をカバーします |
| ファイルの読み込みと内容の解析 | (OSError, UnicodeDecodeError) | ファイルが存在していても、無効なエンコーディングが含まれている場合があります |
| JSON/YAML の解析 | (json.JSONDecodeError, ValueError) | 内容の形式が正しくありません |
| 文字列から数値への変換 | ValueError | 形式が無効です |
| Dict/list へのアクセス | (KeyError, IndexError) | キーがないか、インデックスが範囲外です |
| ネットワークリクエスト | (ConnectionError, TimeoutError) | ネットワーク固有の障害です |
| サブプロセスの実行 | (subprocess.SubprocessError, OSError) | プロセスの起動または実行に失敗しました |
| 正規表現の処理 | re.error | パターンが無効です |
悪い例 — すべてを握りつぶします:
try:
do_risky_thing()
except Exception:
pass
良い例 — 理解できる例外を処理します:
try:
do_risky_thing()
except NetworkError:
return retry()
except ValidationError as e:
return error_response(e.message)
# Unexpected errors propagate up
広範な例外捕捉が許容される場合: アプリケーションの最上位境界(CLI の main()、API リクエストハンドラー)に限ります。未処理のクラッシュを避けるために必要な場合にのみ使用します。その場合でも、処理を続行する前に例外の詳細をすべてログに記録します。
エラーを握りつぶさない
エラーを捕捉した場合は、必ず次のいずれかを行ってください。
- 処理する — 意味のある回復処理を行います
- ログに記録して再スローする — 失敗を見えるようにします
- 変換する — 呼び出し元向けに、より具体的なエラーでラップします
空の catch / except ブロックはバグです。
命名規則
名前から、次のことが明確に伝わるようにしてください。
- 誰が動作しているか — アクションを実行する主体
- どのようなアクションが行われるか — 振る舞いを表す動詞
- データや所有権の流れの方向 — どこからどこへ向かうか
方向の明確化
前置詞(to、from、into、onto)または名前付きパラメーターを使用してください。
悪い例 — 曖昧:
shop.buy_item(item_id, buyer) # Who is buying?
transfer(amount, account) # Transfer to or from?
良い例 — 明確:
shop.sell_item_to(item_id, buyer) # Shop sells TO buyer
shop.sell(item_id, to=buyer) # Named parameter clarifies
transfer_from(account, amount) # Direction explicit
account.transfer_to(other, amount) # Direction in method name
音読テスト
メソッド呼び出しを声に出して読んだときに自然に聞こえない場合、その名前は適切ではありません。
# "shop buy item buyer" — confusing
shop.buy_item(item_id, buyer)
# "shop sell item to buyer" — clear
shop.sell_item_to(item_id, buyer)
ブール値の命名
ブール値には必ず is、has、should、can、will、did のいずれかを先頭に付けてください。
# Bad — ambiguous (is it a noun? a verb? a state?)
active = True
permission = True
refresh = True
# Good — clearly a yes/no question
is_active = True
has_permission = True
should_refresh = True
命名パターン
| パターン | 使用する場面 | 例 |
|---|---|---|
verb_noun_to(target) | アクションが対象に向かう | send_message_to(user) |
verb_noun_from(source) | アクションがソースから向かう | receive_payment_from(customer) |
noun.verb_to(target) | オブジェクトが対象に向けてアクションを実行する | cart.transfer_to(order) |
verb(noun, to=target) | 名前付きパラメーターで意味を明確にする | assign(task, to=developer) |
省略形を使わない
毎回、単語を省略せずに書いてください。許容される省略形は、広く理解されている技術用語の id、url、api、db、io のみです。
1 文字の変数は禁止です。 ループカウンターであっても例外ではありません。i や j では、何を反復処理しているのかが分かりません。
# Bad — what is i? what is j?
for i in range(len(rows)):
for j in range(len(columns)):
grid[i][j] = calculate(i, j)
# Good — names describe the iteration
for row_index in range(len(rows)):
for column_index in range(len(columns)):
grid[row_index][column_index] = calculate(row_index, column_index)
よくある違反 — 頻繁に見かけますが、必ず展開してください。
| こちらを使う | こちらは使わない |
|---|---|
dependency | dep |
index / position | idx |
source | src |
destination | dst |
description | desc |
threshold | thresh |
config / configuration | cfg |
message | msg |
request | req |
response | res |
context | ctx |
error | err |
value | val |
count | cnt |
button | btn |
user | usr |
callback | cb |
function | fn |
manager | mgr |
service | svc |
repository | repo |
implementation | impl |
password | pwd |
temporary | tmp |
number | num |
全一覧:references/naming-reference.md
避けること
| 避ける | 代わりに使う |
|---|---|
1 文字の名前(i、x、e) | 説明的な名前(row_index、coordinate、error) |
汎用的な名前(data、list、temp) | 具体的な名詞(user_data、order_list) |
否定形のブール値(is_not_disabled) | 肯定形(is_enabled) |
定数と明確さ
マジック値を使わない
すべての数値リテラルと文字列リテラルには名前を付けてください。リテラルを書く前に、抽出テストを適用してください。
抽出テスト: リテラルが 0、1、-1、True、False、None、"" のいずれでもない場合は、名前付き定数が必要です。
以下も対象です。
- しきい値と上限 —
MAX_RETRIES = 3、HIGH_COUPLING_THRESHOLD = 10 - サイズと測定値 —
MIN_FONT_SIZE_PX = 12、MASK_VISIBLE_CHARACTERS = 4 - 文字列パターン —
DEFAULT_ENCODING = "utf-8"、CSV_DELIMITER = "," - 構成値 —
TOP_RESULTS_DISPLAY_LIMIT = 20、SCAN_DEPTH = 3
定数には、値そのものではなく意味を表す名前を付けてください。 THREE = 3 では意味がありません。MAX_RETRIES = 3 なら意図が伝わります。
悪い例:
if retry_count > 3:
sleep(60)
if len(results) > 20:
results = results[:20]
良い例:
MAX_RETRIES = 3
RETRY_DELAY_SECONDS = 60
TOP_RESULTS_DISPLAY_LIMIT = 20
if retry_count > MAX_RETRIES:
sleep(RETRY_DELAY_SECONDS)
if len(results) > TOP_RESULTS_DISPLAY_LIMIT:
results = results[:TOP_RESULTS_DISPLAY_LIMIT]
定数はモジュールの先頭に配置し、目的ごとにまとめて、関数定義より前に置いてください。
ブール値のパラメーター
ブール値の引数は、呼び出し箇所で意味が分かりにくくなります。
悪い例 — True は何を意味しますか?
create_user(data, True, False)
良い例 — 名前付きパラメーターまたはオプション:
create_user(data, send_welcome=True, require_verification=False)
言語が名前付きパラメーターをサポートしていない場合は、オプションオブジェクトまたは構造体を使用してください。
デフォルトでは不変にする
デフォルトでは不変にします。再代入が本当に必要な場合に限り、可変バインディングを使用します。
const(JS/TS)、final(Java/Dart)、readonly(C#)、または同等のものを使用しますlet/varは値を変更する必要がある場合に限って使用します(ループカウンター、累積値など)- 関数の引数は変更せず、代わりに新しい値を返します
# Bad — mutates the input
def apply_discount(cart):
cart["total"] *= 0.8
return cart
# Good — returns a new value
def apply_discount(cart):
return {**cart, "total": cart["total"] * 0.8}
ドキュメント
Docstring
Docstring は常に更新されるドキュメントです。 公開 API は、実装を読まなくても理解できるようにしてください。
必須要素
すべての公開関数、メソッド、クラスには、次の内容を含める必要があります。
- 目的 — 何をするのか(1 行)
- パラメーター — 各パラメーターの型と意味
- 戻り値 — 何が返されるか、およびその条件
- 副作用 — 状態の変更、I/O、ミューテーション
- エラー — 発生する可能性のある例外やエラー
- 例 — よくあるケースを示す、現実的な使用例
Docstring の例
def sell_item_to(self, item_id: str, buyer: Customer) -> Receipt:
"""Sell an item from shop inventory to a customer.
Transfers ownership of the item from the shop to the buyer,
processes payment, and updates inventory.
Args:
item_id: Unique identifier of the item to sell.
buyer: Customer purchasing the item. Must have sufficient balance.
Returns:
Receipt containing transaction details and timestamp.
Raises:
ItemNotFoundError: If item_id doesn't exist in inventory.
InsufficientBalanceError: If buyer can't afford the item.
ItemAlreadySoldError: If item was sold between check and purchase.
Examples:
Basic sale:
>>> shop = Shop(inventory=[item])
>>> buyer = Customer(balance=100)
>>> receipt = shop.sell_item_to(item.id, buyer)
>>> assert receipt.amount == item.price
>>> assert item.id not in shop.inventory
Handling insufficient balance:
>>> poor_buyer = Customer(balance=0)
>>> shop.sell_item_to(item.id, poor_buyer)
Raises InsufficientBalanceError
"""
Docstring のルール
- 例は実際のテストシナリオに沿ったものにします
- 動作を変更した場合は Docstring も更新します
- Docstring は飾りではなく、コードの重要な一部として扱います
コメント
コメントを書く前に、削除テストを行ってください。 コメントを頭の中で削除してみてください。何か失われますか? 命名や構造ですでに同じ情報が伝わる場合は、コメントを書かないでください。
| コメントを書く場合 | コメントを書かない場合 |
|---|---|
| 理由 — 意図、業務上の理由、自明でない背景 | 内容 — コードを見ればすでにわかること |
| 自明でない落とし穴やエッジケース | 明らかな操作 |
| 複雑なアルゴリズムの概要 | 説明が必要なひどいコード(代わりにコードを修正します) |
| チケットや課題への参照がある TODO | 背景のない TODO |
| 正規表現パターンの説明(パターンが何に一致するか) | 関数呼び出しの言い換え(# Send the email) |
悪い例 — コードを言い換えているだけです:
# Get the users
users = get_users()
# Filter active users
active_users = filter_active(users)
# Count the results
count = len(active_users)
良い例 — コメントは不要です(コード自体で明らかです):
users = get_users()
active_users = filter_active(users)
count = len(active_users)
良い例 — 理由を説明しています:
# Offset by 1 because CSS cascade position is 1-indexed but array is 0-indexed
cascade_position = file_index + 1
コードの動作を説明するコメントが必要なら、そのコードはもっと明確にできるはずです。 変数名を変更し、関数を切り出し、ロジックを簡潔にすれば、コメントは不要になります。
ビルダー用チェックリスト
このスキルの適用対象となるコードを書く前に、計画がこれらの制約に沿っていることを確認してください。ここでは、上記で詳しく説明したガイダンスを、実装前の確認用にまとめています。ビルダーはコードを書く前にこのセクションを読み、リファクタリング担当者は「適用されるルール」の表と詳しい説明を参照してください。
- レイヤー 1〜3 を実装前に記述する(まずテストを失敗させ、その後コードで成功させる)
- レイヤー 1:ユニットテストでロジック、正常系、エッジケースを網羅する
- レイヤー 2:統合テストで各要素が正しく連携することを検証する
- レイヤー 3:振る舞いテストで実際のユーザーフローを最初から最後までシミュレートする
- レイヤー 4:変更内容をもとに人による確認用チェックリストを作成する
- 各テストでは概念を 1 つだけ扱い、Arrange-Act-Assert 構造にする
- テストは分離され、決定的で、高速である
- 関数は短く、単一の責務を持つ
- ネストは最大 3 階層とし、早期リターンを使用する
- 境界ではエラーを速やかに、具体的な型で失敗させる
- 例外の型は処理に対応させる(裸の
except Exceptionは使用しない) - 空の catch/except ブロックを作らない
- 名前が読み上げテストに合格する
- 1 文字の変数は使用せず、説明的な名前を付ける
- 省略名は使わず、単語を省略せずに書く(
depではなくdependency) - メソッド名で方向が明確になるようにする(to/from)
- ブール値には is/has/should/can を接頭辞として付ける
- すべてのリテラルに抽出テストを行い、0/1/True/False/None/"" 以外は名前付き定数にする
- 定数はモジュールの先頭に置き、目的ごとにまとめる
- ブール型パラメーターには名前付き引数またはオプションを使用する
- 変数はデフォルトで不変にする(
const/final/readonly) - 未使用のインポートやワイルドカードインポートを使用しない
- コメントアウトされたコードやデッドコードを残さない
- すべての公開 API に完全な Docstring を付ける
- コメントは削除テストに合格させ、内容ではなく理由だけを説明する
強制ルール
これらのルールは、check.js(clean-team)によって決定的にチェックされます。これらの標準を更新する場合は、対応する check.js のルールも合わせて更新してください。その逆も同様です。
| ルール ID | 重大度 | チェック内容 |
|---|---|---|
no-debugger | エラー | コードに残された debugger 文 |
no-var | エラー | var 宣言(const/let を使用してください) |
no-empty-catch | エラー | 処理のない空の catch ブロック |
no-console | 警告 | console.log/warn/error 文 |
no-double-equals | 警告 | 厳密等価の代わりに使用されている ==/!=(== null は許可) |
リファレンス
references/testing-reference.md— テストピラミッドの詳細、モックのガイドライン、アンチパターンreferences/naming-reference.md— 命名規則の全容、省略形のルール、ドメインの命名references/error-handling-reference.md— エラー階層、再試行/フォールバックのパターン、エラーバウンダリー
アセット
assets/tdd-checklist.md— TDD ワークフローの手順別チェックリストassets/docstring-templates.md— コピーして使える docstring テンプレート(Python、JS/TS、C#、Rust、Go)assets/code-review-checklist.md— 包括的なコードレビューのチェックリスト
テンプレート
ドメイン固有のコード品質拡張です。各ファイルでは、プロジェクトの種類に応じて同じカテゴリ(構造、命名、テスト、エラー処理、パフォーマンス)のひな形を提供します。
templates/web-development.md— Web 固有の品質ルールtemplates/data.md— データパイプラインと分析に関する品質ルールtemplates/world-building.md— Unity と VRChat の品質ルール
スクリプト
scripts/check_naming.py— あらゆるコードベースの命名規則を検証しますscripts/check_complexity.py— 関数の長さ、ネストの深さ、パラメーター数をチェックします