mcpField
C
code-simplification

code-simplification

github.com/addyosmani开发111.5k64.6k 次安装2 周前更新
代码仓库

关于 code-simplification

面向 AI 编码 Agent 的模型无关技能,在保持行为不变的前提下降低代码复杂度。它重视可读性、项目规范、渐进修改和测试验证,而非减少行数。

主要功能

  1. 01严格保持原有行为
  2. 02遵循项目规范
  3. 03清晰优先于紧凑
  4. 04渐进简化并测试修改
  5. 05将重构限制在任务范围内

适用场景

  1. 01理清深层嵌套或过长的函数
  2. 02检查近期修改中的重复逻辑
  3. 03重构赶工编写的代码
  4. 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 会认可这项修改带来的整体改进

评论

支持 Markdown