clean-code

clean-code

热门

通过规范的命名、短小的函数和清晰的错误处理,编写可读、可维护的代码。当用户提到“清理这段代码”、“这个函数太长了”、“代码坏味道”、“命名规范”、“童子军规则”、“单一职责”或“单元测试质量”时使用。在审查拉取请求的可读性、梳理混乱的函数、讨论注释风格或改进错误处理模式时也会触发。涵盖SRP、注释纪律、格式化和单元测试。有关重构技术,请参见refactoring-patterns。有关架构和依赖规则,请参见clean-architecture。

1698Star
171Fork
更新于 2026/7/16
SKILL.md
readonly只读
name
clean-code
description

通过规范的命名、短小的函数和清晰的错误处理,编写可读、可维护的代码。当用户提到“清理这段代码”、“这个函数太长了”、“代码坏味道”、“命名规范”、“童子军规则”、“单一职责”或“单元测试质量”时使用。在审查拉取请求的可读性、梳理混乱的函数、讨论注释风格或改进错误处理模式时也会触发。涵盖SRP、注释纪律、格式化和单元测试。有关重构技术,请参见refactoring-patterns。有关架构和依赖规则,请参见clean-architecture。

整洁代码框架

一种严谨的代码编写方法,旨在传达意图、减少意外并欢迎变更。在编写新代码、审查拉取请求、重构遗留系统或就代码质量提供建议时应用这些原则。

核心原则

代码被阅读的次数远多于编写的次数——为读者优化。 阅读与编写的比例远超10:1,因此每个命名选择、函数边界和格式化决策要么增加清晰度,要么增加成本。整洁代码读起来像精心编写的散文:名称揭示意图,函数一步步讲述故事,并遵循童子军规则——始终让代码比你发现时更整洁。

评分

目标:10/10。 根据以下原则对任何代码进行0-10评分。报告当前分数以及达到10/10所需的具体改进。

  • 9-10: 名称揭示意图,函数短小且专注,错误处理一致,测试整洁且全面
  • 7-8: 基本整洁,但存在轻微命名歧义或少数长函数;测试可能缺少边界情况
  • 5-6: 混合——良好模式与不清晰名称、重复逻辑或不一致错误处理并存
  • 3-4: 长而多用途的函数,误导性名称,测试差或缺失
  • 1-2: 几乎不可读——魔法数字、晦涩缩写、无结构、无测试

整洁代码框架

编写清晰沟通并适应变更的代码的六项纪律:

1. 有意义的命名

核心概念: 名称应揭示意图,避免误导信息,并使代码读起来像散文。如果名称需要注释来解释,那么名称就是错误的。

为什么有效: 名称是最普遍的文档形式——精心选择的名称消除了阅读实现的需要;糟糕的名称迫使每个读者逆向工程意图。

关键见解:

  • 名称应回答它为什么存在、做什么以及如何使用
  • 没有编码、前缀或类型信息(没有匈牙利命名法);单字母仅用于极小作用域的循环计数器
  • 类是名词;方法是动词
  • 每个概念一个词:不要混用fetchretrieveget
  • 作用域越大,名称应越长、越具描述性
  • 自由重命名——IDE使其变得简单

代码应用:

上下文 模式 示例
变量 意图揭示 elapsedTimeInDays而不是d
布尔值 谓词短语 isActivehasPermissioncanEdit
函数 动词+名词 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 eligibleisEligible()
解释“为什么” 保留为注释 // RFC 7231 requires this header for proxies
注释掉的代码 删除它 信任版本控制
团队格式化 决定一次,自动化 Prettier、Black、gofmt

决定注释是否值得保留时,请参见references/comments-formatting.md——好与坏注释目录和垂直格式化规则。

4. 错误处理

核心概念: 错误处理是与业务逻辑分离的关注点。使用异常而不是返回码,为每个异常提供上下文,并且绝不返回或传递null。

为什么有效: 返回码用检查污染快乐路径;异常将两者干净地分离。返回null强制每个调用者进行null检查,一个遗漏的检查会在远离源头的地方崩溃。

关键见解:

  • 先写try-catch——它定义了一个事务边界
  • 优先使用非受检异常——受检异常违反开闭原则
  • 根据调用者的需求定义异常类,而不是根据失败类型
  • 不要返回null(使用空集合、Optional或抛出);也不要传递null
  • 特例/空对象模式:返回具有默认行为的对象而不是null

代码应用:

上下文 模式 示例
null返回 空集合或Optional return Collections.emptyList()而不是return null
错误码 替换为异常 throw new InsufficientFundsException(balance, amount)
第三方API 用适配器包装 PortfolioService包装供应商API,翻译其异常
特例 空对象模式 GuestUser具有默认行为而不是null检查
错误中的上下文 包含操作+状态 "Failed to save invoice #1234 for customer 'Acme'"

设计异常或null策略时,请参见references/error-handling.md——特例模式和第三方API包装示例。

5. 单元测试

核心概念: 测试是一等代码,与生产代码一样保持整洁。脏测试比没有测试更糟糕——它们成为拖慢每次变更的负债。

为什么有效: 整洁测试是可执行的文档和重构的安全网;脏测试使每次修改都成为与难以理解的测试代码的斗争。

关键见解:

  • TDD三定律:先写一个失败的测试;只写足以失败的测试;只写足以通过的代码
  • 每个测试一个概念——一个逻辑断言,不一定是单个assert
  • F.I.R.S.T.:快速、独立、可重复、自我验证、及时
  • 构建领域特定测试语言:像DSL一样可读的辅助函数
  • 像重构生产代码一样重构测试代码

代码应用:

上下文 模式 示例
测试结构 安排-执行-断言 设置、执行、验证——清晰分离
测试命名 场景+预期行为 shouldRejectExpiredToken而不是test1
共享设置 构建器/工厂辅助函数 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 null检查像病毒一样传播 Optional、空集合或空对象

快速诊断

问题 如果否 行动
你能在不阅读函数体的情况下理解每个函数吗? 名称未揭示意图 重命名以描述其功能
所有函数都在20行以下吗? 函数做了太多事 将子操作提取为命名辅助函数
零个注释掉的代码块? 死代码造成混乱 删除——版本控制有历史
错误处理是否与业务逻辑分离? try-catch混乱主流程 提取处理程序;异常优于返回码
每个类是否只有一个职责? 类积累不相关的职责 拆分为专注、命名良好的类
每个公共方法是否有测试? 没有变更安全网 在进一步更改前添加测试
测试名称是否描述行为? 失败难以解释 重命名为shouldDoXWhenY
重复是否低于3次? 复制粘贴传播错误 提取共享逻辑(§6)
魔法数字是否是命名常量? 意图隐藏在原始值后面 命名常量(§6)
所有测试是否在10秒内运行? 慢测试不会被运行 模拟外部依赖;拆分集成测试

进一步阅读

基于Robert C. Martin关于软件工艺的开创性指南:

关于作者

Robert C. Martin("Uncle Bob") 自1970年开始编程,是敏捷宣言的合著者,并创立了Uncle Bob Consulting和Clean Coders。他的著作——《整洁代码》、《程序员的职业素养》、《整洁架构》和《整洁敏捷》——塑造了一代开发者对代码质量的思考方式,他的核心立场是:要想快,唯一的方法是做好。