swe
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseSoftware Engineering Best Practices
软件工程最佳实践
A comprehensive guide to writing maintainable, scalable, and high-quality software, organized by development phase.
一份按开发阶段组织的、关于编写可维护、可扩展且高质量软件的综合指南。
Key Principles Summary
核心原则摘要
- Simplicity wins: Write the simplest code that works; add complexity only when required
- Single responsibility: Each function/class/module should do one thing well
- Self-documenting code: Code should explain itself; comments are a code smell
- Fail fast: Validate inputs early, let unexpected errors propagate
- Test behavior: Focus on what code does, not implementation details
- No backwards compatibility: Don't add legacy support unless explicitly requested
- Consistency: Match existing project conventions over personal preference
- Simplicity wins: 编写能正常运行的最简代码;仅在必要时增加复杂度
- Single responsibility: 每个函数/类/模块应只做好一件事
- Self-documenting code: 代码应自我解释;注释是代码异味的表现
- Fail fast: 尽早验证输入,让意外错误向上传播
- Test behavior: 关注代码的功能,而非实现细节
- No backwards compatibility: 除非明确要求,否则不要添加遗留支持
- Consistency: 遵循现有项目约定,而非个人偏好
Phase 1: Design
第一阶段:设计
Extended guidelines for architecture, patterns, and advanced design topics.
关于架构、模式和高级设计主题的扩展指南。
Advanced Principles
进阶原则
Single Responsibility
Single Responsibility
Each function/class/module should do ONE thing well.
❌ MULTIPLE RESPONSIBILITIES ✅ SINGLE RESPONSIBILITY
─────────────────────────────────────────────────────
function processUser(user) { function validateUser(user) { }
validate(user); function saveUser(user) { }
save(user); function sendWelcomeEmail(user) { }
sendEmail(user); function logUserCreation(user) { }
logActivity(user);
} // Compose in callerApply at every level:
- Functions: Do one operation
- Classes: Handle one concept
- Modules: Own one domain
Analyze responsibilities:
- Find callees of a function to see what it does
- Find callers of a function to see who depends on it
每个函数/类/模块应只做好一件事。
❌ MULTIPLE RESPONSIBILITIES ✅ SINGLE RESPONSIBILITY
─────────────────────────────────────────────────────
function processUser(user) { function validateUser(user) { }
validate(user); function saveUser(user) { }
save(user); function sendWelcomeEmail(user) { }
sendEmail(user); function logUserCreation(user) { }
logActivity(user);
} // Compose in caller在每个层级应用:
- 函数:执行单一操作
- 类:处理单一概念
- 模块:负责单一领域
分析职责:
- 查看函数的被调用方,了解它的实际功能
- 查看函数的调用方,了解谁依赖它
The Rule of Three
The Rule of Three
Duplicate code twice before extracting. Premature abstraction is worse than duplication.
- Use duplication detection to find blocks duplicated 3+ times
重复代码出现三次后再提取抽象。过早的抽象比重复代码更糟糕。
- 使用重复代码检测工具查找重复3次及以上的代码块
Avoid Backwards Compatibility
Avoid Backwards Compatibility
Don't introduce backwards compatibility unless explicitly requested.
- Delete deprecated code, don't mark it
- Don't write shims or polyfills unless asked
- Don't maintain multiple code paths for old/new
- If refactoring, replace entirely
Find dead code - unused files, exports, and dependencies that can be safely deleted.
除非明确要求,否则不要引入向后兼容性。
- 删除已废弃的代码,不要只是标记
- 除非被要求,否则不要编写shims或polyfills
- 不要同时维护新旧代码路径
- 如果进行重构,直接替换旧代码
查找死代码 - 可以安全删除的未使用文件、导出项和依赖项。
Feature Flags Are a Code Smell
Feature Flags Are a Code Smell
typescript
// ❌ FEATURE FLAGS: Hidden complexity
if (featureFlags.newCheckout) {
await newCheckoutFlow(cart);
} else {
await legacyCheckoutFlow(cart);
}
// ✅ SIMPLE: One code path
await checkoutFlow(cart);typescript
// ❌ FEATURE FLAGS: Hidden complexity
if (featureFlags.newCheckout) {
await newCheckoutFlow(cart);
} else {
await legacyCheckoutFlow(cart);
}
// ✅ SIMPLE: One code path
await checkoutFlow(cart);Architecture
架构
Layered Architecture
Layered Architecture
┌─────────────────────────────────────────────────────┐
│ Presentation Layer │ UI, API Controllers
├─────────────────────────────────────────────────────┤
│ Application Layer │ Use Cases, Orchestration
├─────────────────────────────────────────────────────┤
│ Domain Layer │ Business Logic, Entities
├─────────────────────────────────────────────────────┤
│ Infrastructure Layer │ Database, External Services
└─────────────────────────────────────────────────────┘
Rule: Dependencies point downward only┌─────────────────────────────────────────────────────┐
│ Presentation Layer │ UI、API控制器
├─────────────────────────────────────────────────────┤
│ Application Layer │ 用例、编排逻辑
├─────────────────────────────────────────────────────┤
│ Domain Layer │ 业务逻辑、实体
├─────────────────────────────────────────────────────┤
│ Infrastructure Layer │ 数据库、外部服务
└─────────────────────────────────────────────────────┘
Rule: Dependencies point downward onlyDependency Direction
Dependency Direction
✅ GOOD ❌ BAD
─────────────────────────────────────────────────────
Domain → nothing Domain → Infrastructure
Application → Domain UI → Database directly
Infrastructure → Domain Circular dependenciesAnalyze dependencies:
- Check for circular dependencies between modules
- Ensure domain doesn't depend on infrastructure
✅ GOOD ❌ BAD
─────────────────────────────────────────────────────
Domain → nothing Domain → Infrastructure
Application → Domain UI → Database directly
Infrastructure → Domain Circular dependencies分析依赖关系:
- 检查模块之间的循环依赖
- 确保领域层不依赖基础设施层
Design Patterns
Design Patterns
Use patterns when they solve a real problem, not preemptively.
| Category | Pattern | Use When |
|---|---|---|
| Creational | Factory | Object creation is complex |
| Creational | Builder | Many optional parameters |
| Structural | Adapter | Integrating incompatible interfaces |
| Structural | Facade | Simplifying complex subsystem |
| Behavioral | Strategy | Multiple algorithms, runtime selection |
| Behavioral | Observer | Many dependents need notification |
仅在解决实际问题时使用模式,不要预先使用。
| 类别 | 模式 | 使用场景 |
|---|---|---|
| Creational | Factory | 对象创建逻辑复杂时 |
| Creational | Builder | 存在多个可选参数时 |
| Structural | Adapter | 集成不兼容的接口时 |
| Structural | Facade | 简化复杂子系统时 |
| Behavioral | Strategy | 存在多种算法且需运行时选择时 |
| Behavioral | Observer | 多个依赖项需要通知时 |
Security Patterns
安全模式
Input Validation
输入验证
- Validate all external input
- Use allowlists over denylists
- Validate on server side
- 验证所有外部输入
- 使用允许列表而非拒绝列表
- 在服务器端进行验证
Data Protection
数据保护
- Encrypt sensitive data at rest and in transit
- Never log sensitive information
- Use parameterized queries (prevent SQL injection)
- Escape output (prevent XSS)
Search for security issues:
- Find hardcoded secrets (password assignments, API keys)
- Find logging of potentially sensitive data
- 对静态和传输中的敏感数据进行加密
- 永远不要记录敏感信息
- 使用参数化查询(防止SQL注入)
- 对输出进行转义(防止XSS)
查找安全问题:
- 查找硬编码的密钥(密码赋值、API密钥)
- 查找可能包含敏感数据的日志
Phase 2: Implementation
第二阶段:实现
Guidelines for self-documenting code, naming, functions, error handling, and avoiding code smells.
关于自文档化代码、命名、函数设计、错误处理和避免代码异味的指南。
Self-Documenting Code
自文档化代码
Write code that explains itself. Avoid inline comments.
Comments are a code smell - they indicate the code isn't clear enough. Instead of adding comments, improve the code:
❌ BAD: Comment explaining unclear code
// Check if user can access the resource
if (u.r === 1 && u.s !== 0 && checkTs(u.t)) { ... }
✅ GOOD: Self-documenting code needs no comment
if (user.isAdmin && user.isActive && !user.isSessionExpired()) { ... }When you feel the need to write a comment, first try to:
- Rename variables/functions to be more descriptive
- Extract complex logic into well-named functions
- Break down long expressions into meaningful intermediates
Only acceptable comments:
- Links to external documentation (bug trackers, RFCs, specs)
- Legal/license headers
- Warnings about non-obvious side effects or gotchas
❌ INLINE COMMENTS (Don't write these)
─────────────────────────────────────────────────────
// Increment i
// Loop through array
// Set x to 5
// This function does stuff
// Added by John on 2024-01-15
// Get the user
// Check if valid
// Return the result
// TODO: Fix this later (track in issue tracker instead)
// FIXME: This is broken (fix it now or create an issue)
✅ ACCEPTABLE COMMENTS (Rare exceptions)
─────────────────────────────────────────────────────
// See RFC 7231 Section 6.5.4 for status code semantics
// Workaround for browser bug https://bugs.webkit.org/12345
// WARNING: This mutates the input array for performance编写能自我解释的代码。避免内联注释。
注释是代码异味的表现——它们表明代码不够清晰。与其添加注释,不如改进代码:
❌ BAD: Comment explaining unclear code
// Check if user can access the resource
if (u.r === 1 && u.s !== 0 && checkTs(u.t)) { ... }
✅ GOOD: Self-documenting code needs no comment
if (user.isAdmin && user.isActive && !user.isSessionExpired()) { ... }当你觉得需要写注释时,先尝试:
- 重命名变量/函数使其更具描述性
- 将复杂逻辑提取到命名清晰的函数中
- 将长表达式拆分为有意义的中间变量
仅可接受的注释:
- 指向外部文档的链接(bug追踪器、RFC、规范)
- 法律/许可证头
- 关于非明显副作用或陷阱的警告
❌ INLINE COMMENTS (Don't write these)
─────────────────────────────────────────────────────
// Increment i
// Loop through array
// Set x to 5
// This function does stuff
// Added by John on 2024-01-15
// Get the user
// Check if valid
// Return the result
// TODO: Fix this later (track in issue tracker instead)
// FIXME: This is broken (fix it now or create an issue)
✅ ACCEPTABLE COMMENTS (Rare exceptions)
─────────────────────────────────────────────────────
// See RFC 7231 Section 6.5.4 for status code semantics
// Workaround for browser bug https://bugs.webkit.org/12345
// WARNING: This mutates the input array for performanceNaming Conventions
命名规范
Be explicit, clear, and concise. Names should reveal intent.
✅ GOOD ❌ BAD
─────────────────────────────────────────────────────
getUserById(userId) get(id)
isValidEmail(email) check(email)
calculateTotalPrice(items) calc(items)
MAX_RETRY_ATTEMPTS MAX
userRepository ur
fetchUserData() getData()
hasPermission(user, action) perm(u, a)
activeUsers list
emailAddress str
retryCount n
isEnabled flag
createdAt date明确、清晰、简洁。名称应体现意图。
✅ GOOD ❌ BAD
─────────────────────────────────────────────────────
getUserById(userId) get(id)
isValidEmail(email) check(email)
calculateTotalPrice(items) calc(items)
MAX_RETRY_ATTEMPTS MAX
userRepository ur
fetchUserData() getData()
hasPermission(user, action) perm(u, a)
activeUsers list
emailAddress str
retryCount n
isEnabled flag
createdAt dateAvoid Ambiguous Parameters
避免模糊参数
Never use single-letter or unclear parameter names.
❌ BAD: Ambiguous parameters
function process(x) { ... }
function calc(a, b, c) { ... }
const result = items.map(x => x.value);
✅ GOOD: Clear, descriptive parameters
function processOrder(order) { ... }
function calculateDiscount(price, quantity, discountRate) { ... }
const values = items.map(item => item.value);永远不要使用单字母或模糊的参数名称。
❌ BAD: Ambiguous parameters
function process(x) { ... }
function calc(a, b, c) { ... }
const result = items.map(x => x.value);
✅ GOOD: Clear, descriptive parameters
function processOrder(order) { ... }
function calculateDiscount(price, quantity, discountRate) { ... }
const values = items.map(item => item.value);Naming Guidelines by Type
按类型划分的命名指南
| Type | Convention | Examples |
|---|---|---|
| Variables | camelCase, noun/noun phrase | |
| Functions | camelCase, verb/verb phrase | |
| Booleans | Prefix: | |
| Constants | UPPER_SNAKE_CASE | |
| Classes | PascalCase, noun | |
| Interfaces | PascalCase, noun/adjective | |
| Enums | PascalCase (name), UPPER_SNAKE (values) | |
| Private members | Prefix | |
Review naming consistency:
- List all symbols to review naming patterns
- Query specific symbol names to check consistency
| 类型 | 规范 | 示例 |
|---|---|---|
| 变量 | camelCase,名词/名词短语 | |
| 函数 | camelCase,动词/动词短语 | |
| 布尔值 | 前缀: | |
| 常量 | UPPER_SNAKE_CASE | |
| 类 | PascalCase,名词 | |
| 接口 | PascalCase,名词/形容词 | |
| 枚举 | PascalCase(名称),UPPER_SNAKE(值) | |
| 私有成员 | 前缀 | |
检查命名一致性:
- 列出所有符号以检查命名模式
- 查询特定符号名称以验证一致性
File Organization
文件组织
File Naming Conventions
文件命名规范
Follow existing project structure. Be consistent.
✅ CONSISTENT FILE NAMING
─────────────────────────────────────────────────────
kebab-case (recommended for most projects):
user-service.ts
order-repository.ts
api-client.test.ts
PascalCase (React components, classes):
UserProfile.tsx
OrderList.tsx
snake_case (Python, Ruby):
user_service.py
order_repository.py
ALWAYS match existing project conventions!遵循现有项目结构。保持一致。
✅ CONSISTENT FILE NAMING
─────────────────────────────────────────────────────
kebab-case(大多数项目推荐):
user-service.ts
order-repository.ts
api-client.test.ts
PascalCase(React组件、类):
UserProfile.tsx
OrderList.tsx
snake_case(Python、Ruby):
user_service.py
order_repository.py
ALWAYS match existing project conventions!Directory Structure
目录结构
✅ GOOD STRUCTURE ❌ BAD STRUCTURE
─────────────────────────────────────────────────────
src/ src/
users/ utils.ts (god file)
user-service.ts helpers.ts (vague)
user-repository.ts misc.ts (dumping ground)
user.types.ts stuff.ts (meaningless)
user-service.test.ts file1.ts (lazy naming)
orders/
order-service.ts
order-repository.ts✅ GOOD STRUCTURE ❌ BAD STRUCTURE
─────────────────────────────────────────────────────
src/ src/
users/ utils.ts (god file)
user-service.ts helpers.ts (vague)
user-repository.ts misc.ts (dumping ground)
user.types.ts stuff.ts (meaningless)
user-service.test.ts file1.ts (lazy naming)
orders/
order-service.ts
order-repository.tsFollow Existing Code Structure
遵循现有代码结构
Consistency trumps personal preference.
Before writing new code:
- Study existing patterns - How are similar files structured?
- Match naming style - Use same conventions as surrounding code
- Follow established architecture - Don't introduce new patterns arbitrarily
- Respect module boundaries - Place code where similar code lives
❌ DON'T: Introduce inconsistent patterns
// Existing: userService.getById(id)
// New code: fetchSingleOrder(orderId) // Different naming style!
✅ DO: Match existing conventions
// Existing: userService.getById(id)
// New code: orderService.getById(orderId) // Consistent!一致性优先于个人偏好。
编写新代码前:
- 研究现有模式 - 类似文件的结构是怎样的?
- 匹配命名风格 - 使用与周边代码相同的规范
- 遵循既定架构 - 不要随意引入新模式
- 尊重模块边界 - 将代码放在同类代码所在的位置
❌ DON'T: Introduce inconsistent patterns
// Existing: userService.getById(id)
// New code: fetchSingleOrder(orderId) // Different naming style!
✅ DO: Match existing conventions
// Existing: userService.getById(id)
// New code: orderService.getById(orderId) // Consistent!Avoid Barrel Files and Re-exports
避免桶文件和重导出
Barrel files ( re-exporting siblings) are an anti-pattern. They hide dependencies, create circular import risks, and force wide rebuilds when unrelated exports change.
index.ts❌ BAD: Barrel file
// src/users/index.ts
export * from "./user-service";
export * from "./user-repository";
// usage
import { userService } from "./users";
✅ GOOD: Direct imports
import { userService } from "./users/user-service";
import { userRepository } from "./users/user-repository";Rules:
- Import from the concrete module, not
index.ts - Don't add re-export layers unless a framework explicitly requires it
- Prefer explicit dependencies over convenience
Analyze existing patterns:
- Map project structure to understand organization
- Inspect existing services to see how they're structured
- Query symbol names to check naming conventions
桶文件(重导出同级文件)是反模式。 它们会隐藏依赖关系,带来循环导入风险,并且当无关导出项变化时会强制大范围重新构建。
index.ts❌ BAD: Barrel file
// src/users/index.ts
export * from "./user-service";
export * from "./user-repository";
// usage
import { userService } from "./users";
✅ GOOD: Direct imports
import { userService } from "./users/user-service";
import { userRepository } from "./users/user-repository";规则:
- 从具体模块导入,而非
index.ts - 除非框架明确要求,否则不要添加重导出层
- 优先选择显式依赖而非便捷性
分析现有模式:
- 映射项目结构以理解组织方式
- 检查现有服务的结构
- 查询符号名称以验证命名规范
Function Design
函数设计
✅ BEST PRACTICES ❌ ANTI-PATTERNS
─────────────────────────────────────────────────────
Small, focused functions God functions (100+ lines)
2-3 parameters max 6+ parameters
Return early for edge cases Deep nesting
Pure functions when possible Hidden side effects
Descriptive names Abbreviations
Single level of abstraction Mixed abstraction levelsAnalyze function complexity:
- Find callees of a function (too many = does too much)
- View function body to assess complexity
✅ BEST PRACTICES ❌ ANTI-PATTERNS
─────────────────────────────────────────────────────
Small, focused functions God functions (100+ lines)
2-3 parameters max 6+ parameters
Return early for edge cases Deep nesting
Pure functions when possible Hidden side effects
Descriptive names Abbreviations
Single level of abstraction Mixed abstraction levels分析函数复杂度:
- 查看函数的被调用方(过多则说明职责过重)
- 查看函数体以评估复杂度
Error Handling
错误处理
Best Practices
最佳实践
✅ DO ❌ DON'T
─────────────────────────────────────────────────────
Fail fast and loud Swallow exceptions silently
Use specific exception types Catch generic Exception
Include context in error messages Return null for errors
Validate inputs at boundaries Trust external data
Log errors with stack traces Log without context
Use Result/Either types Return magic values (-1, null)
Handle errors at appropriate level Handle everywhere or nowhere
Let unexpected errors propagate Catch everything "just in case"✅ DO ❌ DON'T
─────────────────────────────────────────────────────
Fail fast and loud Swallow exceptions silently
Use specific exception types Catch generic Exception
Include context in error messages Return null for errors
Validate inputs at boundaries Trust external data
Log errors with stack traces Log without context
Use Result/Either types Return magic values (-1, null)
Handle errors at appropriate level Handle everywhere or nowhere
Let unexpected errors propagate Catch everything "just in case"Never Silence Exceptions
永远不要静默异常
Don't catch exceptions unless you can handle them meaningfully.
typescript
// ❌ NEVER: Silent catch
try {
await saveUser(user);
} catch (error) {
// silently ignored
}
// ❌ NEVER: Catch and log only (still loses the error)
try {
await saveUser(user);
} catch (error) {
console.log(error);
}
// ✅ GOOD: Let it propagate (caller handles or app crashes visibly)
await saveUser(user);
// ✅ GOOD: Handle specific errors you can recover from
try {
await saveUser(user);
} catch (error) {
if (error instanceof DuplicateEmailError) {
return { error: "Email already registered" };
}
throw error; // Re-throw unexpected errors
}
// ✅ GOOD: Add context and re-throw
try {
await saveUser(user);
} catch (error) {
throw new UserServiceError(`Failed to save user ${user.id}`, {
cause: error,
});
}Find problematic error handling:
- Search for empty catch blocks
- Search for catch blocks that only log
- Search for bare except clauses (Python)
Rules:
- Empty catch blocks are always wrong
- Catch only exceptions you can meaningfully handle
- Re-throw or propagate everything else
- If you catch, either recover or add context and re-throw
- Prefer crashing visibly over failing silently
除非你能有意义地处理异常,否则不要捕获它。
typescript
// ❌ NEVER: Silent catch
try {
await saveUser(user);
} catch (error) {
// silently ignored
}
// ❌ NEVER: Catch and log only (still loses the error)
try {
await saveUser(user);
} catch (error) {
console.log(error);
}
// ✅ GOOD: Let it propagate (caller handles or app crashes visibly)
await saveUser(user);
// ✅ GOOD: Handle specific errors you can recover from
try {
await saveUser(user);
} catch (error) {
if (error instanceof DuplicateEmailError) {
return { error: "Email already registered" };
}
throw error; // Re-throw unexpected errors
}
// ✅ GOOD: Add context and re-throw
try {
await saveUser(user);
} catch (error) {
throw new UserServiceError(`Failed to save user ${user.id}`, {
cause: error,
});
}查找有问题的错误处理:
- 搜索空的catch块
- 搜索仅记录日志的catch块
- 搜索bare except子句(Python)
规则:
- 空catch块永远是错误的
- 仅捕获你能有意义处理的异常
- 重新抛出或传播所有其他异常
- 如果捕获异常,要么恢复,要么添加上下文后重新抛出
- 优先选择明显崩溃而非静默失败
Error Handling Patterns
错误处理模式
typescript
// ✅ Result type pattern
type Result<T, E = Error> = { ok: true; value: T } | { ok: false; error: E };
function parseConfig(path: string): Result<Config> {
try {
const data = readFile(path);
return { ok: true, value: JSON.parse(data) };
} catch (error) {
return { ok: false, error: new ConfigError(`Invalid config: ${path}`) };
}
}
// ✅ Early return pattern
function processUser(user: User | null): string {
if (!user) return "No user";
if (!user.isActive) return "User inactive";
if (!user.hasPermission) return "No permission";
return performAction(user); // Happy path
}typescript
// ✅ Result type pattern
type Result<T, E = Error> = { ok: true; value: T } | { ok: false; error: E };
function parseConfig(path: string): Result<Config> {
try {
const data = readFile(path);
return { ok: true, value: JSON.parse(data) };
} catch (error) {
return { ok: false, error: new ConfigError(`Invalid config: ${path}`) };
}
}
// ✅ Early return pattern
function processUser(user: User | null): string {
if (!user) return "No user";
if (!user.isActive) return "User inactive";
if (!user.hasPermission) return "No permission";
return performAction(user); // Happy path
}Code Smells
代码异味
| Smell | Description | Refactoring | Detection |
|---|---|---|---|
| God Class | Class knows/does too much | Split into focused classes | Find callees, inspect class |
| Feature Envy | Method uses other class's data more | Move method to that class | Analyze dependencies |
| Data Clumps | Same data groups appear together | Extract into a class | Find duplicate code |
| Primitive Obsession | Using primitives for domain concepts | Create value objects | Search code patterns |
| Long Parameter List | Functions with 5+ parameters | Use parameter object | Search code patterns |
| Shotgun Surgery | One change affects many classes | Consolidate related code | Find callers |
| Divergent Change | One class changed for many reasons | Split by responsibility | Inspect class |
| Dead Code | Unused code left in codebase | Delete it | Find unused code |
| Speculative Generality | Code for hypothetical futures | Delete until needed | Find unused code |
Detect code smells:
- Find unused code (dead code)
- Find duplicate code (Data Clumps pattern)
- Find callees of suspected god functions (too many = does too much)
- Search for functions with 5+ parameters (long parameter lists)
- Find unused exports
| 异味名称 | 描述 | 重构方案 | 检测方式 |
|---|---|---|---|
| God Class | 类的职责过多 | 拆分为多个专注的类 | 查找被调用方,检查类的内容 |
| Feature Envy | 方法更多使用其他类的数据 | 将方法移动到对应类中 | 分析依赖关系 |
| Data Clumps | 相同的数据组反复出现 | 提取为类 | 查找重复代码 |
| Primitive Obsession | 使用原始类型表示领域概念 | 创建值对象 | 搜索代码模式 |
| Long Parameter List | 函数参数超过5个 | 使用参数对象 | 搜索代码模式 |
| Shotgun Surgery | 一处修改影响多个类 | 整合相关代码 | 查找调用方 |
| Divergent Change | 一个类因多种原因被修改 | 按职责拆分 | 检查类的内容 |
| Dead Code | 代码库中遗留的未使用代码 | 删除它 | 查找未使用代码 |
| Speculative Generality | 为假设的未来场景编写的代码 | 删除直到需要时再添加 | 查找未使用代码 |
检测代码异味:
- 查找未使用的代码(死代码)
- 查找重复代码(Data Clumps模式)
- 查找疑似上帝函数的被调用方(过多则说明职责过重)
- 搜索参数超过5个的函数(长参数列表)
- 查找未使用的导出项
Linting & Code Formatting
代码检查与格式化
Automate style enforcement. Don't argue about formatting in code reviews.
自动化风格检查。不要在代码评审中争论格式问题。
Principles
原则
| Principle | Why |
|---|---|
| Automate formatting | Eliminates bikeshedding, ensures consistency |
| Use recommended rules | Battle-tested defaults, less configuration |
| Lint on save/commit | Catch issues early, before PR |
| Zero warnings policy | Treat warnings as errors in CI |
| Format on save | Never commit unformatted code |
| 原则 | 原因 |
|---|---|
| 自动化格式化 | 消除无意义的争论,确保一致性 |
| 使用推荐规则 | 经过实战检验的默认配置,减少配置工作 |
| 保存/提交时检查 | 提前发现问题,在PR之前解决 |
| 零警告策略 | 在CI中将警告视为错误 |
| 保存时格式化 | 永远不要提交未格式化的代码 |
Separation of Concerns
关注点分离
| Tool | Purpose | Examples |
|---|---|---|
| Formatter | Code style (spacing, quotes, etc.) | Prettier, dprint, Biome, ruff format |
| Linter | Code quality (bugs, patterns) | ESLint, Biome, Ruff, Pylint |
| Type checker | Type safety | TypeScript, mypy, Pyright |
Don't overlap responsibilities. Use a formatter for formatting, a linter for logic issues. Disable linter formatting rules when using a formatter.
| 工具类型 | 用途 | 示例 |
|---|---|---|
| 格式化工具 | 代码风格(缩进、引号等) | Prettier, dprint, Biome, ruff format |
| 代码检查工具 | 代码质量(bug、模式) | ESLint, Biome, Ruff, Pylint |
| 类型检查工具 | 类型安全 | TypeScript, mypy, Pyright |
不要重叠职责。 使用格式化工具处理格式,使用代码检查工具处理逻辑问题。使用格式化工具时禁用代码检查工具中的格式规则。
When to Disable Rules
何时禁用规则
Almost never. If a rule flags your code, fix the code - don't disable the rule.
typescript
// ❌ NEVER: Disable type safety - write proper types instead
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const response: any = await legacyApi.fetch();
// ✅ CORRECT: Write types for untyped APIs
interface LegacyResponse {
data: unknown;
status: number;
}
const response: LegacyResponse = await legacyApi.fetch();If you think you need to disable a rule:
- You're wrong - fix the code instead
- Recommended rules exist for good reasons - follow them
- If you can't satisfy a rule, your design is flawed - refactor
几乎永远不要。 如果规则标记了你的代码,修复代码——不要禁用规则。
typescript
// ❌ NEVER: Disable type safety - write proper types instead
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const response: any = await legacyApi.fetch();
// ✅ CORRECT: Write types for untyped APIs
interface LegacyResponse {
data: unknown;
status: number;
}
const response: LegacyResponse = await legacyApi.fetch();如果你认为需要禁用规则:
- 你错了——修复代码
- 推荐规则存在是有原因的——遵循它们
- 如果你无法满足规则,说明你的设计有缺陷——重构
While Writing Code Checklist
编码时检查清单
- Write tests first (or alongside)
- Keep functions small and focused
- Use meaningful names
- Handle errors properly
- Run linter and fix issues
- Format code before committing
- Type check passes
- 先写测试(或同步编写)
- 保持函数小而专注
- 使用有意义的名称
- 正确处理错误
- 运行代码检查工具并修复问题
- 提交前格式化代码
- 类型检查通过
Phase 3: Testing
第三阶段:测试
Guidelines for test pyramid, BDD, test quality, and avoiding test anti-patterns.
关于测试金字塔、BDD、测试质量和避免测试反模式的指南。
Test Pyramid
测试金字塔
/\
/ \ E2E Tests (few)
/----\ - Critical user journeys
/ \ - Slow, expensive
/--------\
/ \ Integration Tests (some)
/------------\- Component interactions
/ \- Database, APIs
/----------------\
Unit Tests (many)
- Fast, isolated
- Business logic /\
/ \ E2E Tests (few)
/----\ - Critical user journeys
/ \ - Slow, expensive
/--------\
/ \ Integration Tests (some)
/------------\- Component interactions
/ \- Database, APIs
/----------------\
Unit Tests (many)
- Fast, isolated
- Business logicWhat to Test
测试范围
✅ TEST ❌ SKIP
─────────────────────────────────────────────────────
Business logic Framework code
Edge cases and boundaries Trivial getters/setters
Error handling paths Third-party libraries
Public API contracts Private implementation details
Integration points UI layout (use visual tests)
Security-sensitive code Configuration files✅ TEST ❌ SKIP
─────────────────────────────────────────────────────
Business logic Framework code
Edge cases and boundaries Trivial getters/setters
Error handling paths Third-party libraries
Public API contracts Private implementation details
Integration points UI layout (use visual tests)
Security-sensitive code Configuration filesTest Quality Checklist
测试质量检查清单
- Tests are independent and isolated
- Tests are deterministic (no flakiness)
- Test names describe behavior being tested
- Each test has a single reason to fail
- Tests run fast (< 100ms for unit tests)
- Tests use meaningful assertions
- Setup/teardown is minimal and clear
- 测试独立且隔离
- 测试具有确定性(无随机性失败)
- 测试名称描述了要验证的行为
- 每个测试只有一个失败原因
- 测试运行快速(单元测试<100ms)
- 测试使用有意义的断言
- 初始化/清理逻辑简洁清晰
Behavior-Driven Development (BDD)
Behavior-Driven Development (BDD)
BDD bridges the gap between business requirements and technical implementation by using a shared language that all stakeholders understand.
Business Need → Executable Specification → Working SoftwareBDD通过使用所有利益相关者都能理解的通用语言,弥合业务需求与技术实现之间的差距。
Business Need → Executable Specification → Working SoftwareStructure: Given-When-Then
结构:Given-When-Then
Organize tests around three phases:
| Phase | Purpose | Example |
|---|---|---|
| Given | Preconditions, initial state | "a registered user exists" |
| When | Action being performed | "the user logs in with valid credentials" |
| Then | Expected outcome | "the user should see their dashboard" |
将测试组织为三个阶段:
| 阶段 | 用途 | 示例 |
|---|---|---|
| Given | 前置条件、初始状态 | "存在一个已注册用户" |
| When | 执行的操作 | "用户使用有效凭据登录" |
| Then | 预期结果 | "用户应看到其仪表板" |
Writing Good Scenarios
编写良好的场景
❌ BAD: Implementation details, unclear intent
─────────────────────────────────────────────────────
"open browser, navigate to /login, find element with
id email, type test@test.com, click submit button"
✅ GOOD: Business language, clear intent
─────────────────────────────────────────────────────
Given a registered user exists
When the user logs in with valid credentials
Then the user should see their dashboard❌ BAD: Implementation details, unclear intent
─────────────────────────────────────────────────────
"open browser, navigate to /login, find element with
id email, type test@test.com, click submit button"
✅ GOOD: Business language, clear intent
─────────────────────────────────────────────────────
Given a registered user exists
When the user logs in with valid credentials
Then the user should see their dashboardBDD Best Practices
BDD最佳实践
✅ DO ❌ DON'T
─────────────────────────────────────────────────────
Write from user's perspective Use technical jargon
One behavior per scenario Test multiple things
Use declarative style Include implementation details
Keep scenarios independent Share state between scenarios
Use meaningful data Use "test", "foo", "bar"
Focus on business outcomes Focus on UI interactions✅ DO ❌ DON'T
─────────────────────────────────────────────────────
Write from user's perspective Use technical jargon
One behavior per scenario Test multiple things
Use declarative style Include implementation details
Keep scenarios independent Share state between scenarios
Use meaningful data Use "test", "foo", "bar"
Focus on business outcomes Focus on UI interactionsThree Amigos
Three Amigos
BDD works best when three perspectives collaborate:
- Business/Product: What problem are we solving?
- Development: How will we build it?
- Testing: What could go wrong?
Before writing code:
- Discuss requirements together
- Write scenarios collaboratively
- Agree on acceptance criteria
- Identify edge cases early
BDD在三种角色协作时效果最佳:
- 业务/产品:我们要解决什么问题?
- 开发:我们将如何构建它?
- 测试:可能会出什么问题?
编写代码前:
- 共同讨论需求
- 协作编写场景
- 达成验收标准一致
- 提前识别边缘情况
Writing BDD Tests
编写BDD测试
Basic BDD Structure
基础BDD结构
typescript
describe("UserService", () => {
describe("given a new user with valid data", () => {
const userData = { name: "Alice", email: "alice@example.com" };
describe("when creating the user", () => {
it("then the user should be created with an ID", async () => {
const userService = new UserService();
const user = await userService.create(userData);
expect(user.id).toBeDefined();
expect(user.name).toBe("Alice");
expect(user.email).toBe("alice@example.com");
});
});
});
describe("given user data with an invalid email", () => {
const userData = { name: "Bob", email: "invalid-email" };
describe("when creating the user", () => {
it("then it should reject with a validation error", async () => {
const userService = new UserService();
await expect(userService.create(userData)).rejects.toThrow(
"Invalid email",
);
});
});
});
});typescript
describe("UserService", () => {
describe("given a new user with valid data", () => {
const userData = { name: "Alice", email: "alice@example.com" };
describe("when creating the user", () => {
it("then the user should be created with an ID", async () => {
const userService = new UserService();
const user = await userService.create(userData);
expect(user.id).toBeDefined();
expect(user.name).toBe("Alice");
expect(user.email).toBe("alice@example.com");
});
});
});
describe("given user data with an invalid email", () => {
const userData = { name: "Bob", email: "invalid-email" };
describe("when creating the user", () => {
it("then it should reject with a validation error", async () => {
const userService = new UserService();
await expect(userService.create(userData)).rejects.toThrow(
"Invalid email",
);
});
});
});
});BDD with Shared Context
带共享上下文的BDD
typescript
describe("User Login", () => {
describe("given a registered user exists", () => {
let user: User;
beforeEach(async () => {
user = await createUser({
email: "alice@example.com",
password: "secure123",
});
});
describe("when the user logs in with valid credentials", () => {
let session: Session;
beforeEach(async () => {
session = await login(user.email, "secure123");
});
it("then a valid session should be created", () => {
expect(session.token).toBeDefined();
expect(session.userId).toBe(user.id);
});
it("then the user should be able to access their dashboard", async () => {
const dashboard = await getDashboard(session);
expect(dashboard.userId).toBe(user.id);
expect(dashboard.welcomeMessage).toContain(user.name);
});
});
describe("when the user logs in with wrong password", () => {
it("then login should fail with authentication error", async () => {
await expect(login(user.email, "wrongpassword")).rejects.toThrow(
"Invalid credentials",
);
});
});
});
});typescript
describe("User Login", () => {
describe("given a registered user exists", () => {
let user: User;
beforeEach(async () => {
user = await createUser({
email: "alice@example.com",
password: "secure123",
});
});
describe("when the user logs in with valid credentials", () => {
let session: Session;
beforeEach(async () => {
session = await login(user.email, "secure123");
});
it("then a valid session should be created", () => {
expect(session.token).toBeDefined();
expect(session.userId).toBe(user.id);
});
it("then the user should be able to access their dashboard", async () => {
const dashboard = await getDashboard(session);
expect(dashboard.userId).toBe(user.id);
expect(dashboard.welcomeMessage).toContain(user.name);
});
});
describe("when the user logs in with wrong password", () => {
it("then login should fail with authentication error", async () => {
await expect(login(user.email, "wrongpassword")).rejects.toThrow(
"Invalid credentials",
);
});
});
});
});BDD Error Handling Tests
BDD错误处理测试
typescript
describe("User Processing", () => {
describe("given a null user input", () => {
describe("when processing the user", () => {
it("then it should throw a validation error", () => {
expect(() => processUser(null)).toThrow("User cannot be null");
});
});
});
describe("given an invalid user ID", () => {
describe("when fetching the user", () => {
it("then it should throw a ValidationError", async () => {
await expect(fetchUser(-1)).rejects.toBeInstanceOf(ValidationError);
});
it("then the error should include the invalid ID", async () => {
await expect(fetchUser(-1)).rejects.toThrow(/invalid user id.*-1/i);
});
});
});
});typescript
describe("User Processing", () => {
describe("given a null user input", () => {
describe("when processing the user", () => {
it("then it should throw a validation error", () => {
expect(() => processUser(null)).toThrow("User cannot be null");
});
});
});
describe("given an invalid user ID", () => {
describe("when fetching the user", () => {
it("then it should throw a ValidationError", async () => {
await expect(fetchUser(-1)).rejects.toBeInstanceOf(ValidationError);
});
it("then the error should include the invalid ID", async () => {
await expect(fetchUser(-1)).rejects.toThrow(/invalid user id.*-1/i);
});
});
});
});BDD with Parametrized Tests
带参数化测试的BDD
typescript
describe("Order Discount Calculation", () => {
describe("given different order totals", () => {
const testCases = [
{ orderTotal: 50, expectedDiscount: 0, scenario: "order under $100" },
{ orderTotal: 100, expectedDiscount: 10, scenario: "order exactly $100" },
{
orderTotal: 250,
expectedDiscount: 25,
scenario: "order between $100-$500",
},
{ orderTotal: 500, expectedDiscount: 75, scenario: "order exactly $500" },
{ orderTotal: 1000, expectedDiscount: 150, scenario: "order over $500" },
];
testCases.forEach(({ orderTotal, expectedDiscount, scenario }) => {
describe(`given an ${scenario} ($${orderTotal})`, () => {
describe("when calculating the discount", () => {
it(`then the discount should be $${expectedDiscount}`, () => {
const discount = calculateDiscount(orderTotal);
expect(discount).toBe(expectedDiscount);
});
});
});
});
});
});typescript
describe("Order Discount Calculation", () => {
describe("given different order totals", () => {
const testCases = [
{ orderTotal: 50, expectedDiscount: 0, scenario: "order under $100" },
{ orderTotal: 100, expectedDiscount: 10, scenario: "order exactly $100" },
{
orderTotal: 250,
expectedDiscount: 25,
scenario: "order between $100-$500",
},
{ orderTotal: 500, expectedDiscount: 75, scenario: "order exactly $500" },
{ orderTotal: 1000, expectedDiscount: 150, scenario: "order over $500" },
];
testCases.forEach(({ orderTotal, expectedDiscount, scenario }) => {
describe(`given an ${scenario} ($${orderTotal})`, () => {
describe("when calculating the discount", () => {
it(`then the discount should be $${expectedDiscount}`, () => {
const discount = calculateDiscount(orderTotal);
expect(discount).toBe(expectedDiscount);
});
});
});
});
});
});Finding Untested Code
查找未测试代码
- Generate coverage reports to see which lines/branches are untested
- Find tests for a specific symbol to see what's covered
- Find untested symbols to identify gaps
- Find what production code a test file touches
- 生成覆盖率报告查看哪些行/分支未被测试
- 查找特定符号的测试,了解覆盖情况
- 查找未被测试的符号以识别缺口
- 查找测试文件涉及的生产代码
Testing Anti-Patterns
测试反模式
| Anti-Pattern | Problem | Solution |
|---|---|---|
| Ice Cream Cone | More E2E tests than unit tests | Invert the pyramid |
| Flaky Tests | Tests randomly fail | Fix race conditions, use mocks |
| Slow Tests | Test suite takes too long | Isolate, parallelize, mock I/O |
| Testing Implementation | Tests break on refactor | Test behavior, not internals |
| No Assertions | Tests without meaningful checks | Add specific assertions |
| Test Data Coupling | Tests depend on shared state | Isolate test data |
| 反模式名称 | 问题 | 解决方案 |
|---|---|---|
| Ice Cream Cone | E2E测试数量超过单元测试 | 反转测试金字塔 |
| Flaky Tests | 测试随机失败 | 修复竞态条件,使用mock |
| Slow Tests | 测试套件运行时间过长 | 隔离测试,并行执行,mock I/O |
| Testing Implementation | 重构时测试会失败 | 测试行为,而非内部实现 |
| No Assertions | 测试没有有意义的检查 | 添加具体断言 |
| Test Data Coupling | 测试依赖共享状态 | 隔离测试数据 |
BDD Anti-Patterns
BDD反模式
| Anti-Pattern | Problem | Solution |
|---|---|---|
| UI-focused steps | Brittle, hard to read | Use domain language |
| Too many steps | Hard to understand | Split into focused scenarios |
| Incidental details | Noise obscures intent | Include only relevant data |
| No clear outcome | Can't tell what's tested | End with business assertion |
| Coupled scenarios | Order-dependent tests | Make scenarios independent |
| Developer jargon | Business can't validate | Use ubiquitous language |
Detect test anti-patterns:
- Search for test blocks without assertions
- Run tests with verbose timing to find slow tests
| 反模式名称 | 问题 | 解决方案 |
|---|---|---|
| UI-focused steps | 脆弱、难以阅读 | 使用领域语言 |
| Too many steps | 难以理解 | 拆分为专注的场景 |
| Incidental details | 无关信息掩盖意图 | 仅包含相关数据 |
| No clear outcome | 无法明确测试目标 | 以业务断言结尾 |
| Coupled scenarios | 测试依赖执行顺序 | 使场景相互独立 |
| Developer jargon | 业务人员无法验证 | 使用通用语言 |
检测测试反模式:
- 搜索没有断言的测试块
- 运行测试并查看详细计时以找到慢测试
Test Organization
测试组织
File Structure
文件结构
src/
users/
user-service.ts
user-service.test.ts # Co-located tests
orders/
order-service.ts
order-service.test.tssrc/
users/
user-service.ts
user-service.test.ts # 同目录测试
orders/
order-service.ts
order-service.test.tsOR
OR
src/
users/
user-service.ts
tests/
users/
user-service.test.ts # Mirrored structure
undefinedsrc/
users/
user-service.ts
tests/
users/
user-service.test.ts # 镜像结构
undefinedNaming Tests (BDD Style)
测试命名(BDD风格)
typescript
// ❌ BAD: Vague names, no context
it("works", () => {});
it("handles error", () => {});
it("test1", () => {});
// ❌ BAD: Missing Given-When-Then structure
describe("UserService", () => {
it("creates user", () => {});
it("throws on invalid email", () => {});
});
// ✅ GOOD: Full BDD structure with Given-When-Then
describe("UserService", () => {
describe("given valid user data", () => {
describe("when creating a user", () => {
it("then the user should be persisted with an ID", () => {});
});
});
describe("given an invalid email format", () => {
describe("when creating a user", () => {
it("then a ValidationError should be thrown", () => {});
});
});
});typescript
// ❌ BAD: Vague names, no context
it("works", () => {});
it("handles error", () => {});
it("test1", () => {});
// ❌ BAD: Missing Given-When-Then structure
describe("UserService", () => {
it("creates user", () => {});
it("throws on invalid email", () => {});
});
// ✅ GOOD: Full BDD structure with Given-When-Then
describe("UserService", () => {
describe("given valid user data", () => {
describe("when creating a user", () => {
it("then the user should be persisted with an ID", () => {});
});
});
describe("given an invalid email format", () => {
describe("when creating a user", () => {
it("then a ValidationError should be thrown", () => {});
});
});
});Phase 4: Review
第四阶段:评审
Code review checklist and self-review guidelines before PR.
代码评审检查清单和PR前的自审指南。
Self-Review Before PR
PR前自审
Before submitting code for review, run these checks:
提交代码评审前,执行以下检查:
Automated Checks
自动化检查
- Type check passes
- Linter passes (fix issues)
- Formatter applied
- All tests pass
- Coverage meets threshold
- 类型检查通过
- 代码检查通过(修复问题)
- 代码已格式化
- 所有测试通过
- 覆盖率达到阈值
Code Quality Checks
代码质量检查
- Find unused code
- Find duplicate code
- Check for circular dependencies
- 查找未使用代码
- 查找重复代码
- 检查循环依赖
Manual Checklist
手动检查清单
- Code compiles and runs without errors
- All tests pass
- No debug code or console.log left behind
- No commented-out code
- Code is properly formatted
- Branch is up to date with main
- 代码编译并能正常运行
- 所有测试通过
- 没有遗留调试代码或console.log
- 没有注释掉的代码
- 代码已正确格式化
- 分支已与主分支同步
Code Review Checklist
代码评审检查清单
Correctness
正确性
- Does the code do what it's supposed to?
- Are edge cases handled?
- Are there any obvious bugs?
- Are error cases properly handled?
Verify:
- Trace the call flow to understand what code does
- Find callees of a function to see what it depends on
- 代码是否实现了预期功能?
- 边缘情况是否被处理?
- 是否存在明显的bug?
- 错误情况是否被正确处理?
验证:
- 跟踪调用流程以理解代码功能
- 查找函数的被调用方以了解其依赖
Design
设计
- Is the code at the right abstraction level?
- Does it follow SOLID principles?
- Are there any code smells?
- Is there unnecessary complexity?
- Are dependencies appropriate?
Verify:
- Find duplicate code
- Check dependency direction
- Check for circular dependencies
- Find unused code
- 代码的抽象层级是否合适?
- 是否遵循SOLID原则?
- 是否存在代码异味?
- 是否存在不必要的复杂度?
- 依赖关系是否合理?
验证:
- 查找重复代码
- 检查依赖方向
- 检查循环依赖
- 查找未使用代码
Readability
可读性
- Is the code easy to understand?
- Are names meaningful and consistent?
- Is the logic straightforward?
- Are there unnecessary comments? (code should be self-documenting)
- Is the file organization clear?
Review:
- Map module structure to understand organization
- Check naming consistency by querying symbols
- 代码是否易于理解?
- 名称是否有意义且一致?
- 逻辑是否直接明了?
- 是否存在不必要的注释?(代码应自我解释)
- 文件组织是否清晰?
评审:
- 映射模块结构以理解组织方式
- 查询符号以检查命名一致性
Maintainability
可维护性
- Is the code testable?
- Are dependencies injected?
- Is there appropriate documentation for public APIs?
- Will this be easy to modify in the future?
- Are magic numbers/strings avoided?
Find issues:
- Search for hardcoded numbers (magic values)
- Search for hardcoded strings that should be constants
- 代码是否可测试?
- 是否使用了依赖注入?
- 公共API是否有适当的文档?
- 未来是否易于修改?
- 是否避免了魔法数字/字符串?
查找问题:
- 搜索硬编码数字(魔法值)
- 搜索应作为常量的硬编码字符串
Security
安全性
- Is user input validated?
- Are there any injection vulnerabilities?
- Are secrets properly handled?
- Is authentication/authorization correct?
- Is sensitive data protected?
Security checks:
- Search for string concatenation in queries (SQL injection)
- Search for template literals in queries
- Search for hardcoded passwords and API keys
- Search for eval() usage
- 用户输入是否被验证?
- 是否存在注入漏洞?
- 密钥是否被正确处理?
- 认证/授权是否正确?
- 敏感数据是否被保护?
安全检查:
- 搜索查询中的字符串拼接(SQL注入)
- 搜索查询中的模板字符串
- 搜索硬编码的密码和API密钥
- 搜索eval()的使用
Performance
性能
- Are there any obvious performance issues?
- Are N+1 queries avoided?
- Is caching considered where appropriate?
- Are there unnecessary computations?
- Is memory usage reasonable?
Find performance issues:
- Search for await inside loops (N+1 patterns)
- Search for async map/forEach patterns
- 是否存在明显的性能问题?
- 是否避免了N+1查询?
- 是否在合适的地方考虑了缓存?
- 是否存在不必要的计算?
- 内存使用是否合理?
查找性能问题:
- 搜索循环中的await(N+1模式)
- 搜索async map/forEach模式
Test Coverage
测试覆盖率
- Are there sufficient tests?
- Do tests cover edge cases?
- Are tests testing behavior, not implementation?
Check:
- Find untested symbols
- Find tests for specific functions
- Run coverage report
- 测试是否充分?
- 测试是否覆盖了边缘情况?
- 测试是否验证行为而非实现?
检查:
- 查找未被测试的符号
- 查找特定函数的测试
- 运行覆盖率报告
Review Best Practices
评审最佳实践
For Reviewers
评审者
- Focus on the code, not the author
- Ask questions rather than make demands
- Explain the "why" behind suggestions
- Distinguish between blocking issues and nitpicks
- Acknowledge good solutions
Use analysis to support reviews:
- View the diff to understand what changed
- Find callers of changed functions to trace impact
- Find tests for changed functions to verify coverage
- 关注代码而非作者
- 提出问题而非下达命令
- 解释建议背后的原因
- 区分阻塞问题和小细节
- 认可好的解决方案
使用分析支持评审:
- 查看差异以理解变更内容
- 查找变更函数的调用方以跟踪影响
- 查找变更函数的测试以验证覆盖率
For Authors
作者
- Keep PRs small and focused
- Provide context in PR description
- Respond to all comments
- Don't take feedback personally
- Ask for clarification when needed
Before requesting review:
- Run all automated checks (type check, lint, tests)
- Self-review the diff
- Check for common issues (unused code, duplication)
- 保持PR小而专注
- 在PR描述中提供上下文
- 回复所有评论
- 不要把反馈个人化
- 需要时请求澄清
请求评审前:
- 运行所有自动化检查(类型检查、代码检查、测试)
- 自审差异
- 检查常见问题(未使用代码、重复代码)
Common Review Questions
常见评审问题
Architecture:
- Does this belong in this module/layer?
- Are dependencies flowing in the right direction?
- Is this the right level of abstraction?
Testing:
- Are there sufficient tests?
- Do tests cover edge cases?
- Are tests testing behavior, not implementation?
Future-proofing:
- Will this scale?
- Is this maintainable?
- Are we introducing technical debt?
架构:
- 代码是否属于当前模块/层级?
- 依赖方向是否正确?
- 抽象层级是否合适?
测试:
- 测试是否充分?
- 测试是否覆盖了边缘情况?
- 测试是否验证行为而非实现?
未来兼容性:
- 代码是否可扩展?
- 是否易于维护?
- 是否引入了技术债务?
Phase 5: Maintenance
第五阶段:维护
Guidelines for refactoring, technical debt, performance, and documentation.
关于重构、技术债务、性能和文档的指南。
Refactoring
重构
Safe Refactoring Steps
安全重构步骤
- Ensure test coverage before refactoring
- Make small, incremental changes
- Run tests after each change
- Commit frequently
- Refactor OR add features, never both
- 确保测试覆盖率 在重构前
- 进行小的、增量式变更
- 每次变更后运行测试
- 频繁提交
- 要么重构要么添加功能,不要同时进行
Pre-Refactoring Analysis
重构前分析
- Check test coverage for the code you're refactoring
- Find callers of the function to understand what depends on it
- Find callees of the function to understand what it depends on
- Find duplicate code that should be refactored together
- 检查要重构代码的测试覆盖率
- 查找函数的调用方以了解依赖
- 查找函数的被调用方以了解其依赖
- 查找应一起重构的重复代码
Common Refactorings
常见重构技巧
| Technique | When to Use | Analysis |
|---|---|---|
| Extract Method | Long method, reusable logic | Search for patterns to replace |
| Extract Class | Class has multiple responsibilities | Find callees to identify groups |
| Inline Method | Method body is as clear as name | Search and replace pattern |
| Move Method | Method uses another class's data more | Analyze dependencies |
| Rename | Name doesn't reveal intent | Search and replace across codebase |
| Replace Conditional with Polymorphism | Complex type-checking logic | Search for switch/if chains |
| Replace Magic Number with Constant | Unexplained numeric literals | Search for hardcoded values |
| Introduce Parameter Object | Long parameter lists | Search for long parameter patterns |
| Replace Inheritance with Composition | Inheritance is forced | Analyze class hierarchy |
| 技巧名称 | 使用场景 | 分析方式 |
|---|---|---|
| Extract Method | 长方法、可复用逻辑 | 搜索可替换的模式 |
| Extract Class | 类有多个职责 | 查找被调用方以识别职责组 |
| Inline Method | 方法体与名称一样清晰 | 搜索并替换模式 |
| Move Method | 方法更多使用其他类的数据 | 分析依赖关系 |
| Rename | 名称无法体现意图 | 全代码库搜索替换 |
| Replace Conditional with Polymorphism | 复杂的类型检查逻辑 | 搜索switch/if链 |
| Replace Magic Number with Constant | 无法解释的数值字面量 | 搜索硬编码值 |
| Introduce Parameter Object | 长参数列表 | 搜索长参数模式 |
| Replace Inheritance with Composition | 强制使用继承 | 分析类层级 |
Refactoring Workflow
重构工作流
- Analyze - Find callers, callees, and tests for the target
- Preview changes - Dry-run search/replace to see what would change
- Apply changes - Execute the refactoring
- Run tests - Verify nothing broke
- Commit - Save progress with meaningful message
- 分析 - 查找目标代码的调用方、被调用方和测试
- 预览变更 - 试运行搜索替换以查看影响范围
- 应用变更 - 执行重构
- 运行测试 - 验证没有问题
- 提交 - 用有意义的消息保存进度
Technical Debt
技术债务
Types of Technical Debt
技术债务类型
| Type | Description | Handling | Detection |
|---|---|---|---|
| Deliberate | Conscious shortcuts | Document, schedule payback | Issues tracker |
| Accidental | Unintentional issues | Fix when discovered | Lint warnings, code review |
| Bit Rot | Code ages poorly | Regular maintenance | Unused code, dependency analysis |
| Outdated Dependencies | Security/compatibility | Regular updates | Dependency audit |
| 类型 | 描述 | 处理方式 | 检测方式 |
|---|---|---|---|
| Deliberate | 有意识的捷径 | 记录,安排偿还时间 | 问题追踪器 |
| Accidental | 无意引入的问题 | 发现后立即修复 | 代码检查警告、代码评审 |
| Bit Rot | 代码随时间老化 | 定期维护 | 未使用代码、依赖分析 |
| Outdated Dependencies | 安全/兼容性问题 | 定期更新 | 依赖审计 |
Finding Technical Debt
查找技术债务
- Find unused code (dead code debt) - files, exports, dependencies
- Find duplicate code (DRY violation debt)
- Find circular dependencies (architecture debt)
- Find outdated dependencies
- 查找未使用代码(死代码债务)- 文件、导出项、依赖项
- 查找重复代码(违反DRY原则债务)
- 查找循环依赖(架构债务)
- 查找过时依赖
Managing Debt
管理债务
- Track it - Document in issues/backlog
- Quantify it - Estimate effort to fix
- Prioritize it - Balance with features
- Pay it down - Allocate time each sprint
- Prevent it - Code reviews, standards
- 跟踪 - 在问题/待办事项中记录
- 量化 - 估算修复工作量
- 优先级排序 - 与功能需求平衡
- 偿还 - 每个迭代分配时间
- 预防 - 代码评审、标准规范
Performance
性能
Optimization Rules
优化规则
- Don't optimize prematurely - Make it work first
- Measure before optimizing - Profile to find bottlenecks
- Optimize the right thing - Focus on hot paths
- Know the costs - Understand time/space complexity
- 不要过早优化 - 先让代码运行起来
- 优化前先测量 - 通过分析找到瓶颈
- 优化正确的部分 - 关注热点路径
- 了解成本 - 理解时间/空间复杂度
Common Performance Pitfalls
常见性能陷阱
| Pitfall | Solution | Detection |
|---|---|---|
| N+1 queries | Batch queries, use joins | Search for await in loops |
| Unnecessary computation | Cache results, lazy evaluation | Profiling |
| Memory leaks | Clean up references, use weak refs | Memory profiling |
| Blocking I/O | Use async operations | Code review |
| Large payloads | Paginate, compress, filter fields | Network profiling |
| No indexing | Add database indexes | Query analysis |
Find performance issues:
- Search for N+1 query patterns (await inside loops)
- Search for synchronous file operations
- Search for expensive operations in loops (e.g., JSON.parse in loop)
| 陷阱名称 | 解决方案 | 检测方式 |
|---|---|---|
| N+1 queries | 批量查询、使用连接 | 搜索循环中的await |
| Unnecessary computation | 缓存结果、延迟计算 | 性能分析 |
| Memory leaks | 清理引用、使用弱引用 | 内存分析 |
| Blocking I/O | 使用异步操作 | 代码评审 |
| Large payloads | 分页、压缩、过滤字段 | 网络分析 |
| No indexing | 添加数据库索引 | 查询分析 |
查找性能问题:
- 搜索N+1查询模式(循环中的await)
- 搜索同步文件操作
- 搜索循环中的昂贵操作(如JSON.parse)
Cleanup and Maintenance
清理与维护
Regular Maintenance Tasks
定期维护任务
- Update dependencies - Keep packages current
- Check for security issues - Run security audits
- Remove unused code - Delete unused files/exports
- Remove duplicate code - Extract and reuse
- Fix circular dependencies - Refactor to break cycles
- Update outdated patterns - Modernize legacy code
- 更新依赖 - 保持包为最新版本
- 检查安全问题 - 运行安全审计
- 删除未使用代码 - 删除未使用的文件/导出项
- 删除重复代码 - 提取并复用
- 修复循环依赖 - 重构以打破循环
- 更新过时模式 - 现代化遗留代码
Documentation
文档
What to Document
文档范围
✅ DOCUMENT ❌ SKIP
─────────────────────────────────────────────────────
Public APIs Obvious code
Architecture decisions (ADRs) Implementation details
Setup and deployment Every function
Non-obvious behavior Self-documenting code
Known limitations Temporary hacks (fix them)✅ DOCUMENT ❌ SKIP
─────────────────────────────────────────────────────
Public APIs Obvious code
Architecture decisions (ADRs) Implementation details
Setup and deployment Every function
Non-obvious behavior Self-documenting code
Known limitations Temporary hacks (fix them)Documentation Types
文档类型
| Type | Purpose | Location |
|---|---|---|
| README | Project overview, setup | Repository root |
| API Docs | Endpoint/function reference | Generated from code |
| ADRs | Architecture decisions | |
| Runbooks | Operational procedures | |
| Inline Comments | Non-obvious code explanations | In source code (rare) |
| 类型 | 用途 | 位置 |
|---|---|---|
| README | 项目概述、安装配置 | 仓库根目录 |
| API Docs | 端点/函数参考文档 | 从代码生成 |
| ADRs | 架构决策记录 | |
| Runbooks | 操作流程文档 | |
| Inline Comments | 非明显代码的解释 | 源代码中(罕见) |
Architectural Anti-Patterns
架构反模式
| Anti-Pattern | Problem | Solution | Detection |
|---|---|---|---|
| Big Ball of Mud | No clear structure | Define boundaries and layers | Map structure, analyze deps |
| Golden Hammer | Using one solution for everything | Choose right tool for job | Code review |
| Spaghetti Code | Tangled, unstructured code | Modularize, add structure | Check circular dependencies |
| Lava Flow | Dead code nobody dares remove | Document and delete | Find unused code |
| Copy-Paste Programming | Duplicated code everywhere | Extract and reuse | Find duplicate code |
| Magic Numbers/Strings | Hardcoded values without context | Use named constants | Search for hardcoded values |
| Circular Dependencies | Modules depend on each other | Introduce abstraction layer | Analyze dependency cycles |
| Leaky Abstraction | Implementation details leak out | Strengthen encapsulation | Check exports vs internals |
| 反模式名称 | 问题 | 解决方案 | 检测方式 |
|---|---|---|---|
| Big Ball of Mud | 没有清晰的结构 | 定义边界和层级 | 映射结构、分析依赖 |
| Golden Hammer | 用一种解决方案解决所有问题 | 选择合适的工具 | 代码评审 |
| Spaghetti Code | 代码混乱无结构 | 模块化、添加结构 | 检查循环依赖 |
| Lava Flow | 无人敢删除的死代码 | 记录并删除 | 查找未使用代码 |
| Copy-Paste Programming | 代码重复到处都是 | 提取并复用 | 查找重复代码 |
| Magic Numbers/Strings | 无上下文的硬编码值 | 使用命名常量 | 搜索硬编码值 |
| Circular Dependencies | 模块相互依赖 | 引入抽象层 | 分析依赖循环 |
| Leaky Abstraction | 实现细节暴露 | 加强封装 | 检查导出项与内部实现 |
After Writing Code Checklist
代码编写后检查清单
- Self-review before PR
- Ensure tests pass
- Update documentation
- Clean up debug code
- Check for unused code
- Check for duplication
- Commit with meaningful message
- PR前自审
- 确保测试通过
- 更新文档
- 清理调试代码
- 检查未使用代码
- 检查重复代码
- 用有意义的消息提交
Code Quality Mantras
代码质量格言
"Make it work, make it right, make it fast" - Kent Beck
"Any fool can write code that a computer can understand.
Good programmers write code that humans can understand." - Martin Fowler
"Simplicity is the ultimate sophistication" - Leonardo da Vinci
"The best code is no code at all" - Jeff Atwood"Make it work, make it right, make it fast" - Kent Beck
"Any fool can write code that a computer can understand.
Good programmers write code that humans can understand." - Martin Fowler
"Simplicity is the ultimate sophistication" - Leonardo da Vinci
"The best code is no code at all" - Jeff Atwood