code-quality-principles について
KISS、YAGNI、SOLIDの原則を、Python、Rust、TypeScriptの例とともに解説するコーディングスキルです。リファクタリングでの可読性と保守性の改善を支援します。
主な機能
- 01KISS、YAGNI、SOLIDの指針
- 02言語別のコード例
- 03コードレビュー用チェックリスト
- 04原則が衝突する場合の指針
活用シーン
- 01リファクタリングによる可読性と保守性の向上
- 02不要な複雑さがないかコードをレビュー
- 03簡潔さと抽象化のどちらを選ぶか検討
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ athola/ claude-night-market --skill code-quality-principlesインストール後、Claude Code で/code-quality-principlesを入力して読み込まれたか確認します。
詳細
コード品質の原則
KISS、YAGNI、SOLID の原則に関するガイダンスと言語ごとの例です。
使用する場面
- コードの可読性と保守性を高める
- リファクタリングで SOLID、KISS、YAGNI の原則を適用する
使用しない場面
- 使い捨てのスクリプトや一度限りのデータ移行
- 可読性を犠牲にすることが正当化される、パフォーマンスが重要なコード
KISS(Keep It Simple, Stupid)
原則:不要な複雑さを避けます。巧妙な解決策より、分かりやすい解決策を優先します。
ガイドライン
| 優先するもの | 避けるもの |
|---|---|
| シンプルな条件分岐 | 単純なチェックに複雑な正規表現を使う |
| 明示的なコード | マジックナンバー/文字列 |
| 標準的なパターン | 巧妙なショートカット |
| 直接的な解決策 | 抽象化しすぎたレイヤー |
Python の例
# Bad: Overly clever one-liner
users = [u for u in (db.get(id) for id in ids) if u and u.active and not u.banned]
# Good: Clear and readable
users = []
for user_id in ids:
user = db.get(user_id)
if user and user.active and not user.banned:
users.append(user)
Rust の例
// Bad: Unnecessary complexity
fn process(data: &[u8]) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
data.iter()
.map(|&b| b.checked_add(1).ok_or("overflow"))
.collect::<Result<Vec<_>, _>>()
.map_err(|e| e.into())
}
// Good: Simple and clear
fn process(data: &[u8]) -> Result<Vec<u8>, &'static str> {
let mut result = Vec::with_capacity(data.len());
for &byte in data {
result.push(byte.checked_add(1).ok_or("overflow")?);
}
Ok(result)
}
YAGNI(You Aren't Gonna Need It)
原則:実際に必要になるまでは機能を実装しません。
ガイドライン
| すること | しないこと |
|---|---|
| 現在の問題を解決する | 仮定上の将来に備えて作り込む |
| 3つ目のユースケースが現れたら追加する | 1つのユースケースのために抽象化を作る |
| デッドコードを削除する | 「念のため」のコードを残す |
| 実用最小限の解決策 | 時期尚早な最適化 |
Python の例
# Bad: Premature abstraction for one use case
class AbstractDataProcessor:
def process(self, data): ...
def validate(self, data): ...
def transform(self, data): ...
class CSVProcessor(AbstractDataProcessor):
def process(self, data):
return self.transform(self.validate(data))
# Good: Simple function until more cases appear
def process_csv(data: list[str]) -> list[dict]:
return [parse_row(row) for row in data if row.strip()]
TypeScript の例
// Bad: Over-engineered config system
interface ConfigProvider<T> {
get<K extends keyof T>(key: K): T[K];
set<K extends keyof T>(key: K, value: T[K]): void;
watch<K extends keyof T>(key: K, callback: (v: T[K]) => void): void;
}
// Good: Simple config for current needs
const config = {
apiUrl: process.env.API_URL || 'http://localhost:3000',
timeout: 5000,
};
SOLID の原則
単一責任の原則
各モジュール/クラスには、変更する理由が1つだけあるべきです。
# Bad: Multiple responsibilities
class UserManager:
def create_user(self, data): ...
def send_welcome_email(self, user): ... # Email responsibility
def generate_report(self, users): ... # Reporting responsibility
# Good: Separated responsibilities
class UserRepository:
def create(self, data): ...
class EmailService:
def send_welcome(self, user): ...
class UserReportGenerator:
def generate(self, users): ...
開放/閉鎖の原則
拡張に対して開き、変更に対して閉じているべきです。
# Bad: Requires modification for new types
def calculate_area(shape):
if shape.type == "circle":
return 3.14 * shape.radius**2
elif shape.type == "rectangle":
return shape.width * shape.height
# Must modify to add new shapes
# Good: Extensible without modification
from abc import ABC, abstractmethod
class Shape(ABC):
@abstractmethod
def area(self) -> float: ...
class Circle(Shape):
def __init__(self, radius: float):
self.radius = radius
def area(self) -> float:
return 3.14 * self.radius**2
リスコフの置換原則
サブタイプは、その基底型と置き換え可能でなければなりません。
# Bad: Violates LSP - Square changes Rectangle behavior
class Rectangle:
def set_width(self, w):
self.width = w
def set_height(self, h):
self.height = h
class Square(Rectangle): # Breaks when used as Rectangle
def set_width(self, w):
self.width = self.height = w # Unexpected side effect
# Good: Separate types with common interface
class Shape(ABC):
@abstractmethod
def area(self) -> float: ...
class Rectangle(Shape):
def __init__(self, width: float, height: float): ...
class Square(Shape):
def __init__(self, side: float): ...
インターフェース分離の原則
クライアントは、使用しないインターフェースに依存すべきではありません。
// Bad: Fat interface
interface Worker {
work(): void;
eat(): void;
sleep(): void;
}
// Good: Segregated interfaces
interface Workable {
work(): void;
}
interface Feedable {
eat(): void;
}
// Clients only implement what they need
class Robot implements Workable {
work(): void { /* ... */ }
}
依存関係逆転の原則
具象ではなく、抽象に依存します。
# Bad: Direct dependency on concrete class
class OrderService:
def __init__(self):
self.db = PostgresDatabase() # Tight coupling
# Good: Depend on abstraction
from abc import ABC, abstractmethod
class Database(ABC):
@abstractmethod
def save(self, data): ...
class OrderService:
def __init__(self, db: Database):
self.db = db # Injected abstraction
クイックリファレンス
| 原則 | 問いかけること | 危険信号 |
|---|---|---|
| KISS | 「もっとシンプルな方法はないか?」 | 単純な問題に複雑な解決策を使っている |
| YAGNI | 「今これが必要か?」 | 仮定上のユースケースに備えて作り込んでいる |
| SRP | 「変更する理由は何か?」 | クラスが複数の役割を担っている |
| OCP | 「変更せずに拡張できるか?」 | 型ごとの switch 文 |
| LSP | 「サブタイプで基底型を置き換えられるか?」 | オーバーライドしたメソッドに副作用がある |
| ISP | 「クライアントはすべてのメソッドを必要としているか?」 | 空のメソッド実装 |
| DIP | 「抽象に依存しているか?」 | ビジネスロジック内に new キーワードがある |
原則が衝突する場合
- KISS と SOLID:小規模なプロジェクトでは KISS を優先します。複雑さが増すにつれて、SOLID のパターンを追加します。
- YAGNI と DIP:実装が2つ以上できるまでは、抽象化を追加しません。
- 可読性と DRY:誤った抽象化をするより、少し重複させることを優先します。
コードレビューへの統合
コードレビューでは、次の点を確認します。
- 不要な複雑さがない(KISS)
- 先走った機能がない(YAGNI)
- 各クラスの責任が1つである(SRP)
- 巨大なクラスがない(500行超)
- 依存関係は生成するのではなく、注入されている(DIP)
検証: 行数を確認するには wc -l <file> を実行し、ファイルごとのクラス数を数えるには rg -c "class " <file>(または grep -c "class " <file>)を実行します。
関連スキル
imbue:karpathy-principles- 「シンプルさ優先」の原則は、Karpathy が観察した LLM によるコーディングの落とし穴から導かれた4原則の統合として、KISS、YAGNI、SOLID をまとめています- ゲートとスキルの連携グラフ全体については、
docs/quality-gates.md#skill-level-quality-gate-compositionを参照してください
終了条件
- 提案されたコード変更はすべて、統合レビューのチェックリストに照らして確認されています。不要な複雑さがない(KISS)、推測に基づく機能がない(YAGNI)、各クラスが単一の責任を持つ(SRP)、500行を超えるゴッドクラスがない、依存関係は生成せず注入する(DIP)
- KISS と SOLID が競合する場合、解決方法が文書化されています。小規模なプロジェクトでは KISS を優先し、複雑さが増すにつれて SOLID のパターンを適用します。選択は明示されており、暗黙ではありません
- 変更したファイルに対して
wc -l <file>を実行し、ファイルが 500 行を超える場合(ゴッドクラスのしきい値)は結果を記録しています - テスト用のモック境界として機能する場合を除き、実装が 1 つしかない新しい抽象化は導入されていません
コメント
Markdown 対応