


让组件众多的大型代码库保持易读,修改时不出问题
在让 AI 处理由许多组件组成的大型代码库时使用。它给出代码写法、文件与改动大小、代码评审要点、测试、API 命名和构建流程的规则,让改动保持小而易读,不会弄坏其他部分。
要使用这个AI规则
## 避免只传 true/false 的参数,让调用一看就懂
在调用处优先考虑清晰性,而非追求技巧性。
- 直接将变量内联到字符串格式化占位符中,而不是按位置传递。
- 在语言支持的情况下,优先使用方法引用而非简单的闭包。
- 避免使用含义不明确的位置参数——裸露的布尔值、null/none 或数字字面量——这会迫使阅读者猜测其含义(例如 `foo(false)`)。优先使用枚举、具名方法、新类型(newtype)或其他能自我说明的 API 形式。
- 当无法采用自我说明的形式、必须保留位置字面量时,用内联注释 `/*param_name*/` 对其进行标注,注释内容需与被调用方的参数名完全一致。除非注释确实能提升清晰度,否则字符串/字符字面量不需要加注释。
- 让分支处理保持穷尽(不使用兜底的通配分支),这样新增的情况能在编译期被发现。
- 为新增的抽象(trait/接口)添加文档注释,说明其作用以及实现者应如何使用它。
- 遵循文件内既有的约定。不要在等价的写法之间来回折腾代码,除非确实能带来可读性或功能上的提升;也不要仅仅为了满足某种简写形式而添加类型注解。
- 不要创建只被引用一次的小型辅助函数。
## 不让核心组件继续膨胀,把文件和改动拆小
保持单元规模小,这样不相关的改动就不会互相冲突。
### 模块大小
- 优先新建模块,而不是扩大现有模块。模块的非测试代码行数目标控制在约 500 行以内;一旦文件接近约 800 行,除非有充分且已记录的理由,否则应将新功能放入新模块中。
- 对于已经吸引大量不相关改动的高频、核心编排文件,应最严格地执行这一原则;让此类文件专注于编排本身,而不是不断堆积独立的辅助函数。
- 从大型模块中提取代码时,应将其测试以及类型/模块文档一并迁移到新实现旁边,使不变量始终贴近拥有它们的代码。
### 抵制让最大的组件继续膨胀
- 最大或最核心的组件往往是新代码的阻力最小路径,而这正是它不断膨胀的原因。要抵制向其中添加内容的冲动。首先应思考是否有更合适的现有组件可以承载这部分代码,或者是否到了引入新组件的时候——并按需进行重构。在评审中,应对那些会不必要地扩大核心组件的改动提出反对意见。
### 改动规模
- 除非改动是机械性的,否则改动总行数应控制在约 800 行以内;涉及复杂逻辑的改动则应控制在约 500 行以内。
- 如果改动规模更大,应将其拆分为可供评审的多个阶段,并根据实际的差异内容、依赖关系和受影响的调用位置来划分阶段,先落地规模最小且逻辑自洽的那一部分。
## 在代码评审中找出破坏兼容的改动和无限膨胀的上下文
评审时应重点关注那些日后才发现会代价高昂的风险。
### 保持公开接口面尽量小
让每个组件的公开/导出 API 尽可能精简。优先采用私有内部实现搭配明确导出的公开接口的方式,并避免在生产代码中大量增加仅供测试使用的辅助函数。
### 保护模型/推理上下文
当系统维护着一个会在每次推理请求中发送给模型的上下文时,应对其加以保护:
- 以增量方式构建上下文;绝不重写历史记录。
- 避免频繁改动上下文,以免导致缓存失效。
- 所有被注入的内容都必须有明确的大小上限和硬性上限——不允许存在无界的项,也不允许任何单个项超过固定的 token 上限。
- 对于任何单独就可能超过较大规模阈值的新增项,都应标记出来进行额外的人工评审。
- 所有被注入的片段都应通过单一的、有类型约束的抽象来处理,而不是临时随意插入。
### 检测破坏性改动
主动排查外部集成接口中的破坏性改动,包括:公开 API、事件负载(即便是实验性的)、命令行参数、配置加载,以及恢复现有会话或持久化状态的能力。
## 用集成测试和快照测试检查真实行为
测试行为本身,保持测试的真实可信,并且不要把测试代码混入生产代码中。
- 对于核心行为的改动,优先编写针对真实实例的集成测试,而不是范围狭窄的单元测试。任何对该行为的改动都必须新增集成测试;应列举出需要覆盖的各项行为。
- 将单元测试放在专门的同级测试文件中,而不是内联写在实现文件里,并为该文件起一个有描述性的名字。不要在生产模块中添加仅供测试使用的函数,也不要仅仅为了遵循这条约定而重写已有的内联测试模块。
- 任何对用户可见渲染输出的改动都必须包含快照覆盖——新增或更新快照,并将差异审查作为该改动的一部分,使 UI 层面的影响始终清晰可见。
- 使用深度相等断言来对整个对象进行校验,而不是逐个字段比较,并使用能生成清晰差异输出的断言库。
- 不要在测试内部修改全局进程状态(例如环境变量);应将依赖项和标志位从外部传入。
- 不要测试静态定义的常量,也不要为已经移除的逻辑添加否定式测试。
- 在编写新的测试初始化代码之前,先检查是否已存在可用的测试辅助函数,以保持测试代码简洁、易读。
## 让 API 的命名和字段格式处处一致
在演进网络化或跨语言的 API 时,保持传输边界的一致性可以防止出现无声的破坏性问题。
- 将主要开发精力集中在当前 API 版本上;不要向已废弃的版本添加新的接口面。
- 按角色对负载进行一致的命名(请求参数用一种后缀、响应用一种后缀、通知用另一种后缀)。方法的暴露方式应为“资源名 + 动作名”,并对资源的单复数形式保持一致的约定。
- 为字段和枚举值确定一种统一的传输命名大小写约定,并在所有地方一致应用,只有在明确的兼容性需求下才允许例外。保持序列化器与类型生成注解的一致,使双方保持一致;当一方使用了显式重命名时,另一方也必须相应匹配。
- 对于可辨识联合类型,应在每个序列化器中使用显式标签。
- 在边界处优先使用纯字符串 ID,并在内部进行转换。时间戳应使用整数,并统一采用 `*_at` 的命名方式。
- 对于列表类端点,默认应实现基于游标的分页(输入为游标和数量限制;输出为数据数组和下一个游标)。
- 将实验性接口置于显式标记之后进行门控,并且每当 API 结构发生变化时,都要重新生成文档、示例和 schema 测试固件。
## 先运行格式化工具和范围小的测试,不改动沙箱防护
尊重工具链以及已经存在的各项约束。
### 环境与沙箱防护
- 切勿在没有明确意图的情况下新增或修改用于管理沙箱或环境能力标志的代码。这些防护机制之所以存在,是因为相关代码和测试是围绕特定的运行时限制编写的——例如,当网络访问之类的能力不可用时会提前退出的测试。悄悄移除或改动某个防护机制,会破坏这些原有的前提假设。
### 构建与工具链卫生
- 完成代码改动后自动运行格式化工具;无需为格式化操作单独请求批准。
- 优先运行与改动范围最贴近、最小相关的测试,将范围限定在被改动的组件上。只有在共享代码或核心代码发生改动时,才扩大到完整测试套件,并且在启动耗时较长的完整套件运行之前先进行确认。
- 当你改动了作为生成产物(如 schema、锁文件、自动生成的类型)输入的内容时,应在同一次改动中重新生成这些产物;产物是否漂移会在 CI 中得到验证。
- 如果构建工具需要显式声明源文件才能在编译期访问它们,那么每当你新增此类读取操作时,都应相应更新该声明。
- 对运行缓慢的构建和测试命令要有耐心;预期中的锁竞争可能会导致它们变慢。不要通过进程 ID 强行终止它们。
### 平台支持
- 除非某项功能明确是特定操作系统专属的,否则测试和功能都必须支持所有官方支持的操作系统。在系统允许组件运行于不同操作系统的场景下,应确保新增测试能够覆盖这些跨操作系统的配置。
