clean-code

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Clean Code

Clean Code

Principles for transforming "code that works" into "code that is clean" — code that can be read, understood, and enhanced by any developer.
"Code is clean if it can be understood easily — by everyone on the team." — Dave Thomas
本指南提供将「能运行的代码」转化为「整洁代码」的原则——这类代码可以被任何开发人员阅读、理解和改进。
「如果团队中的每个人都能轻松理解,那这段代码就是整洁的。」—— Dave Thomas

When to Apply

适用场景

Reference these guidelines when:
  • Writing new code and choosing names, function signatures, structure
  • Reviewing pull requests for readability and maintainability
  • Refactoring legacy code or reducing complexity
  • Identifying and fixing code smells
  • Improving team code standards
在以下场景中可参考本指南:
  • 编写新代码时,确定命名、函数签名与结构
  • 评审拉取请求,检查代码的可读性与可维护性
  • 重构遗留代码或降低代码复杂度
  • 识别并修复代码坏味道
  • 完善团队代码规范

Rule Categories

规则分类

PriorityCategoryImpact
1NamingHIGH — affects every line of code
2FunctionsHIGH — core unit of abstraction
3Code SmellsHIGH — early detection prevents rot
4FormattingMEDIUM — readability at a glance
5Error HandlingMEDIUM — robustness and clarity
6CommentsMEDIUM — most are avoidable
7Object CalisthenicsASPIRATIONAL — exercises for better OO design
优先级类别影响程度
1命名高影响 — 影响每一行代码
2函数高影响 — 抽象的核心单元
3代码坏味道高影响 — 早期检测可防止代码腐化
4格式规范中影响 — 一眼可见的可读性
5错误处理中影响 — 健壮性与清晰度
6注释中影响 — 大多数注释可避免
7Object Calisthenics进阶目标 — 优化面向对象设计的练习

1. Naming

1. 命名

Good names are the single most impactful thing you can do for readability.
Priority order:
  1. Consistency — same concept = same name everywhere
  2. Intent-revealing — name says what it does, not how
  3. Specific — avoid vague names like
    data
    ,
    info
    ,
    manager
    ,
    handler
    ,
    utils
  4. Searchable — unique enough to grep
  5. Brief — short but not cryptic
typescript
// 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) {}
Conventions:
  • Classes/types: nouns (
    Customer
    ,
    OrderRepository
    ). Avoid
    Manager
    ,
    Data
    ,
    Info
    .
  • Methods/functions: verbs (
    createOrder
    ,
    validateEmail
    ,
    isEligible
    )
  • Booleans: question form (
    isActive
    ,
    hasPermission
    ,
    canWithdraw
    )
  • Collections: plural nouns (
    users
    ,
    orderItems
    )
See
references/NAMING.md
for full guidelines.
好的命名是提升代码可读性最有效的手段。
优先级顺序:
  1. 一致性 — 同一概念在所有地方使用相同名称
  2. 传达意图 — 名称说明用途,而非实现方式
  3. 具体明确 — 避免模糊的名称,如
    data
    info
    manager
    handler
    utils
  4. 便于搜索 — 名称足够独特,可通过grep查找
  5. 简洁精炼 — 简短但不晦涩
typescript
// 不好的写法
const d = new Date();
const arr = users.filter((u) => u.a);
function process(data: any) {}

// 好的写法
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. Functions

2. 函数

typescript
// 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> { ... }
Rules:
  • Small — strive for under 20 lines
  • Do one thing — if you use "and" to describe it, split it
  • One level of abstraction — don't mix business logic with low-level details
  • Few arguments — 0-2 ideal, 3+ warrants a parameter object
  • No side effects — or name them explicitly (
    saveAndNotify
    , not
    save
    )
  • Command/Query separation — a function either does something or returns something, not both
typescript
// 不好的写法 — 职责过多,名称模糊
function handle(order: Order, sendEmail: boolean, log: boolean) {
  // 验证、计算、保存、发送邮件、记录日志 — 全部放在一个函数里
}

