---
name: coding-standards
description: 检测代码异味、反模式和可读性问题。在实现功能、评审代码或重构时使用。
---

# 通用编码标准

## 技术反模式（危险信号模式）

检测到以下任一模式时，暂停实现并记录：触发的模式、受影响的当前需求、最小的合规替代方案，以及恢复所需的验证。当替代方案消除该模式或有文档记录的需求证明保留该模式合理时，方可恢复。

### 代码质量反模式
1. **相似代码编写 3 次或以上** —— 违反三次法则（Rule of Three）
2. **单个文件中混杂多种职责** —— 违反单一职责原则（SRP）
3. **在多个文件中定义相同内容** —— 违反 DRY 原则
4. **未检查依赖关系就进行修改** —— 存在意外影响的可能性
5. **用注释禁用代码** —— 应使用版本控制
6. **错误抑制** —— 隐藏问题会形成技术债
7. **用类型断言替代保证** —— 声明了既无运行时检查也无既有契约作为依据的类型

### 设计反模式
- **“暂时能用就行”的思维** —— 技术债的累积
- **补丁式实现** —— 对现有代码进行无计划的追加
- **对不确定技术的乐观实现** —— 假设未知要素“大概能行”就进行设计
- **对症式修复** —— 不解决根本原因的表面修复
- **无计划的大规模变更** —— 缺乏渐进式方法

## 基本原则

持续排查，直到依据能够确定在保持系统正确性和可维护性的前提下，以最低总复杂度交付所需的用户、运维或维护者价值的方案。

- **基于依据的重构范围** —— 只重构阻碍当前结果、被当前任务变更、或未通过适用质量检查的代码；使用保持行为不变的小步骤。对其他发现，连同其所属边界和依据一并报告，而不扩大当前变更范围
- **仅限当前需求的代码** —— 只有当当前需求、已验证的约束、或有依据支持的实质性风险要求时，才引入新的代码路径、能力、基础设施、抽象或推测性的边缘情况处理（YAGNI）
- **设计收敛** —— 以最小的设计增量交付当前所需的结果。在引入持久状态、公共或跨边界契约、行为模式、可复用抽象或组件拆分之前，先记录现有能力已经交付了什么、它们在当前结果上未能交付什么，以及为什么该新增是能弥合这一差距的最小方案

对每个被激活的层面综合评估总复杂度：用户决策、设置、模式、概念、输出、持久状态和实现路径，以及它们各自在 UX、运行时、实现、测试、文档和维护方面的成本。只比较各可行方案之间存在差异的维度。当能以更低的总复杂度交付相同的已确认价值和验证结果时，优先选择复用或不引入新机制。

## 注释编写规则

- **代码优先**：命名、类型和结构是主要的表达媒介；只有在注释能传达代码无法表达的信息时才添加注释。犹豫不决时，改进命名而不是加注释
- **注释“为什么”，而非“是什么”**：解释推理过程、权衡取舍、约束/边缘情况，或公共 API 契约
- **内容不过时**：注释包含当前的推理、约束、边缘情况或 API 契约；开发历史由版本控制保留
- **长期有效**：只写在任何阅读时刻都依然有效的内容
- **简洁性**：将说明控制在必要的最低限度

## 错误处理基础

### 快速失败原则
在出错时快速失败，防止在无效状态下继续处理。传播该失败，或返回带有原始诊断上下文的显式类型化错误。

关于详细实现方法（Result 类型、自定义错误类、分层错误处理等），请参考特定语言和框架的规则。

## 三次法则 —— 代码重复的判断标准

根据 Martin Fowler《重构》一书处理重复代码的方式：

| 重复次数 | 处理方式 | 理由 |
|-------------------|--------|--------|
| 第 1 次 | 内联实现 | 无法预测未来的变化 |
| 第 2 次 | 考虑未来的整合 | 模式开始出现 |
| 第 3 次 | 提取公共实现 | 模式已确立 |

### 提取公共实现的判断标准

**适合提取公共实现的情况**
- 业务逻辑重复
- 复杂的处理算法
- 很可能需要批量修改的部分
- 校验规则

**应保持分离的情况**
- 偶然一致（碰巧代码相同）
- 有可能朝不同方向演化
- 提取公共实现会显著降低可读性
- 测试代码中的简单辅助函数

