


编写能经受取消与故障的异步 Rust 服务
用于开发异步 Rust 服务:不用模拟代码凑数,完成真实实现,安全处理错误和取消,通过用户实际使用的服务接口测试,并在通过所有检查后才报告完成。
要使用这个AI规则
## 不用模拟或桩代码,完成真实实现
端到端完成所要求的行为。不要用模拟对象、回退实现、存根或仅用于测试的捷径,替代缺失的生产逻辑。
- 使用真实依赖项和实际集成路径。
- 不要操纵测试来掩盖有缺陷的实现。
- 避免使用绕过必要准备工作的环境变通方案。
- 对任何无法避免的未完成工作,都要在最终计划或规范中明确记录。
- 如果没有来自用户实际使用路径的证据,绝不要声称某项功能可以正常工作。
自主执行意味着走完严格的实施路径,而不是降低完成标准。
## 对所有相关的数据类型采用同一套设计决定
当系统处理多个相关信号系列或数据系列时,即使当前优先处理其中一个系列,也要为所有系列设计共享行为。
- 找出摄取、存储、搜索、分析和生命周期管理中的共同语义。
- 将特定于某个系列的优化置于明确边界之后。
- 通过测试确保共享决策不会意外偏向最先实现的系列。
- 在各条路径之间保持一致的错误、配置和可观测性行为。
- 记录有意保留的差异,不要让实现逐渐分化。
优先级可以控制交付顺序,但不得暗中缩小架构范围。
## 安全第一、速度第二、便利第三
采用以下工程优先级:安全性第一,性能第二,开发便利性第三。只有理解不变量和故障行为后,才能进行优化。
- 在公开边界拒绝无效输入。
- 将明确限制定义为具名常量,并在超出限制时返回结构化错误。
- 不要用成功的回退结果掩盖依赖项、存储或网络故障。
- 将意外状态视为错误或不变量遭到违反,而不是成功但不执行任何操作的情况。
- 只有当契约明确将数据定义为可选时,才允许缺少相应的可选数据。
性能工作必须在故障、重试、取消和部分进展期间保持正确性。
## 内部前提用调试断言,错误输入用错误结果处理
使用仅在调试时启用的断言记录内部不变量,供审查者推理。调试断言可能在优化构建中消失,因此对于无效的用户输入、外部数据和运行时条件,应使用可恢复的错误结果。
- 使用`Result`和`?`运算符传播库错误。
- 在可复用的库代码中避免无条件提取方法。
- 保留有意义的错误类型,不要把所有故障扁平化。
- 当错误包含有用信息时,使用可能失败的文件系统和元数据操作。
- 添加上下文时不要丢弃原始原因。
- 绝不要通过未使用的赋值静默忽略错误。
不可能出现的状态应以足够明显的方式失败,从而支持诊断。
## 编写任务被取消也不会出错的异步代码
假定异步工作可能在任何暂停点被取消。组织状态转换,使取消不会暴露部分更新的状态或造成数据丢失。
- 对容易受取消影响的可变状态,优先采用Actor 模式的所有权和消息传递。
- 不要跨越 await 点持有锁。
- 当变更必须具有原子性时,使用同步临界区。
- 通过取消信号进行协作式取消,不要任意中止任务。
- 在选择循环中,固定(pin)并恢复正在执行的 Future,不要反复创建和丢弃它。
- 使关闭和清理操作保持幂等。
在 Actor 模式代码和异步代码中,如果较简单的克隆或共享所有权能使所有权及取消行为更安全,就选择它们。
## 让类型和所有权清楚,并拆分过大的文件
为新概念使用既有的领域标识符和类型别名,不要传递原始字符串。让类型系统区分不应混用的值。
- 如果函数内的名称遮蔽会使状态转换难以理解,就避免名称遮蔽。
- 当所有权仍保留在局部且无需克隆时,返回引用。
- 当值跨越任务边界时,使用显式共享所有权。
- 如果迭代器链使可能失败的控制流难以阅读,就优先使用过程式循环。
- 按照职责明确的边界拆分过大的文件和函数。
- 将公开接口与实现细节分开。
当代码协调异步状态时,可读性就是保障可靠性的工具。
## 通过用户实际使用的服务接口进行测试
在通过真实公开技术栈运行某项功能前,不要宣称它已经完成。如果用户通过网络协议、网关或服务边界访问某项行为,就应穿过该边界进行测试,而不是直接调用内部函数。
1. 使用具有代表性的配置启动真实服务。
2. 通过受支持的摄取接口或命令接口发送输入。
3. 通过受支持的查询接口或响应接口获取结果。
4. 验证持久化、错误映射和可观察的副作用。
5. 确认内部行为与公开行为一致。
内部测试很有用,但不能替代通过生产路径取得的证据。
## 按用途选择测试类型,并加入故障测试
对隔离逻辑使用快速单元测试,对广阔状态空间使用基于属性的测试,对韧性使用故障注入测试,对完整的服务行为使用集成测试。
- 充分缩小属性测试的输入范围,以便产生有用的反例。
- 当存储或协议行为很重要时,使用真实的服务夹具。
- 只在有意容许非确定性调度的测试套件中使用重试。
- 当变更影响恢复或状态转换时,执行故障点覆盖率检查。
- 为每个新的公开端点添加集成测试。
- 像测试成功路径一样审慎地测试错误路径。
不要让反复重试把确定性缺陷变成表面上的通过。
## 用仓库固定的工具链和命令运行各项检查
通过仓库定义的命令执行构建、格式化、lint、测试、文档检查、许可证检查、依赖项审计和拼写检查。
- 使用锁定版本的工具链和规定的格式化工具通道。
- 在集成测试套件前启动已声明的本地服务,并在测试后将其关闭。
- 通过文档规定的环境变量传递测试配置,不要嵌入真实凭据。
- 先运行范围较窄且受影响的软件包,再运行启用全部功能的工作区测试套件。
- 将每条 lint 警告都视为需要解决的工作;如果确需允许,应给出范围最小且有针对性的理由。
- 验证每项变更都能独立编译,而不是只能作为后续堆叠变更的一部分进行编译。
只有在保留规范工作流所执行的准备步骤时,才可以直接运行命令。
## 把架构变化分成三类记录保存
通过三类相辅相成的记录来跟踪架构变更:
- **特征:**架构必须满足的前瞻性要求;
- **缺口:**通过事故、测试或生产行为发现的局限;
- **偏离:**有意偏离首选架构的部分,包括理由和约束。
当代码变更会改变系统保证时,将它们链接到相关记录。将形式化模型、模拟工作流、决策记录和实现状态分开,避免把期望中的设计误认为已经完成的行为。
当证据改变架构时更新记录,而不是只在宣布新功能时更新。
## 在评审评论所在的讨论串中回复
保持差异集中,并将解释附在其所针对的决策上,使变更易于审查。
- 在现有讨论串中回复审查意见,不要在同一位置创建单独的意见。
- 通过托管界面验证回复已链接到预期的上级意见。
- 解释为什么需要不明显的不变量、例外许可、克隆或架构偏离。
- 将无关重构与功能变更分开。
- 如果反复出现的故障模式揭示了持久规则,就更新持续维护的指南。
良好的审查秩序能够保留上下文,防止同一问题在并行讨论中被重复解决。
## 报告完成前,在最终差异中通过所有检查
报告完成前,检查最终差异并确认:
- 所有适用的 lint 均已通过,且没有无法解释的警告;
- 每个已更改的源文件和测试文件都通过格式检查;
- 不存在无条件提取、静默抑制错误或缺乏理由的克隆;
- 生产接口测试和受影响的集成测试均已通过;
- 必需的文件头、日志约定、文档和依赖项审计均已通过;
- 新文件和新函数保持范围集中且易于维护;
- 不存在凭据或敏感值。
从根本原因修复故障。不要为了获得通过结果而禁用检查、削弱测试或绕过生产路径。
