关于 clean-code
clean-code 是面向编写、审查和重构代码的开发技能,重点在于可读性与可维护性。它涵盖命名、函数、错误处理、测试和代码异味,并提供评分方法来找出改进方向。
主要功能
- 01表达意图的命名原则
- 02专注型函数设计
- 03注释与格式指导
- 04错误处理实践
- 05单元测试原则
- 06代码异味诊断
适用场景
- 01审查拉取请求的代码清晰度
- 02重构难以阅读的函数
- 03评估测试与错误路径质量
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ wondelai/ skills --skill clean-code安装后在 Claude Code 中输入/clean-code确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/code-smells.mdcomments-formatting.mderror-handling.mdfunctions-and-methods.mdnaming-conventions.mdtesting-principles.md详细介绍
Clean Code Framework
一种严谨的代码编写方法,让代码清晰表达意图、尽量减少意外,并乐于拥抱变化。在编写新代码、审查拉取请求、重构旧系统或提供代码质量建议时,应用这些原则。
核心原则
代码被阅读的次数远多于编写的次数——应以读者为先。 阅读与编写的比例远高于 10:1,因此每个命名选择、函数边界和格式决定,要么提升清晰度,要么增加成本。整洁的代码读起来就像行文优美的文字:名称揭示意图,函数一步步讲述一个故事,并且遵循童子军规则——离开时总要让代码比接手时更整洁。
评分
目标:10/10。 根据以下原则,为任意代码打 0–10 分。报告当前得分,以及达到 10/10 所需的具体改进。
- 9–10: 名称清晰表达意图,函数短小而专注,错误处理一致,测试整洁且全面
- 7–8: 整体整洁,仅有轻微命名歧义或少数过长函数;测试可能缺少边界情况
- 5–6: 优缺点并存——既有良好模式,也存在名称不清、逻辑重复或错误处理不一致的问题
- 3–4: 函数过长且职责繁多,名称具有误导性,测试质量差或缺失
- 1–2: 几乎无法阅读——充斥魔法数字和晦涩缩写,没有结构,也没有测试
Clean Code Framework
六项准则,让代码清晰表达意图并适应变化:
1. 有意义的名称
核心概念: 名称应揭示意图、避免误导,并让代码读起来像行文。如果一个名称需要注释解释,那它就不合适。
为何有效: 名称是最普遍的文档形式——恰当的名称让人无需阅读实现便能理解;糟糕的名称则迫使每位读者反向推断意图。
关键见解:
- 名称应说明它为何存在、做什么以及如何使用
- 不要使用编码、前缀或类型信息(不要使用匈牙利命名法);只有在作用域极小的循环计数器中才使用单字母名称
- 类使用名词;方法使用动词
- 一个概念使用一个词:不要混用
fetch、retrieve和get - 作用域越大,名称就应越长、越具体
- 随时重命名——IDE 让这件事轻而易举
代码应用:
| 上下文 | 模式 | 示例 |
|---|---|---|
| 变量 | 揭示意图 | 用 elapsedTimeInDays,而不是 d |
| 布尔值 | 使用谓语表达 | isActive、hasPermission、canEdit |
| 函数 | 动词 + 名词 | 用 calculateMonthlyRevenue(),而不是 calc() |
| 类 | 以名词命名职责 | 用 InvoiceGenerator,而不是 InvoiceManager |
重命名或审查名称时,请参阅 references/naming-conventions.md——其中包含各语言的约定、易读易搜的命名对照表,以及修改前后的示例。
2. 函数
核心概念: 函数应当小巧、专注于一件事并把它做好——理想情况下为 4–6 行、零到两个参数,以及一个抽象层级。
有效原因: 小巧且用途单一的函数易于命名、理解、测试和复用;过长的函数会隐藏错误、难以测试,还会积累各种职责。
关键要点:
- 逐步深入规则:代码自上而下阅读,每个函数依次调用下一个抽象层级的函数
- 参数数量:零个最佳,一个可以接受,两个尚可,三个及以上需要说明理由
- 标志参数是异味——说明函数做了两件事;应拆分函数
- 命令-查询分离:要么改变状态,要么返回值,不能两者兼做
- 能拆就拆:只要能提取出一个命名函数,就去做
- 不要有隐藏的副作用——函数名必须如实说明全部行为
代码应用:
| 场景 | 模式 | 示例 |
|---|---|---|
| 函数过长 | 提取命名步骤 | validateInput(); transformData(); saveRecord(); |
| 标志参数 | 拆分为两个函数 | renderForPrint() / renderForScreen(),而不是 render(isPrint) |
| 错误情况 | 在开头使用保护子句 | 遇到错误时提前返回,保留单一正常路径 |
| 参数过多 | 引入参数对象 | new DateRange(start, end),而不是 report(start, end, format, locale) |
| 副作用 | 明确说明影响 | checkPassword() 会启动会话 → 重命名或拆分 |
拆分过长函数时,请参阅 references/functions-and-methods.md,了解参数数量规则、命令-查询分离和逐步深入的示例。
3. 注释与格式
核心概念: 注释说明你没能在代码中表达清楚的内容。必要时,注释应解释为什么,而不是是什么。格式能构建清晰的视觉结构,让代码易于浏览。
有效原因: 注释会过时——代码变了,注释却常常没变,最终形成比没有文档更糟糕的文档。整洁的格式让开发者能像读报纸一样浏览代码:先看标题,需要时再看细节。
关键要点:
- 最好的注释是一个命名恰当的提取函数
- 可以接受的注释:法律声明、TODO、公共 API 文档,以及真正解释“为什么”的说明
- 删除注释掉的代码和日志式注释——版本控制会记住这些内容
- 不同概念之间留出垂直空白,概念内部保持垂直紧凑;在变量使用附近声明变量
- 报纸比喻:文件顶部放置高级函数,细节放在下方
代码应用:
| 场景 | 模式 | 示例 |
|---|---|---|
| 解释“是什么” | 换成更好的名称 | // check if eligible → isEligible() |
| 解释“为什么” | 保留为注释 | // RFC 7231 requires this header for proxies |
| 注释掉的代码 | 删除 | 相信版本控制 |
| 团队格式规范 | 一次定好,自动执行 | Prettier、Black、gofmt |
决定一条注释是否值得保留时,请参阅 references/comments-formatting.md,了解好注释与坏注释的示例集和垂直格式规则。
4. 错误处理
核心概念: 错误处理是独立于业务逻辑的一项关注点。使用异常而不是返回码,为每个异常提供上下文,并且绝不返回或传递 null。
为何有效: 返回码会让成功路径充斥检查;异常则能清晰地将两者分开。返回 null 会迫使每个调用方都进行 null 检查,而一处遗漏就会导致错误在距离源头很远的地方发生。
关键要点:
- 先编写 try-catch — 它定义了事务边界
- 优先使用非受检异常 — 受检异常会违反开闭原则
- 根据调用方的需求定义异常类,而不是根据失败类型
- 不要返回 null(使用空集合、Optional 或抛出异常);也不要传递 null
- 特例 / Null Object 模式:返回具有默认行为的对象,而不是 null
代码应用:
| 上下文 | 模式 | 示例 |
|---|---|---|
| 返回 null | 空集合或 Optional | return Collections.emptyList(),而不是 return null |
| 错误码 | 替换为异常 | throw new InsufficientFundsException(balance, amount) |
| 第三方 API | 使用适配器封装 | PortfolioService 封装供应商 API,并转换其异常 |
| 特殊情况 | Null Object 模式 | 使用具有默认行为的 GuestUser,而不是进行 null 检查 |
| 错误上下文 | 包含操作 + 状态 | "Failed to save invoice #1234 for customer 'Acme'" |
设计异常或 null 策略时,请参阅 references/error-handling.md — 其中包含特例模式和第三方 API 封装示例。
5. 单元测试
核心概念: 测试是一等代码,应以与生产代码相同的规范保持整洁。脏乱的测试比没有测试更糟糕 — 它们会成为负担,拖慢每一次变更。
为何有效: 整洁的测试是可执行文档,也是重构的安全网;脏乱的测试会让每次修改都变成与难以理解的测试代码搏斗。
关键要点:
- TDD 三定律:先编写一个失败的测试;只编写足以让测试失败的测试;只编写足以让测试通过的代码
- 每个测试只验证一个概念 — 一个逻辑断言,不一定只有一个 assert
- F.I.R.S.T.:快速(Fast)、独立(Independent)、可重复(Repeatable)、自验证(Self-validating)、及时(Timely)
- 构建领域专用测试语言:让辅助函数读起来像 DSL
- 像重构生产代码一样随时重构测试代码
代码应用:
| 上下文 | 模式 | 示例 |
|---|---|---|
| 测试结构 | Arrange-Act-Assert | 准备、执行、验证 — 清晰分离 |
| 测试命名 | 场景 + 预期行为 | shouldRejectExpiredToken,而不是 test1 |
| 共享设置 | Builder / 工厂辅助函数 | aUser().withRole(ADMIN).build() |
| 不稳定的测试 | 移除外部依赖 | 模拟时间、网络、文件系统 |
编写或清理测试时,请参阅 references/testing-principles.md — 其中详细介绍 TDD 定律、F.I.R.S.T. 原则和整洁测试模式。
6. 代码异味与启发式规则
核心概念: 异味是更深层设计问题的表面迹象——学会快速识别它们,并采取有针对性的重构,而不是含糊地“清理”。
为何有效: 异味是无需深入分析便能指向可能问题的启发式规则,能将代码审查的直觉转化为具体、可重复的行动。
关键要点:
- 函数异味:参数过多、输出参数、标志参数、无用函数
- 常见异味:重复代码、抽象层次不当、特性依恋、魔法数字
- 测试异味:覆盖不足、跳过测试、未测试边界条件和失败路径
- 以小步、经过测试的方式重构——绝不要同时重构和添加功能
- 童子军规则:让代码比你发现它时更整洁
代码应用:
| 场景 | 模式 | 示例 |
|---|---|---|
| 重复代码 | 提取共用逻辑 | 通用验证 → validateEmail() 辅助函数 |
| 特性依恋 | 将方法移至数据所属的类中 | order.calculateTotal() 而不是 calculator.total(order) |
| 无用代码 | 删除 | 移除未使用的函数、不可达的分支 |
| 魔法数字 | 使用命名常量 | MAX_LOGIN_ATTEMPTS = 5,而不是直接写 5 |
| 霰弹式修改 | 整合相关变更 | 将分散的逻辑归并到单个模块中 |
遇到难以命名的异味时,请参阅 references/code-smells.md——其中按类别列出了完整目录,并为每种异味配有针对性的重构方法。
常见错误
| 错误 | 失败原因 | 修正方法 |
|---|---|---|
| 缩写名称 | 写起来省几秒,读起来却要花几小时 | 使用完整且有描述性的名称;IDE 会提供自动补全 |
| “巧妙”的单行代码 | 写起来很炫,调试起来却不可能 | 展开为易读且命名清晰的步骤 |
| 用注释代替重构 | 注释会过时;代码才是真相 | 提取一个命名清晰的函数来代替 |
| 捕获通用异常 | 连同预期错误一起吞掉程序缺陷 | 捕获特定异常;让其他异常继续传播 |
| 错误路径没有测试 | 正常路径可用,边缘情况却会崩溃 | 测试每个分支、边界和失败模式 |
| 过早优化 | 为微小收益掩盖代码意图 | 先写整洁的代码;再优化经测量确认的瓶颈 |
| 上帝类 | 一个 2000 行的类包办所有事情 | 应用 SRP——按职责拆分 |
| 没有测试就重构 | 回归问题没有安全网 | 先编写表征测试 |
| 约定不一致 | 每个文件都像来自不同的代码库 | 统一风格;使用 linter 和格式化工具强制执行 |
| 到处返回 null | 空值检查像病毒一样蔓延 | 使用 Optional、空集合或 Null Object |
快速诊断
| 问题 | 如果否 | 行动 |
|---|---|---|
| 不读函数体也能理解每个函数吗? | 名称没有表达意图 | 重命名,以描述其功能 |
| 所有函数都少于 20 行吗? | 函数做了太多事情 | 将子操作提取为命名清晰的辅助函数 |
| 没有注释掉的代码块吗? | 无用代码造成困惑 | 删除——版本控制中有历史记录 |
| 错误处理与业务逻辑分开了吗? | try-catch 使主流程杂乱 | 提取处理程序;优先使用异常而不是返回码 |
| 每个类都只有单一职责吗? | 类累积了互不相关的职责 | 拆分为职责明确、命名清晰的类 |
| 每个公共方法都有测试吗? | 变更没有安全网 | 继续修改前先添加测试 |
| 测试名称能描述行为吗? | 难以理解失败原因 | 重命名为 shouldDoXWhenY |
| 重复代码不超过 3 处吗? | 复制粘贴会传播缺陷 | 提取共用逻辑(§6) |
| 魔法数字都已命名为常量吗? | 原始值隐藏了意图 | 为常量命名(§6) |
| 所有测试都能在 10 秒内运行完吗? | 慢速测试不会被运行 | 模拟外部依赖;拆分集成测试 |
延伸阅读
基于 Robert C. Martin 关于软件工艺的经典指南:
- 《Clean Code: A Handbook of Agile Software Craftsmanship》,作者 Robert C. Martin
- 《The Clean Coder: A Code of Conduct for Professional Programmers》,作者 Robert C. Martin
- 《Clean Architecture: A Craftsman's Guide to Software Structure and Design》,作者 Robert C. Martin
- 《Refactoring: Improving the Design of Existing Code》,作者 Martin Fowler
关于作者
Robert C. Martin(“Uncle Bob”) 自 1970 年起从事编程工作,是《敏捷宣言》的共同作者,并创立了 Uncle Bob Consulting 和 Clean Coders。他的著作——Clean Code、The Clean Coder、Clean Architecture 和 Clean Agile——塑造了整整一代开发者对代码质量的看法。他的核心主张是,想要加快速度,唯一的办法就是把事情做好。