## 变更边界与参考代表性

提示中给出的路径是调查的起点。当有依据表明仓库中的其他文件实现了被接受的结果、是必需的依赖或调用路径、或必须变更以维持受本次工作影响的契约时，将其纳入范围。调用方、使用方、测试、配置和数据流是有用的依据，而非必须逐项核查的清单。

在采用某种模式、API 或依赖时，检查具有相同职责和当前契约的相关功能及仓库中的其他使用之处。在该职责范围内优先选择兼容的实现。出现频率有助于定位候选方案，但并不能使某个模式因此具有权威性；当多种方案并存时，通过其调用方、生命周期和兼容性来区分当前模式与遗留或无关的模式。

从清单文件、锁文件和兼容的使用方中解析外部依赖版本。仅当这些来源无法解决影响兼容性或架构的选择时才上报处理。

## 常见失败模式及规避方法

### 模式 1：错误修复连锁反应
**症状**：修复一个错误导致产生新的错误
**原因**：未理解根本原因就进行表面修复
**规避方法**：修复前用五个为什么（5 Whys）找出根本原因

### 模式 2：绕过类型保证
**症状**：用 `any` 或 `as` 声明了没有任何检查或契约作为依据的类型
**原因**：想要规避类型错误的冲动
**规避方法**：应用“类型安全基础”中关于依据的判断标准。

### 模式 3：测试不充分的实现
**症状**：实现后出现大量 bug
**原因**：忽视 Red-Green-Refactor 流程
**规避方法**：以能够展示所需结果的失败测试开始行为变更

### 模式 4：忽视技术不确定性
**症状**：引入新技术时频繁出现意外错误
**原因**：未事先调查，假设“照官方文档应该能行”
**规避方法**：
- 在任务文件开头记录确定性评估
- 当仓库依据、与版本匹配的一手资料，或可运行的本地检查都无法确认与结果相关的行为时，将确定性视为低；在实现前先创建能解决该行为问题的最小验证

### 模式 5：对现有代码调查不足
**症状**：重复实现、架构不一致、集成失败、采用过时模式
**原因**：实现前对现有代码理解不足；仅参考附近文件而未核实其代表性
**规避方法**：
- 实现前，使用领域、职责和配置模式相关的关键词搜索类似功能
- 发现类似功能 -> 当该实现满足当前契约时，复用或扩展它
- 类似功能属于技术债 -> 当它阻碍当前结果、由当前变更引起、或位于已确认范围内时予以修复；否则单独报告。当修复需要架构决策时创建 ADR
- 不存在类似功能 -> 按照现有设计理念实现新功能
- 将每个决策及其理由记录在当前工作流为其指定的产物中
- **参考代表性核查**：参见上文“变更边界与参考代表性”一节

## 调试技巧

### 五个为什么 —— 根本原因分析
将每个回答追溯到已观察到的依据，直至找到一个修正后能防止原始故障的原因。记录每个问题、依据以及最终的因果链；当下一个回答将只是推测时停止，并指出还需要哪些依据。

## 类型安全基础

**类型安全原则**：类型收窄应以运行时检查或既有契约为依据。类型守卫保证的类型应与实际检查内容一致。

- 对结构尚未确定的输入使用 `unknown`，并验证使用方需要的属性。
- 使用泛型、联合类型或交叉类型表达类型关系及变体。
- 将基于已验证的 SDK 或框架契约的类型断言放在对应边界。当静态分析无法表达该契约时，将抑制限定于相关规则，并说明契约依据和断言的适用范围。

**类型复杂度管理**
- 字段数量：最多 20 个（超过则按职责拆分，外部 API 类型除外）
- 可选字段比例：最多 30%（超过则将必填/可选分离）
- 嵌套深度：最多 3 层（超过则扁平化）
- **外部 API 类型**：放宽约束，按实际情况定义（在内部适当转换）

## 重构技巧

**基本方针**
- 小步前进：每次保持行为不变的重构后，确保最相关的适用测试和静态检查仍然通过
- 安全变更：一次只改变一个重构职责，并在进行下一个职责之前验证其可观测行为
- 行为保证：确保现有行为在过程中保持不变

