关于 clean-code
面向开发者的整洁代码参考技能,适用于编写、审查和重构代码。内容围绕可读性与可维护性,提供原则、代码异味识别及提交前检查。
主要功能
- 01命名与函数设计
- 02识别代码异味
- 03格式与错误处理
- 04注释编写指引
- 05面向对象练习
- 06提交前检查清单
适用场景
- 01选择清晰的名称和函数结构
- 02审查代码以改善可维护性
- 03重构旧代码并降低复杂度
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ marcioaltoe/ skills --skill clean-code安装后在 Claude Code 中输入/clean-code确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
agents/openai.yamlreferences/CODE_SMELLS.mdNAMING.mdOBJECT_CALISTHENICS.md详细介绍
清晰代码
将“能运行的代码”转变为“清晰代码”的原则——让任何开发者都能阅读、理解并改进代码。
“如果团队中的每个人都能轻松理解代码,那么代码就是清晰的。”——Dave Thomas
适用场景
在以下场景中参考这些指南:
- 编写新代码并选择名称、函数签名和结构
- 审查拉取请求,检查可读性和可维护性
- 重构遗留代码或降低复杂度
- 识别并修复代码异味
- 改进团队代码规范
规则类别
| 优先级 | 类别 | 影响 |
|---|---|---|
| 1 | 命名 | 高 — 影响每一行代码 |
| 2 | 函数 | 高 — 抽象的核心单元 |
| 3 | 代码异味 | 高 — 尽早发现可防止代码腐化 |
| 4 | 格式 | 中 — 让代码一目了然 |
| 5 | 错误处理 | 中 — 提高健壮性和清晰度 |
| 6 | 注释 | 中 — 大多数注释都可以避免 |
| 7 | 对象柔术 | 进阶目标 — 改进面向对象设计的练习 |
1. 命名
好的名称是提升可读性最有效的方法。
优先顺序:
- 一致性 — 同一概念在任何地方都使用同一名称
- 揭示意图 — 名称说明它做什么,而不是怎么做
- 具体明确 — 避免使用
data、info、manager、handler、utils等含糊的名称 - 便于搜索 — 名称要足够独特,方便使用 grep 搜索
- 简洁 — 简短但不晦涩
// Bad
const d = new Date();
const arr = users.filter((u) => u.a);
function process(data: any) {}
// Good
const createdAt = new Date();
const activeUsers = users.filter((user) => user.isActive);
function validatePayment(payment: Payment) {}
命名约定:
- 类/类型:使用名词(
Customer、OrderRepository)。避免使用Manager、Data、Info。 - 方法/函数:使用动词(
createOrder、validateEmail、isEligible) - 布尔值:使用疑问形式(
isActive、hasPermission、canWithdraw) - 集合:使用复数名词(
users、orderItems)
完整指南请参阅 references/NAMING.md。
2. 函数
// Bad — does too many things, unclear name
function handle(order: Order, sendEmail: boolean, log: boolean) {
// validate, calculate, save, email, log — all in one
}
// Good — small, single-purpose, descriptive
function validateOrder(order: Order): ValidationResult { ... }
function calculateTotal(items: OrderItem[]): Money { ... }
function saveOrder(order: Order): Promise<void> { ... }
规则:
- 简短 — 尽量控制在 20 行以内
- 只做一件事 — 如果需要用“并且”来描述它,就将其拆分
- 单一抽象层级 — 不要混合业务逻辑与底层细节
- 参数尽量少 — 理想情况下为 0 至 2 个,3 个及以上时应考虑使用参数对象
- 没有副作用 — 或在名称中明确体现(使用
saveAndNotify,而不是save) - 命令/查询分离 — 函数要么执行操作,要么返回结果,不能两者兼顾
3. 代码异味
表明代码可能需要重构的迹象。它们不是 bug,而是设计上的阻碍。
| 异味 | 症状 | 快速修复 |
|---|---|---|
| 过长方法 | 超过 20 行,包含多个关注点 | 提取方法 |
| 过大类 | 职责过多 | 提取类(SRP) |
| 参数列表过长 | 参数超过 3 个 | 引入参数对象 |
| 基本类型偏执 | 使用字符串/数字表示领域概念 | 封装为值对象 |
| 依恋情结 | 方法使用其他类的数据多于自身数据 | 移动方法 |
| 数据泥团 | 同一组字段总是一起出现 | 提取类 |
| Switch 语句 | 在代码库中使用 switch/if-else 检查类型 | 用多态替代 |
| 发散式变化 | 一个类因多种原因而修改 | 按职责拆分 |
| 霰弹式修改 | 一项修改影响多个文件 | 将相关代码集中在一起 |
| 投机性通用化 | “以防万一”而添加的抽象 | 删除(YAGNI) |
| 死代码 | 无法到达或未使用的代码 | 删除 |
| 消息链 | a.getB().getC().doSomething() | 隐藏委托(迪米特法则) |
详细示例和重构策略请参阅 references/CODE_SMELLS.md。
4. 格式
报纸隐喻 — 代码应像报纸文章一样从上到下阅读。顶部先给出高层摘要,下面再呈现细节。
class OrderProcessor {
// Public API first — the "headline"
process(order: Order): ProcessResult {
this.validate(order);
const total = this.calculateTotal(order);
return this.save(order, total);
}
// Supporting methods below, in order of appearance
private validate(order: Order) { ... }
private calculateTotal(order: Order): Money { ... }
private save(order: Order, total: Money): ProcessResult { ... }
}
规则:
- 相关代码应放在一起(垂直紧凑)
- 不同概念之间留空行(垂直疏朗)
- 变量应在靠近其使用位置的地方声明
- 调用方放在被调用方上方(逐步下降规则)
- 缩进保持一致 — 不容妥协
5. 错误处理
- 优先使用异常,而非错误代码 — 让正常流程保持简洁
- 不要返回
null— 使用undefined、Result类型或抛出异常 - 不要传入
null— 否则到处都需要防御性检查 - 尽早失败 — 在边界处验证,信任内部代码
- 使用具体的异常 — 优先使用
InsufficientFundsError,而非通用的Error
// Bad — null checks cascade through codebase
function getUser(id: string): User | null {
return db.find(id);
}
const user = getUser(id);
if (user === null) { ... } // Every caller must check
// Good — throw at boundary, trust within domain
function getUser(id: string): User {
const user = db.find(id);
if (!user) throw new UserNotFoundError(id);
return user;
}
6. 注释
“不要注释糟糕的代码——重写它。”
大多数注释都是为了弥补代码未能表达意图。应优先编写自解释的代码,而非添加注释。
好的注释:
- 解释为什么要这样做(业务原因、非显而易见的决策)
- 警告(“由于 X,这段代码运行缓慢”“这里的顺序很重要”)
- 附带上下文的 TODO(链接到问题)
- 法律/许可证声明
- 公共 API 文档(库的 JSDoc)
不好的注释:
- 复述代码的作用(
// increment counter) - 注释掉的代码(这正是 git 的用途)
- 日志/变更记录注释
- 无意义内容(
// constructor、// getters) - 强制要求的样板内容
// Bad — restates the obvious
// Check if user is active
if (user.isActive) { ... }
// Good — explains a non-obvious business rule
// Users who haven't verified email within 30 days are auto-deactivated
// per compliance requirement GDPR-2024-42
if (user.isAutoDeactivated) { ... }
7. 对象体操
Jeff Bay 提出的九项练习,用于改进面向对象设计。将其视为理想目标——练习时严格遵守,在生产环境中灵活运用。
| # | 规则 | 目标 |
|---|---|---|
| 1 | 每个方法最多一层缩进 | 积极拆分方法 |
| 2 | 不要使用 else | 尽早返回、使用守卫子句和多态 |
| 3 | 为所有具有领域含义的基本类型添加封装 | 使用值对象(Email、Money、UserId) |
| 4 | 使用一等集合 | 用领域专用类封装数组 |
| 5 | 每行只出现一个点 | 遵循迪米特法则——只与熟悉的对象交互 |
| 6 | 不要使用缩写 | 如果名称太长,说明类承担了太多职责 |
| 7 | 保持实体精简 | 类少于 50 行,方法少于 10 行 |
| 8 | 限制实例变量数量 | 力求控制在 2–3 个;这会促使类职责聚焦 |
| 9 | 不使用 getter/setter | 对象应有行为,而不只是数据 |
每条规则的示例请参阅 references/OBJECT_CALISTHENICS.md。
实现检查清单
提交代码前:
- 新团队成员能否在不提问的情况下理解这段代码?
- 名称是否清晰表达意图且保持一致?
- 每个函数是否只做一件事?
- 是否有我可以顺手修复的代码异味?
- 注释是否解释了为什么,而不是做什么?
- 错误处理是否简洁且具体?
- 我是否让代码比接手时更好?(童子军规则)
评论
支持 Markdown