code-simplification について
AI コーディングエージェント向けのモデル非依存スキルです。動作を維持しながらコードの複雑さを減らし、行数より可読性、プロジェクト規約、段階的な変更とテスト検証を重視します。
主な機能
- 01動作を厳密に維持
- 02プロジェクト規約への準拠
- 03簡潔さより明瞭さを優先
- 04段階的な簡素化とテスト
- 05リファクタリングの範囲を限定
活用シーン
- 01深いネストや長い関数の整理
- 02最近の変更にある重複の確認
- 03時間に追われて書かれたコードのリファクタリング
- 04テストを維持しながら可読性を向上
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ addyosmani/ agent-skills --skill code-simplificationインストール後、Claude Code で/code-simplificationを入力して読み込まれたか確認します。
詳細
コードの簡素化
Claude Code Simplifier プラグインに着想を得ています。ここでは、あらゆる AI コーディングエージェントで使える、モデルに依存しないプロセス駆動型のスキルとして適応しています。
概要
コードの動作を完全に保ちながら、複雑さを軽減します。目標は行数を減らすことではありません。読みやすく、理解しやすく、変更しやすく、デバッグしやすいコードにすることです。すべての簡素化は、次の簡単な基準を満たす必要があります。「新しいチームメンバーは、元のコードよりも早く理解できるでしょうか?」
使用するタイミング
- 機能が動作し、テストも通るものの、実装が必要以上に複雑に感じられるとき
- コードレビューで、可読性や複雑さに関する問題が指摘されたとき
- 深くネストしたロジック、長い関数、不明瞭な名前に遭遇したとき
- 時間に追われて書かれたコードをリファクタリングするとき
- 複数のファイルに散在する関連ロジックをまとめるとき
- 変更をマージした後に、重複や不整合が生じたとき
使用しない場合:
- コードがすでにすっきりしていて読みやすいとき — 簡素化そのものを目的にしてはいけません
- コードの動作をまだ理解していないとき — 簡素化する前に、まず理解してください
- パフォーマンスが重要なコードで、「より簡潔な」バージョンの速度が明らかに遅くなるとき
- モジュール全体を書き換えようとしているとき — 使い捨てのコードを簡素化しても労力の無駄です
5つの原則
1. 動作を完全に維持する
コードの動作を変えず、表現方法だけを変えてください。すべての入力、出力、副作用、エラーの挙動、エッジケースを同じまま維持する必要があります。簡素化によって動作が保たれるか確信できない場合は、変更しないでください。
ASK BEFORE EVERY CHANGE:
→ Does this produce the same output for every input?
→ Does this maintain the same error behavior?
→ Does this preserve the same side effects and ordering?
→ Do all existing tests still pass without modification?
2. プロジェクトの規約に従う
簡素化とは、外部の好みを押し付けることではなく、コードベースとの一貫性を高めることです。簡素化する前に:
1. Read CLAUDE.md / project conventions
2. Study how neighboring code handles similar patterns
3. Match the project's style for:
- Import ordering and module system
- Function declaration style
- Naming conventions
- Error handling patterns
- Type annotation depth
プロジェクトの一貫性を損なう簡素化は、簡素化ではなく、無用な変更です。
3. 小手先の工夫より明快さを優先する
簡潔なコードを読み解くのに一瞬考え込む必要があるなら、明示的なコードのほうが優れています。
// UNCLEAR: Dense ternary chain
const label = isNew ? 'New' : isUpdated ? 'Updated' : isArchived ? 'Archived' : 'Active';
// CLEAR: Readable mapping
function getStatusLabel(item: Item): string {
if (item.isNew) return 'New';
if (item.isUpdated) return 'Updated';
if (item.isArchived) return 'Archived';
return 'Active';
}
// UNCLEAR: Chained reduces with inline logic
const result = items.reduce((acc, item) => ({
...acc,
[item.id]: { ...acc[item.id], count: (acc[item.id]?.count ?? 0) + 1 }
}), {});
// CLEAR: Named intermediate step
const countById = new Map<string, number>();
for (const item of items) {
countById.set(item.id, (countById.get(item.id) ?? 0) + 1);
}
4. バランスを保つ
簡素化には、過剰な簡素化という落とし穴があります。次の点に注意してください。
- 過度にインライン化する — 概念に名前を与えていたヘルパーを削除すると、呼び出し元のコードが読みづらくなります
- 無関係なロジックをまとめる — 2つの単純な関数を1つの複雑な関数にまとめても、簡素化にはなりません
- 「不要な」抽象化を削除する — 複雑さを減らすためではなく、拡張性やテスト容易性のために存在する抽象化もあります
- 行数を減らすことを目指す — 行数の削減は目標ではありません。理解しやすくすることが目標です
5. 変更箇所に範囲を限定する
特に指定がない限り、最近変更されたコードの簡素化を基本とします。対象範囲を広げるよう明示的に依頼されていない限り、無関係なコードへのついでのリファクタリングは避けてください。範囲を限定しない簡素化は差分を不必要に増やし、意図しないリグレッションのリスクを高めます。
簡素化のプロセス
ステップ 1:手を加える前に理解する(チェスタトンの柵)
何かを変更または削除する前に、それが存在する理由を理解してください。これは「チェスタトンの柵」です。道路を横切る柵があり、その理由がわからないなら、取り壊してはいけません。まず理由を理解し、その理由が今も当てはまるか判断してください。
BEFORE SIMPLIFYING, ANSWER:
- What is this code's responsibility?
- What calls it? What does it call?
- What are the edge cases and error paths?
- Are there tests that define the expected behavior?
- Why might it have been written this way? (Performance? Platform constraint? Historical reason?)
- Check git blame: what was the original context for this code?
これらの質問に答えられないなら、簡素化する準備ができていません。まず、さらに背景を調べてください。
ステップ 2:簡素化できる箇所を見つける
次のパターンを確認します。それぞれは曖昧な兆候ではなく、具体的なシグナルです。
構造の複雑さ:
| パターン | シグナル | 簡素化 |
|---|---|---|
| 深いネスト(3 階層以上) | 制御フローを追いにくい | 条件をガード節またはヘルパー関数に切り出す |
| 長い関数(50 行以上) | 複数の責務を持っている | 説明的な名前を付けた、役割を絞った関数に分割する |
| ネストした三項演算子 | 構文の解析に頭を使う | if/else の連鎖、switch、または検索オブジェクトに置き換える |
| 真偽値のパラメータフラグ | doThing(true, false, true) | オプションオブジェクトまたは個別の関数に置き換える |
| 繰り返される条件分岐 | 同じ if チェックが複数箇所にある | 適切な名前を付けた述語関数に切り出す |
命名と読みやすさ:
| パターン | シグナル | 簡素化 |
|---|---|---|
| 汎用的な名前 | data、result、temp、val、item | 内容を説明する名前に変更する:userProfile、validationErrors |
| 省略された名前 | usr、cfg、btn、evt | 省略形が一般的な場合(id、url、api)を除き、完全な語を使う |
| 誤解を招く名前 | 状態も変更するのに get という名前の関数 | 実際の動作を反映する名前に変更する |
| 「何を」を説明するコメント | count++ の上にある // increment counter | コードを見れば明らかなので、コメントを削除する |
| 「なぜ」を説明するコメント | // Retry because the API is flaky under load | コードでは表現できない意図を伝えるため、残す |
冗長性:
| パターン | シグナル | 簡素化 |
|---|---|---|
| ロジックの重複 | 同じ 5 行以上のコードが複数箇所にある | 共通関数に切り出す |
| デッドコード | 到達不能な分岐、未使用の変数、コメントアウトされたブロック | 本当に不要であることを確認してから削除する |
| 不要な抽象化 | 価値を加えていないラッパー | ラッパーをインライン化し、基になる関数を直接呼び出す |
| 過剰に設計されたパターン | ファクトリーのためのファクトリー、戦略が 1 つだけのストラテジー | シンプルで直接的な方法に置き換える |
| 冗長な型アサーション | すでに推論されている型へのキャスト | アサーションを削除する |
ステップ 3:変更を段階的に適用する
一度に 1 つずつ簡素化し、変更のたびにテストを実行します。リファクタリングの変更は、機能追加やバグ修正の変更とは分けて提出します。 リファクタリングと機能追加を行う PR は、2 つの PR に分割します。
FOR EACH SIMPLIFICATION:
1. Make the change
2. Run the test suite
3. If tests pass → commit (or continue to next simplification)
4. If tests fail → revert and reconsider
複数の簡素化を、まとめて 1 つの未テストの変更として行うのは避けます。何かが壊れたとき、どの簡素化が原因かを把握する必要があります。
500 行のルール: リファクタリングで 500 行を超える変更が必要な場合は、手作業ではなく、自動化(codemod、sed スクリプト、AST 変換)に取り組みます。この規模の手作業による編集は、ミスが起きやすく、レビューの負担も大きくなります。
ステップ 4:結果を確認する
すべての簡素化を終えたら、全体を見直して評価します。
COMPARE BEFORE AND AFTER:
- Is the simplified version genuinely easier to understand?
- Did you introduce any new patterns inconsistent with the codebase?
- Is the diff clean and reviewable?
- Would a teammate approve this change?
「簡素化」したバージョンのほうが理解しにくい、またはレビューしにくい場合は、元に戻します。簡素化の試みがすべて成功するとは限りません。
言語ごとのガイダンス
TypeScript / JavaScript
// SIMPLIFY: Unnecessary async wrapper
// Before
async function getUser(id: string): Promise<User> {
return await userService.findById(id);
}
// After
function getUser(id: string): Promise<User> {
return userService.findById(id);
}
// SIMPLIFY: Verbose conditional assignment
// Before
let displayName: string;
if (user.nickname) {
displayName = user.nickname;
} else {
displayName = user.fullName;
}
// After
const displayName = user.nickname || user.fullName;
// SIMPLIFY: Manual array building
// Before
const activeUsers: User[] = [];
for (const user of users) {
if (user.isActive) {
activeUsers.push(user);
}
}
// After
const activeUsers = users.filter((user) => user.isActive);
// SIMPLIFY: Redundant boolean return
// Before
function isValid(input: string): boolean {
if (input.length > 0 && input.length < 100) {
return true;
}
return false;
}
// After
function isValid(input: string): boolean {
return input.length > 0 && input.length < 100;
}
Python
# SIMPLIFY: Verbose dictionary building
# Before
result = {}
for item in items:
result[item.id] = item.name
# After
result = {item.id: item.name for item in items}
# SIMPLIFY: Nested conditionals with early return
# Before
def process(data):
if data is not None:
if data.is_valid():
if data.has_permission():
return do_work(data)
else:
raise PermissionError("No permission")
else:
raise ValueError("Invalid data")
else:
raise TypeError("Data is None")
# After
def process(data):
if data is None:
raise TypeError("Data is None")
if not data.is_valid():
raise ValueError("Invalid data")
if not data.has_permission():
raise PermissionError("No permission")
return do_work(data)
React / JSX
// SIMPLIFY: Verbose conditional rendering
// Before
function UserBadge({ user }: Props) {
if (user.isAdmin) {
return <Badge variant="admin">Admin</Badge>;
} else {
return <Badge variant="default">User</Badge>;
}
}
// After
function UserBadge({ user }: Props) {
const variant = user.isAdmin ? 'admin' : 'default';
const label = user.isAdmin ? 'Admin' : 'User';
return <Badge variant={variant}>{label}</Badge>;
}
// SIMPLIFY: Prop drilling through intermediate components
// Before — consider whether context or composition solves this better.
// This is a judgment call — flag it, don't auto-refactor.
よくある正当化
| 正当化 | 実際 |
|---|---|
| 「動いているのだから、触る必要はない」 | 読みにくいコードは、壊れたときに修正するのも困難です。今シンプルにしておけば、今後の変更のたびに時間を節約できます。 |
| 「行数が少ないほど、常にシンプルだ」 | 1 行のネストされた三項演算子は、5 行の if/else よりシンプルではありません。シンプルさとは行数ではなく、理解する速さです。 |
| 「ついでに、関係ないコードも手早くシンプルにしよう」 | 範囲を定めない単純化は、差分を不必要に大きくし、変更するつもりのなかったコードでリグレッションが起きるリスクを高めます。作業に集中しましょう。 |
| 「型を見れば、自ずと内容がわかる」 | 型が示すのは構造であり、意図ではありません。適切な名前が付いた関数は、型シグネチャよりも「何を」ではなく「なぜ」を明確に説明します。 |
| 「この抽象化は、後で役立つかもしれない」 | まだ使われていない抽象化を、推測で残してはいけません。今使われていないなら、それは価値のない複雑さです。削除し、必要になったときに再び追加しましょう。 |
| 「元の作成者には、何か理由があったはずだ」 | そうかもしれません。git blame を確認し、チェスタトンの柵を意識しましょう。しかし、積み重なった複雑さに理由がないこともよくあります。それは単に、プレッシャーの中で反復作業をした結果、残ったものです。 |
| 「この機能を追加しながら、リファクタリングもしよう」 | リファクタリングと機能開発は分けましょう。変更を混在させると、レビュー、取り消し、履歴の把握が難しくなります。 |
危険信号
- テストに合格させるためにテストの変更が必要な単純化(動作を変えてしまった可能性があります)
- 「シンプルにした」はずなのに、元のコードより長く、追いにくくなったコード
- プロジェクトの規約ではなく、自分の好みに合わせた名前の変更
- 「コードがすっきりする」という理由でエラーハンドリングを削除すること
- 十分に理解していないコードを単純化すること
- 多数の単純化を、レビューしにくい大きなコミットにまとめること
- 依頼されていないのに、現在のタスクの範囲外のコードをリファクタリングすること
検証
単純化の作業を終えたら、次を確認します。
- 既存のテストを変更せずに、すべて合格する
- 新たな警告を発生させずに、ビルドが成功する
- リンター/フォーマッターが通る(スタイルの悪化がない)
- それぞれの単純化が、レビュー可能な段階的変更になっている
- 差分が整理されている。無関係な変更が混在していない
- 単純化したコードがプロジェクトの規約に従っている(CLAUDE.md または同等のものと照合済み)
- エラーハンドリングが削除も弱体化もされていない
- 不要なコードが残っていない(未使用のインポート、到達不能な分岐など)
- チームメイトまたはレビューエージェントが、全体として改善だと判断できる変更になっている