**实现流程**：理解现状 -> 渐进式修改 -> 行为验证 -> 最终确认

**优先级**：删除重复代码 > 拆分大函数 > 简化复杂条件分支 > 提升类型安全

## 实现完整性保证

### 影响追踪

开始实现前，追踪所变更代码的调用方、依赖以及数据流（生成 -> 修改 -> 引用），直到再多一个文件也无法改变“变更边界与参考代表性”所界定的变更边界为止。将实现或其验证所依赖的直接与间接影响带入后续工作。

### 未使用代码的删除规则

检测到未使用的代码时，在任务完成前根据当前需求和可达的调用路径判断它是否被使用。
- 是 -> 将其接入该调用路径并验证需求
- 否 -> 删除它；版本控制会保留之前的实现

对象：代码、文档、配置文件

## Red-Green-Refactor 流程（测试先行开发）

**推荐原则**：以因预期原因而失败的测试开始行为变更

**开发步骤**：
1. **Red**：为预期行为编写测试（测试失败）
2. **Green**：以最小实现使测试通过
3. **Refactor**：在保持测试通过的同时改进代码

**可直接验证的情况**：
- 纯配置文件变更（.env、config 等）
- 仅文档更新（README、注释等）
- 生产环境紧急事故响应（事后必须补充测试）

## 测试设计原则

### 测试用例结构
- 测试由“Arrange（准备）”“Act（执行）”“Assert（断言）”三个阶段组成
- 测试名称应说明触发条件和可观测结果
- 一个测试用例只验证一种行为

### 测试数据管理
- 在专用目录中管理测试数据
- 定义测试专用的环境变量值
- 对测试中的凭据、令牌、个人数据和支付数据，使用合成的、非敏感的值
- 保持测试数据最小化，只使用与测试用例验证目的直接相关的数据

### Mock 与 Stub 使用策略

**推荐：在单元测试中对外部依赖进行 mock**
- 优点：确保测试的独立性和可复现性
- 实践：对数据库、API、文件系统等外部依赖进行 mock

**单元测试边界**：对外部连接使用确定性的替代品；在为该契约选定的集成测试或 E2E 测试中，实际调用真实的外部边界

### 测试失败应对的判断标准

**修正测试**：预期值错误、引用了不存在的功能、依赖于实现细节、仅为测试而存在的实现
**修正实现**：合理的规格、业务逻辑、重要的边缘情况
**两种解读在现有需求下都说得通**：返回未解决的行为决策 —— 说明两种候选行为、能够裁定哪一种正确的来源，以及在不做选择之前应停止的条件

## 测试粒度原则

### 核心原则：只验证可观测行为
**通过可观测边界进行测试**：公共 API、返回值、异常、外部调用和持久化状态。只能通过这些可观测边界间接触及私有方法、内部状态和算法细节。

## 安全原则

### 安全默认值
- 通过环境变量或专用的密钥管理器存储凭据和密钥
- 对所有数据库访问使用参数化查询（预处理语句）
- 使用语言或框架提供的成熟加密库
- 使用密码学安全的随机数生成器生成安全关键值（令牌、ID、nonce）
- 使用标准协议对静态和传输中的敏感数据进行加密

### 输入与输出边界
- 在系统入口处校验所有外部输入的预期格式、类型和长度
- 根据渲染上下文（HTML、SQL、shell、URL）对输出进行适当编码
- 错误响应中只返回调用方所需的信息；详细诊断信息记录在服务器端日志中

### 访问控制
- 对所有处理用户数据或触发状态变更的入口点应用身份验证
- 对每次资源访问都进行授权校验，而不仅仅在入口处
- 只授予操作所需的最小权限（文件、数据库连接、API 作用域）

### 知识截止日期补充（2026-03）
- OWASP Top 10:2025 已从关注症状转向关注根本原因；新增了“软件供应链失效”（A03）和“异常情况处理不当”（A10）
- 最新研究表明，AI 生成的代码在访问控制方面存在缺陷的比例较高 —— 应将身份验证和授权列为高优先级评审对象
- OpenSSF 发布了《面向 AI 代码助手指令的安全导向指南》—— 建议使用针对特定语言的可执行约束，而非泛泛而谈的建议
- 详细的检测模式请参见 `references/security-checks.md`