// 好的写法 — 小巧、单一职责、描述清晰
function validateOrder(order: Order): ValidationResult { ... }
function calculateTotal(items: OrderItem[]): Money { ... }
function saveOrder(order: Order): Promise<void> { ... }
规则:
  • 小巧 — 尽量控制在20行以内
  • 单一职责 — 如果描述时需要用「和」,就拆分函数
  • 单一抽象层级 — 不要混合业务逻辑与底层细节
  • 参数少 — 0-2个参数为最佳,3个及以上建议使用参数对象
  • 无副作用 — 如有副作用需在名称中明确体现(如
    saveAndNotify
    ,而非
    save
  • 命令/查询分离 — 函数要么执行操作,要么返回结果,不可两者兼具

3. Code Smells

3. 代码坏味道

Indicators that code may need refactoring. Not bugs, but design friction.
SmellSymptomQuick Fix
Long Method> 20 lines, multiple concernsExtract methods
Large ClassMany responsibilitiesExtract class (SRP)
Long Parameter List> 3 parametersIntroduce parameter object
Primitive ObsessionStrings/numbers for domain conceptsWrap in value objects
Feature EnvyMethod uses another class's data more than its ownMove method
Data ClumpsSame group of fields appear togetherExtract class
Switch StatementsType-checking switch/if-else across codebaseReplace with polymorphism
Divergent ChangeOne class changed for many reasonsSplit by responsibility
Shotgun SurgeryOne change touches many filesMove related code together
Speculative Generality"Just in case" abstractionsDelete (YAGNI)
Dead CodeUnreachable or unused codeDelete
Message Chains
a.getB().getC().doSomething()
Hide delegate (Law of Demeter)
See
references/CODE_SMELLS.md
for detailed examples and refactoring strategies.
代码坏味道是代码需要重构的信号,并非bug,但会增加设计阻力。
代码坏味道表现特征快速修复方案
过长方法超过20行,包含多个职责提取方法
过大类承担多个职责提取类(单一职责原则)
过长参数列表参数数量超过3个引入参数对象
基本类型偏执使用字符串/数字表示领域概念封装为值对象
特性依恋方法使用其他类的数据远多于自身类的数据移动方法
数据泥团同一组字段反复一起出现提取类
分支语句泛滥代码库中大量存在基于类型检查的switch/if-else用多态替代
发散式变化一个类因多种原因被修改按职责拆分类
霰弹式修改一次修改需要改动多个文件将相关代码整合到一起
臆想性通用性为「以防万一」添加的抽象删除(你不会需要它原则YAGNI)
死代码无法到达或未被使用的代码删除
消息链
a.getB().getC().doSomething()
隐藏委托(迪米特法则)
详细示例与重构策略请参考
references/CODE_SMELLS.md

4. Formatting

4. 格式规范

The Newspaper Metaphor — code should read top-to-bottom like a newspaper article. High-level summary at the top, details below.
typescript
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 { ... }
}
Rules:
  • Related code stays close together (vertical density)
  • Blank lines between concepts (vertical openness)
  • Variables declared near their usage
  • Caller above callee (stepdown rule)
  • Consistent indentation — non-negotiable
报纸隐喻 — 代码应像报纸一样从上到下阅读,顶部是高层摘要,下方是细节。
typescript
class OrderProcessor {
  // 先放公共API — 相当于「标题」
  process(order: Order): ProcessResult {
    this.validate(order);
    const total = this.calculateTotal(order);
    return this.save(order, total);
  }

  // 下方是支撑方法,按出现顺序排列
  private validate(order: Order) { ... }
  private calculateTotal(order: Order): Money { ... }
  private save(order: Order, total: Money): ProcessResult { ... }
}
规则:
  • 相关代码放在一起(垂直密度)
  • 不同概念之间用空行分隔(垂直留白)
  • 变量声明在使用位置附近
  • 调用方在被调用方上方(向下规则)
  • 一致的缩进 — 必须严格遵守

5. Error Handling

5. 错误处理

  • Exceptions over error codes — keeps happy path clean
  • Don't return
    null
    — use
    undefined
    ,
    Result
    types, or throw
  • Don't pass
    null
    — leads to defensive checks everywhere
  • Fail fast — validate at boundaries, trust internals
  • Specific exceptions
    InsufficientFundsError
    over generic
    Error
typescript
// 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;
}
  • 优先使用异常而非错误码 — 保持主逻辑清晰
  • 不要返回
    null
    — 使用
    undefined
    Result
    类型或抛出异常
  • 不要传递
    null
    — 会导致到处都是防御性检查
  • 快速失败 — 在边界处验证,内部逻辑可信任
  • 使用具体异常 — 用
    InsufficientFundsError
    替代通用的
    Error
typescript
// 不好的写法 — null检查在代码库中层层蔓延
function getUser(id: string): User | null {
  return db.find(id);
}
const user = getUser(id);
if (user === null) { ... } // 每个调用者都必须检查

