新功能设计、实现与审核准则
新功能不能只用“代码能运行”证明可以合入。作者和评审者必须先确认它解决了值得解决的问题,没有重复现有能力;再确认所选方案适合当前架构、接口和长期维护;最后用与风险匹配的测试和运行证据证明实现成立。
本准则适用于新增用户可见行为、公共接口、crate、子系统、平台或硬件能力,以及会扩大现有行为边界的改动。纯 bug 修复和保持行为不变的重构继续遵循 code-quality.md 及适用的领域准则;如果它们同时引入新能力,也必须遵循本文。
本文不重复 Rust 命名、模块拆分、错误处理、unsafe 和测试布局等实现规则。实现质量以 code-quality.md 为基线,更具体的领域准则在其适用范围内优先。
1. 分级要求
所有新功能都必须满足以下核心门槛:
- 有明确的问题、目标用户或调用方、使用场景和成功标准。
- 已检查当前 base、历史实现、相关 issue、开放 PR 和可复用抽象,不重复已有能力。
- 已查阅适用的规范、上游实现、硬件手册或其他 prior art,并记录版本和结论;没有适用资料时记录检索范围和不适用原因。
- 已比较现实可行的替代方案,说明为什么选择当前方案。
- 架构、接口、兼容性和风险与功能规模相称,没有未解释的 hack 或推测性泛化。
- 有能覆盖功能声明、在行为损坏时失败的测试或运行证据。
其余要求按风险适用。评审 checklist 中每项必须标记为“满足”“不适用并说明原因”或“阻塞并给出证据”,不能用一句“改动较小”跳过分析。
1.1 局部功能
同时满足以下条件的功能可以在 PR 描述中完成设计说明:
- 改动局限在一个既有模块或明确边界内;
- 不新增公共 API/ABI、持久化格式或跨 crate 依赖;
- 不引入新的
unsafe、并发模型、资源所有权或安全边界; - 可以用已有测试层和回滚方式充分验证。
1.2 共享功能
跨模块或跨 crate、增加共享 API、改变多个调用方,或新增可复用能力时,PR 描述必须有独立的“设计与替代方案”章节,并获得涉及边界维护者的审核。设计必须说明依赖方向、调用方、迁移方式和测试范围。
1.3 高风险功能
以下任一情况出现,都属于高风险功能:
- 新增或改变公共 API/ABI、syscall、协议、持久化格式或用户可见兼容语义;
- 新增 crate、子系统、跨层依赖方向或平台公共能力;
- 引入硬件协议、MMIO、DMA、IRQ、页表、启动或虚拟化契约;
- 引入新的
unsafe、并发模型、锁顺序、资源所有权或信任边界; - 改变默认行为,需要 feature gate、数据迁移、升级/降级或回滚;
- 对性能、资源上限、可靠性或安全性有重要影响。
高风险功能必须提供可脱离实现 diff 独立评审的设计材料,例如专门的设计文档、issue 或 discussion。材料必须在实现合入前获得认可;即使设计和实现在同一 PR 中,评审者也应先完成设计审核,再逐行审核实现。
2. 审核顺序
新功能评审必须按以下顺序进行:
- **必要性:**问题是否真实,用户或项目是否需要现在解决它?
- **重复性:**base、历史提交、相关 PR 或依赖中是否已经有同等或更好的能力?
- **语义与 prior art:**外部规范和成熟实现如何定义该能力,可借鉴什么,哪些做法不适合本项目?
- **方案选择:**当前方案是否比复用、扩展、替代实现或不做更合适?
- **整体设计:**边界、所有权、数据流、依赖方向、接口和兼容性是否成立?
- **实现质量:**具体代码是否直接表达设计,没有绕过边界或制造额外复杂度?
- **验证与交付:**测试、实际运行、文档、回滚和可观测性是否覆盖声明的能力?
如果前五步发现阻塞问题,应尽早反馈,不必先完成低层次风格检查。设计方向改变后,大量逐行评论可能失效;先修正方向更能减少返工。
3. 问题与成功标准
功能说明必须让不熟悉实现的评审者回答“为什么现在需要它”和“什么结果算完成”。至少包含:
| 内容 | 必须回答的问题 |
|---|---|
| 问题 | 当前用户、调用方或维护者遇到了什么具体限制? |
| 证据 | issue、失败日志、缺失场景、标准差距、硬件需求或 可复现命令是什么? |
| 受众 | 谁会直接使用 API、命令、配置、设备或行为? |
| 场景 | 至少一个具体输入、动作和期望结果是什么? |
| 成功标准 | 哪些可观察结果和测试证明功能完成? |
| 非目标 | 这次明确不解决什么,为什么可以独立留到以后? |
| 不实现的影响 | 保持现状会造成什么实际代价? |
以下动机不足以支持新功能:
- “可能以后用得上”,但没有当前调用方或已确认需求;
- “其他项目有”,但无法说明本项目的问题和语义;
- “这样更通用”或“更先进”,但没有可衡量收益;
- 只为某个测试、样例或单一硬编码输入增加特殊路径;
- 用大规模重写代替对当前问题的说明。
成功标准应描述行为和证据,不应绑定某个实现细节。这样才能公平比较替代方案,也能防止测试只验证当前代码形状。
4. 资料检索与 prior art
4.1 先检查项目内部
作者和评审者至少检查:
- 当前 base 中的同名符号、相似能力、测试、配置和文档;
- 相关路径的提交历史,以及曾被删除、替换或拒绝的实现;
- 关联 issue、discussion 和仍开放的 PR;
- 同层或相邻层的惯用设计、公共抽象和适配边界;
- workspace 依赖或上游 crate 是否已经提供所需能力。
不能仅凭名称判断重复或不重复。必须阅读候选实现的语义、边界和调用方,并说明当前功能是独立、互补、冲突、重复还是被更完整方案替代。
4.2 外部资料的证据优先级
涉及外部语义、硬件或公共接口时,按以下优先级检索:
- 规范、标准、硬件手册、架构手册和正式 API 文档;
- 对应上游项目或参考实现的当前源码与测试;
- 成熟同类项目的实现和设计文档;
- 论文、RFC、正式提案和维护者讨论;
- 博客、问答和二手文章,只用于发现线索,不能单独作为关键语义依据。
引用必须记录可复核标识:标准或文档版本、源码仓库和 commit/tag、硬件版本、访问链接,以及与本方案相关的具体结论。版本相关行为不能只写“参考 Linux”或“参考上游”。
4.3 借鉴而不是照搬
prior art 用来发现成熟语义、失败经验和更直接的实现方式,但不能替代本项目自己的设计判断。比较时必须说明:
- 可复用的是协议语义、接口形状、状态机、算法,还是仅仅命名;
- 对方的运行时、所有权、错误模型、兼容承诺和性能约束是否相同;
- 哪些部分适合直接复用或适配,哪些部分会破坏 TGOSKits 的层次边界;
- 如果复制代码,许可证、归属和依赖是否允许。
没有相关 prior art 是允许的,但必须列出检索的项目、关键词或规范范围,并说明为何不适用。发现 prior art 也不自动证明功能值得加入;必要性仍由本项目的真实问题决定。
5. 方案比较
必须比较所有现实可行的方案,而不是只描述已经写好的实现。除非明显不适用,至少考虑:
- 保持现状或不实现;
- 直接复用现有能力;
- 扩展现有接口、组件或适配层;
- 新增局部实现;
- 新增公共抽象、crate 或子系统;
- 借鉴上游或同类项目的实现方式。
推荐用紧凑表格比较:
| 维度 | 关注点 |
|---|---|
| 语义正确性 | 是否完整满足成功标准和外部规范? |
| 架构适配 | 是否位于正确层,是否保持依赖方向? |
| 接口代价 | 是否新增长期维护的公共面? |
| 实现复杂度 | 主路径、错误路径和并发状态是否容易理解? |
| 兼容性 | 对现有调用方、配置、ABI 和数据有何影响? |
| 资源与性能 | CPU、内存、I/O、启动时间和二进制大小的代价是什么? |
| 可测试性 | 是否能在正确层稳定验证,失败是否可观察? |
| 演进与回滚 | 将来修改或撤销的成本是什么? |
最终说明必须包含:选择了什么、为什么它最符合当前约束、放弃了什么、付出了什么代价。若多个方案在工程上同等有效,评审者应尊重作者选择,不以个人偏好阻塞。
6. 架构与实现方向
6.1 放在正确的层
审核功能属于哪个稳定边界:可复用逻辑、驱动核心、能力接口、平台实现、OS glue、用户态应用还是工具链。重点检查:
- 领域逻辑是否被迫依赖 OS、平台、设备或工具细节;
- 通用层是否出现板卡地址、magic IRQ、用户态 ABI 或运行时全局状态;
- 调用方是否需要穿透多层对象访问内部字段;
- 新依赖是否逆转已有层次,或把可选能力变成全局强依赖;
- 是否已有更合适的共享抽象可以扩展或适配。
实现前必须先确认现有功能是否已经满足需求,并核对所属框架是否已经规定了调用入口、生命周期、扩展点和错误处理。现有功能满足需求时,不得重复实现、绕过框架或在调用方再次执行同一流程;必须按照框架约定接入,包括既定调用入口、执行时序、状态所有权和错误传播。
现有功能不满足需求时,必须先说明具体的语义缺口,并优先扩展或修复拥有该职责的框架边界及其测试。只有能够证明 框架明确不负责该场景,或所需语义、生命周期确有差异时,才允许局部实现;不能只以“现有接口不好用”作为理由,也不能为了“复用”把不同语义强行塞进同一接口。
6.2 所有权、状态和数据流
新增资源或状态时必须说明:
- 谁创建、验证、拥有、共享和释放它;
- 状态转换、失败回滚和部分成功如何表达;
- 哪些路径可睡眠、可分配、运行在 IRQ 或 scheduler-sensitive 上下文;
- 锁顺序、原子同步、唤醒条件和取消/超时如何工作;
- 输入从哪个信任边界进入,在哪里校验和转换;
- 日志、指标或错误如何让失败可定位。
如果设计图或数据流图能显著减少歧义,高风险设计材料应提供图示;简单线性流程不必为了形式增加图。
6.3 识别 hack
以下通常说明设计是 hack、不直接或难以维护:
- 绕过既有能力边界,直接访问另一层的私有状态或底层对象;
- 为一个命令、测试名、设备型号或输入值硬编码分支;
- 同一事实由两个状态源维护,需要手工同步;
- 用全局变量、隐式初始化顺序或调用约定代替明确接口;
- 吞掉错误、伪造成功、静默 fallback 或用 timeout 掩盖状态错误;
- 为避免修正模型而在多处增加例外;
- 实现只满足新测试的字面断言,没有实现完整语义。
只有外部约束确实无法消除时才可接受临时 workaround。PR 必须写明约束、影响范围、保持的安全不变量、测试、诊断方式、移除条件和跟踪项。无法说明何时移除的“临时方案”应按长期架构审核。
7. 接口设计
接口审核从真实调用方开始。新增公共 API 时,同一变更中必须有实际使用者或可执行示例,以证明接口形状来自当前需求而不是猜测。
接口必须满足:
- 暴露完成领域动作所需的最小能力,不泄漏内部对象图和实现类型;
- 名称、参数、返回值和错误表达调用方真正关心的语义;
- 用类型、newtype、enum、bitflags 或验证后的配置表达约束;
- 可恢复失败返回可匹配错误,unsupported、权限、资源耗尽和无效输入不混淆;
- 可见性最小化,内部扩展点不因实现方便被永久公开;
- 同步/异步、阻塞、取消、超时、部分成功和资源释放语义明确;
- 不强迫调用方进行不必要分配、clone、锁定或跨层转换。
7.1 公共和持久接口
公共 API/ABI、syscall、协议、配置和持久化格式一旦发布,修改成本远高于内部接口。还必须检查:
- 现有 调用方和旧数据是否继续工作;
- 未知 flag、版本或字段如何拒绝或兼容;
- 新字段默认值是否保持旧行为;
- 升级、降级、迁移和回滚如何执行;
- 废弃路径、文档和兼容期限是什么;
- 是否需要熟悉该 ABI、协议或硬件的专门评审者。
为已知演进保留明确空间是必要的,但不能为了未知未来增加空 trait、未使用参数、无消费者的 builder、无实现的 feature flag 或过度通用的类型层级。
8. 控制复杂度与过度设计
评审者应在行、函数、类型、模块、crate 和系统层面检查复杂度。无法快速说明主流程、状态来源和错误路径,通常意味着设计仍需简化。
新增抽象至少应满足以下一项:
- 已有多个真实调用方共享同一语义;
- 当前功能存在确定且稳定的多实现边界;
- 抽象隔离了外部依赖、平台差异或资源所有权;
- 抽象显著减少已经存在的重复知识,并让调用方更直接。
以下不是新增抽象的充分理由:
- 以后也许出现第二个实现;
- 某种设计模式要求这样写;
- 可以让代码看起来更通用;
- 当前只有一个调用方,却预先暴露大量配置和扩展点。
未来可能性可以记录,但不能作为接受当前复杂度的理由。等真实需求出现、形状可见后再扩展,通常比提前猜测更安全。
9. 实现与变更形状
一个功能 PR 应是可独立理解和验证的最小完整变化:
- 一个 PR 只解决一个清楚的问题,并包含相关测试和必要文档;
- 大规模移动、重命名、格式化或无关重构与功能改动分离;
- 需要多步落地时,每个提交保持可构建、可测试,并说明依赖顺序;
- 新公共接口在同一 PR 中被真实调用,不能先合入闲置 API;
- 不合入未实现主路径的
todo!()、unimplemented!()、no-op 或假成功分支; - 非目标保持真正独立,不能把完成核心语义所需的错误路径推给未来工作;
- 实现与设计材料、PR 描述和用户文档保持一致。
如果功能规模大到评审者无法辨认主路径、关键边界或风险,应拆成可验证的纵向能力或先行的行为保持型重构。拆分不能让中间提交破坏主分支或引入无人使用的公共接口。
10. 验证与交付证据
10.1 功能声明必须对应证据
推荐在 PR 中维护简短映射:
| 功能声明或风险 | 验证层级 | 命令/用例 | 可观察通过条件 |
|---|---|---|---|
| 示例:设备完成后释放 DMA buffer | driver/QEMU/board | 具体命令 | 完成事件、无泄漏、重复运行通过 |
验证必须满足:
- 测试位于能最直接表达语义的最低层,并有必要的集成或运行覆盖;
- 单元、集成、QEMU、板卡或端到端测试按风险组合,不用宽泛构建代替行为验证;
- 测试在功能缺失、返回错误、遗漏状态转换或破坏兼容性时确实失败;
- 错误路径、边界值、资源清理、并发交错和 unsupported 路径按适用性覆盖;
- 新测试被实际 runner 发现、构建、选择和执行,没有隐藏在 opt-in 或静默 skip 路径;
- 运行证据来自当前 PR head,记录准确命令、目标、配置和成功/失败标志。
用户可见功能还必须更新使用文档和示例。文档中的命令应实际执行;退出码为零但没有到达功能后置条件,不算验证成功。
10.2 性能和资源声明
声称改善性能、内存、启动时间、二进制大小、吞吐或延迟时,必须提供可重复的前后数据,同时说明测试环境、工作负载、波动和非显然代价。没有数据时只能描述为设计预期,不能作为合入依据。
10.3 上线、回滚和可观测性
会改变默认行为、持久状态、生产配置或跨版本交互的功能,按适用性回答:
- 如何启用、禁用和确认功能正在使用;
- 启用或回滚是否需要停机、重启或重新生成数据;
- 升级、降级、回滚后再启用是否安全;
- 哪些日志、事件、指标或状态能证明功能健康;
- 哪些信号触发回滚,失败会影响哪些既有工作负载;
- 外部服务、固件、硬件或工具链依赖不可用时如何失败和排查。
局部、无状态、默认关闭且容易删除的功能可以将这些项标记为不适用,但必须说明依据。
11. 合入判断
11.1 阻塞问题
以下问题默认阻塞新功能合入:
- 无法说明真实问题、目标用户、成功标准或现在实现的必要性;
- 与 base、已有依赖或开放 PR 重复、冲突,或已有方案明显更完整;
- 涉及外部语义却没有查阅权威资料,或实现违反适用规范;
- 没有比较复用、扩展、替代方案和不实现的影响;
- 功能位于错误层次、逆转依赖、复制已有抽象或泄漏内部实现;
- 存在未解释的硬编码、跨层绕过、双重状态源、假成功或静默 fallback;
- 抽象、公共 API 或扩展点没有真实消费者或当前需求;
- 公共接口、ABI、持久化状态、资源所有权、并发或安全风险未解决;
- 高风险功能没有可独立评审的设计材料或缺少对应领域评审者;
- 缺少有效测试、当前 head 运行证据或必要用户文档;
- 测试不能在行为损坏时失败,或 CI/runner 实际跳过新增覆盖;
- 性能收益只有主张没有数据,且该收益是选择复杂方案的主要理由;
- 实现、设计材料、PR 描述和实际行为互相矛盾。
11.2 非阻塞建议
以下通常不应单独阻塞:
- 多个方案在事实、标准和工程原则上同等有效时的个人偏好;
- 不影响当前功能、接口或风险的未来优化;
- 没有项目规范依据的纯风格偏好;
- 已明确列为非目标、可独立实现且不影响当前语义完整性的后续能力。
非阻塞建议应明确标记,避免作者误以为必须扩大当前 PR。评审目标是让改动明确改善整体代码健康,而不是追求不存在的完美实现。
11.3 评审结论必须可解释
无论批准还是要求修改,评审结论都必须说明:
- 功能解决什么问题,为什么值得加入;
- 检查了哪些内部实现、外部资料和相关 PR;
- 当前方案相对替代方案的优势与代价;
- 架构和接口为什么适合项目;
- 哪些测试和运行证据证明功能成立;
- 仍有哪些已知限制、非目标和运行风险。
不能只用“CI 通过”“代码看起来不错”或“与上游类似”作为批准理由。
12. 作者与评审者检查清单
作者提交前
- 问题、受众、场景、成功标准、非目标和不实现影响是否写清?
- 是否搜索 base、历史、issue、开放 PR、相似模块和依赖能力?
- 是否记录适用规范、上游实现、硬件手册或 prior art 的版本与结论?
- 是否比较保持现状、复用/扩展现有能力和新增实现?
- 功能是否位于正确层,所有权、数据流、错误和并发是否明确?
- 接口是否从真实调用方出发,最小、完整、类型化且可演进?
- 是否存在 hack 或临时 workaround;若有,是否满足受控例外要求?
- 是否只引入当前需要的抽象和配置,没有闲置公共 API?
- 每项功能声明和主要风险是否都有会失败的测试或运行证据?
- 文档、兼容、性能数据、上线、回滚和可观测性要求是否按风险覆盖?
评审者批准前
- 是否先完成必要性、重复性、方案和整体设计审核,再进入实现细节?
- 是否独立核对了关键内部事实和外部资料,而不是只接受 PR 叙述?
- 是否能解释为什么当前方案比现实替代方案更适合项目?
- 是否检查了跨层捷径、特殊分支、双重状态和推测性抽象?
- 是否从调用方角度验证 API、错误、兼容和演进语义?
- 是否确认测试被 runner 实际执行,并能在功能损坏时失败?
- 高风险领域是否有设计材料和具备相应知识的评审者?
- 所有跳过项是否都有具体、不降低质量的理由?
- 阻塞意见是否包含规则依据、具体问题、证据和修复方向?
- 最终结论是否解释了功能逻辑,而不是只汇报测试状态?
13. 核心原则
先证明问题值得解决,再证明方案值得采用;先审核边界和接口,再审核实现细节;只为已知需求增加必要复杂度,并用能失败的证据证明功能成立。
参考资料
- Google Engineering Practices: What to look for in a code review
- Google Engineering Practices: The Standard of Code Review
- Google Engineering Practices: Small CLs
- Rust RFC Template
- Rust API Guidelines
- Linux Kernel: Submitting patches
- Linux Kernel: Adding a New System Call
- Kubernetes Enhancement Proposal Template