关于 code-simplification
面向 AI 编码 Agent 的模型无关技能,在保持行为不变的前提下降低代码复杂度。它重视可读性、项目规范、渐进修改和测试验证,而非减少行数。
主要功能
- 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 编码 Agent。
概述
通过降低复杂度来简化代码,同时保持行为完全不变。目标不是减少行数,而是让代码更易于阅读、理解、修改和调试。每项简化都必须通过一个简单的检验:“新团队成员能否比理解原代码更快地理解它?”
适用场景
- 功能已实现且测试通过,但实现方式感觉比必要的更繁重时
- 代码审查指出可读性或复杂度问题时
- 遇到深层嵌套逻辑、过长函数或含义不清的名称时
- 重构在时间压力下编写的代码时
- 整合散落在多个文件中的相关逻辑时
- 合并引入重复或不一致问题的更改后
不适用场景:
- 代码已经简洁易读——不要为了简化而简化
- 你还不理解代码的功能——先理解,再简化
- 代码对性能要求很高,而“更简单”的版本会明显更慢
- 你即将彻底重写整个模块——简化注定会被丢弃的代码是在浪费精力
五项原则
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. 保持平衡
简化可能适得其反:过度简化。注意以下陷阱:
- 过度内联——移除一个为概念命名的辅助函数,会让调用处更难理解
- 合并无关逻辑——把两个简单函数合并成一个复杂函数,并不会更简单
- 移除“多余的”抽象——有些抽象是为了扩展性或可测试性,而非降低复杂度
- 以行数为优化目标——目标不是减少行数,而是让代码更容易理解
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 行以上代码 | 提取为共享函数 |
| 死代码 | 无法执行的分支、未使用的变量、已注释掉的代码块 | 删除(确认确实是死代码后) |
| 不必要的抽象 | 没有带来价值的封装器 | 内联封装器,直接调用底层函数 |
| 过度设计的模式 | 工厂的工厂、只有一种策略的策略模式 | 改用简单直接的方式 |
| 多余的类型断言 | 断言为已能推断出的类型 | 删除断言 |
步骤 3:逐步应用更改
一次只做一项简化。每次更改后运行测试。重构更改应与功能或错误修复更改分开提交。 同时进行重构和功能开发的 PR 实际上包含两个 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
避免将多项简化合并为一次未经测试的更改。如果出现问题,你需要知道是哪项简化导致的。
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,并遵循 Chesterton's Fence 原则。但积累下来的复杂性往往并无缘由,只是时间紧迫时反复迭代留下的痕迹。 |
| “我添加这个功能时顺便重构一下” | 将重构与功能开发分开。混合修改更难审查、回滚,也更难从提交历史中理解。 |
危险信号
- 为了通过测试而修改测试的简化(你可能改变了行为)
- “简化后”的代码比原代码更长、更难理解
- 为了符合个人偏好而重命名,而非遵循项目惯例
- 因为“这样代码更整洁”而移除错误处理
- 简化自己并未完全理解的代码
- 将许多简化打包成一个难以审查的大型提交
- 未经要求就重构超出当前任务范围的代码
验证
完成一轮简化后:
- 所有现有测试均在未修改的情况下通过
- 构建成功,且没有新增警告
- Linter/formatter 检查通过(没有引入风格回退)
- 每项简化都是便于审查的渐进式修改
- 差异整洁,没有混入无关修改
- 简化后的代码遵循项目惯例(参照 CLAUDE.md 或同等文件检查)
- 没有移除或弱化任何错误处理
- 没有遗留死代码(未使用的导入、不可达分支)
- 队友或审查 Agent 会认可这项修改带来的整体改进