// 好的写法 — 在边界处抛出异常,领域内部可信任
function getUser(id: string): User {
  const user = db.find(id);
  if (!user) throw new UserNotFoundError(id);
  return user;
}

6. Comments

6. 注释

"Don't comment bad code — rewrite it."
Most comments compensate for failure to express intent in code. Prefer self-documenting code over comments.
Good comments:
  • Why something is done (business reason, non-obvious decision)
  • Warnings ("this is slow because X", "order matters here")
  • TODOs with context (link to issue)
  • Legal/license headers
  • Public API docs (JSDoc for libraries)
Bad comments:
  • Restating what the code does (
    // increment counter
    )
  • Commented-out code (that's what git is for)
  • Journal/changelog comments
  • Noise (
    // constructor
    ,
    // getters
    )
  • Mandated boilerplate
typescript
// 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) { ... }
「不要给糟糕的代码加注释——重写它。」
大多数注释是为了弥补代码无法传达意图的缺陷。优先使用自文档化代码,而非注释。
好的注释:
  • 原因 — 说明为什么这么做(业务规则、非显而易见的决策)
  • 警告 — (如「此处因X原因运行缓慢」、「顺序很重要」)
  • 待办事项 — 附带上下文(关联到问题链接)
  • 法律/许可证 头部
  • 公共API文档 — (库的JSDoc注释)
坏的注释:
  • 重复代码的功能(如
    // 递增计数器
  • 被注释掉的代码(用git记录历史即可)
  • 日志/变更记录注释
  • 无意义的注释(如
    // 构造函数
    // getter方法
  • 强制要求的模板注释
typescript
// 不好的写法 — 重复显而易见的内容
// 检查用户是否活跃
if (user.isActive) { ... }

// 好的写法 — 解释非显而易见的业务规则
// 根据合规要求GDPR-2024-42,30天内未验证邮箱的用户会被自动停用
if (user.isAutoDeactivated) { ... }

7. Object Calisthenics

7. Object Calisthenics

Nine exercises from Jeff Bay to improve OO design. Treat these as aspirational targets — strict during practice, pragmatic in production.
#RuleGoal
1One level of indentation per methodExtract methods aggressively
2Don't use
else
Early returns, guard clauses, polymorphism
3Wrap all primitives with domain meaningValue objects (
Email
,
Money
,
UserId
)
4First-class collectionsWrap arrays in domain-specific classes
5One dot per lineLaw of Demeter — talk to friends only
6Don't abbreviateIf the name is too long, the class does too much
7Keep entities smallClasses < 50 lines, methods < 10 lines
8Limit instance variablesStrive for 2-3; forces focused classes
9No getters/settersObjects have behavior, not just data
See
references/OBJECT_CALISTHENICS.md
for examples of each rule.
这是Jeff Bay提出的9项练习,用于优化面向对象设计。将这些视为进阶目标——练习时严格遵守,生产环境中灵活运用。
编号规则目标
1每个方法只允许一层缩进积极提取方法
2不要使用
else
使用提前返回、守卫子句、多态
3封装所有具有领域意义的基本类型使用值对象(
Email
Money
UserId
4一等集合将数组封装为领域特定类
5每行最多一个点迪米特法则——只和朋友对话
6不要缩写如果名称太长,说明类的职责过多
7保持实体小巧类少于50行,方法少于10行
8限制实例变量数量尽量控制在2-3个;促使类职责聚焦
9不要使用getter/setter对象应具备行为,而非仅存储数据
每个规则的示例请参考
references/OBJECT_CALISTHENICS.md

Implementation Checklist

实施检查清单

Before submitting code:
  • Can a new team member understand this without asking questions?
  • Are names intention-revealing and consistent?
  • Does each function do exactly one thing?
  • Are there any code smells I can fix while I'm here?
  • Are comments explaining why, not what?
  • Is error handling clean and specific?
  • Did I leave the code better than I found it? (Boy Scout Rule)
提交代码前,请确认:
  • 新团队成员无需提问就能理解这段代码吗?
  • 命名是否能清晰传达意图且保持一致?
  • 每个函数是否只负责一件事?
  • 我能否顺便修复一些代码坏味道?
  • 注释是否在解释「为什么」而非「是什么」?
  • 错误处理是否简洁且明确?
  • 我是否让代码比我接手时更优秀?(童子军规则)