跳到主要内容

新功能设计、实现与审核准则

新功能不能只用“代码能运行”证明可以合入。作者和评审者必须先确认它解决了值得解决的问题,没有重复现有能力;再确认所选方案适合当前架构、接口和长期维护;最后用与风险匹配的测试和运行证据证明实现成立。

本准则适用于新增用户可见行为、公共接口、crate、子系统、平台或硬件能力,以及会扩大现有行为边界的改动。纯 bug 修复和保持行为不变的重构继续遵循 code-quality.md 及适用的领域准则;如果它们同时引入新能力,也必须遵循本文。

本文不重复 Rust 命名、模块拆分、错误处理、unsafe 和测试布局等实现规则。实现质量以 code-quality.md 为基线,更具体的领域准则在其适用范围内优先。

1. 分级要求

所有新功能都必须满足以下核心门槛:

  1. 有明确的问题、目标用户或调用方、使用场景和成功标准。
  2. 已检查当前 base、历史实现、相关 issue、开放 PR 和可复用抽象,不重复已有能力。
  3. 已查阅适用的规范、上游实现、硬件手册或其他 prior art,并记录版本和结论;没有适用资料时记录检索范围和不适用原因。
  4. 已比较现实可行的替代方案,说明为什么选择当前方案。
  5. 架构、接口、兼容性和风险与功能规模相称,没有未解释的 hack 或推测性泛化。
  6. 有能覆盖功能声明、在行为损坏时失败的测试或运行证据。

其余要求按风险适用。评审 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. 审核顺序

新功能评审必须按以下顺序进行:

  1. **必要性:**问题是否真实,用户或项目是否需要现在解决它?
  2. **重复性:**base、历史提交、相关 PR 或依赖中是否已经有同等或更好的能力?
  3. **语义与 prior art:**外部规范和成熟实现如何定义该能力,可借鉴什么,哪些做法不适合本项目?
  4. **方案选择:**当前方案是否比复用、扩展、替代实现或不做更合适?
  5. **整体设计:**边界、所有权、数据流、依赖方向、接口和兼容性是否成立?
  6. **实现质量:**具体代码是否直接表达设计,没有绕过边界或制造额外复杂度?
  7. **验证与交付:**测试、实际运行、文档、回滚和可观测性是否覆盖声明的能力?

如果前五步发现阻塞问题,应尽早反馈,不必先完成低层次风格检查。设计方向改变后,大量逐行评论可能失效;先修正方向更能减少返工。


3. 问题与成功标准

功能说明必须让不熟悉实现的评审者回答“为什么现在需要它”和“什么结果算完成”。至少包含:

内容必须回答的问题
问题当前用户、调用方或维护者遇到了什么具体限制?
证据issue、失败日志、缺失场景、标准差距、硬件需求或可复现命令是什么?
受众谁会直接使用 API、命令、配置、设备或行为?
场景至少一个具体输入、动作和期望结果是什么?
成功标准哪些可观察结果和测试证明功能完成?
非目标这次明确不解决什么,为什么可以独立留到以后?
不实现的影响保持现状会造成什么实际代价?

以下动机不足以支持新功能:

  • “可能以后用得上”,但没有当前调用方或已确认需求;
  • “其他项目有”,但无法说明本项目的问题和语义;
  • “这样更通用”或“更先进”,但没有可衡量收益;
  • 只为某个测试、样例或单一硬编码输入增加特殊路径;
  • 用大规模重写代替对当前问题的说明。

成功标准应描述行为和证据,不应绑定某个实现细节。这样才能公平比较替代方案,也能防止测试只验证当前代码形状。


4. 资料检索与 prior art

4.1 先检查项目内部

作者和评审者至少检查:

  • 当前 base 中的同名符号、相似能力、测试、配置和文档;
  • 相关路径的提交历史,以及曾被删除、替换或拒绝的实现;
  • 关联 issue、discussion 和仍开放的 PR;
  • 同层或相邻层的惯用设计、公共抽象和适配边界;
  • workspace 依赖或上游 crate 是否已经提供所需能力。

不能仅凭名称判断重复或不重复。必须阅读候选实现的语义、边界和调用方,并说明当前功能是独立、互补、冲突、重复还是被更完整方案替代。

4.2 外部资料的证据优先级

涉及外部语义、硬件或公共接口时,按以下优先级检索:

  1. 规范、标准、硬件手册、架构手册和正式 API 文档;
  2. 对应上游项目或参考实现的当前源码与测试;
  3. 成熟同类项目的实现和设计文档;
  4. 论文、RFC、正式提案和维护者讨论;
  5. 博客、问答和二手文章,只用于发现线索,不能单独作为关键语义依据。

引用必须记录可复核标识:标准或文档版本、源码仓库和 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 bufferdriver/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. 作者与评审者检查清单

作者提交前

  1. 问题、受众、场景、成功标准、非目标和不实现影响是否写清?
  2. 是否搜索 base、历史、issue、开放 PR、相似模块和依赖能力?
  3. 是否记录适用规范、上游实现、硬件手册或 prior art 的版本与结论?
  4. 是否比较保持现状、复用/扩展现有能力和新增实现?
  5. 功能是否位于正确层,所有权、数据流、错误和并发是否明确?
  6. 接口是否从真实调用方出发,最小、完整、类型化且可演进?
  7. 是否存在 hack 或临时 workaround;若有,是否满足受控例外要求?
  8. 是否只引入当前需要的抽象和配置,没有闲置公共 API?
  9. 每项功能声明和主要风险是否都有会失败的测试或运行证据?
  10. 文档、兼容、性能数据、上线、回滚和可观测性要求是否按风险覆盖?

评审者批准前

  1. 是否先完成必要性、重复性、方案和整体设计审核,再进入实现细节?
  2. 是否独立核对了关键内部事实和外部资料,而不是只接受 PR 叙述?
  3. 是否能解释为什么当前方案比现实替代方案更适合项目?
  4. 是否检查了跨层捷径、特殊分支、双重状态和推测性抽象?
  5. 是否从调用方角度验证 API、错误、兼容和演进语义?
  6. 是否确认测试被 runner 实际执行,并能在功能损坏时失败?
  7. 高风险领域是否有设计材料和具备相应知识的评审者?
  8. 所有跳过项是否都有具体、不降低质量的理由?
  9. 阻塞意见是否包含规则依据、具体问题、证据和修复方向?
  10. 最终结论是否解释了功能逻辑,而不是只汇报测试状态?

13. 核心原则

先证明问题值得解决,再证明方案值得采用;先审核边界和接口,再审核实现细节;只为已知需求增加必要复杂度,并用能失败的证据证明功能成立。

参考资料