AI 强制编写好代码:工程化质量约束引擎设计
在传统软件开发中,"好代码" 的实践 —— 全面的测试、清晰的文档、小型化模块、静态类型、可快速启动的开发环境 —— 往往被视为可选项。时间压力下,这些 "可选项" 通常被优先削减。然而,随着 AI 编码代理的普及,这一范式正在发生根本性转变。AI 代理不会在制造混乱后再进行清理,它们更像是 "会滚过狗屎并将其拖遍整个房子的 Roomba"。唯一的防护栏是我们设定并强制执行的约束。
本文基于 Logic.inc 团队的实践经验,探讨如何通过工程化手段设计一个 AI 时代的代码质量强制引擎,将原本 "可选" 的最佳实践转变为 "强制" 的技术约束。
100% 覆盖率:从度量指标到 AI 验证机制
最具有争议性也最具价值的约束是:强制要求 100% 代码覆盖率。这一要求初听之下令人怀疑,但实际使用后却成为秘密武器。
覆盖率作为 AI 验证工具
覆盖率在这里并非严格意义上的缺陷预防工具,而是确保 AI 代理双重验证其编写的每一行代码行为的机制。常见的误解是认为 100% 覆盖率意味着 "无 bug",或者我们只是在追逐一个容易被操纵的指标。这两种理解都不正确。
为什么必须是 100%?
- 95% 覆盖率:你仍在决定什么 "足够重要" 需要测试
- 99.99% 覆盖率:你无法确定
./src/foo.ts中未覆盖的那行代码是在你开始新功能工作之前就存在的 - 100% 覆盖率:发生相变,所有模糊性消失
在 100% 覆盖率下,如果某行代码未被覆盖,那一定是因为你刚刚主动做了某些事情。覆盖率报告变成了一个简单的待办事项列表 —— 列出你仍需编写的测试。这也减少了我们需要给予 AI 代理进行推理的自由度。
技术实现参数
-
覆盖率阈值配置:
{ "coverage": { "global": { "statements": 100, "branches": 100, "functions": 100, "lines": 100 }, "exclude": [ "**/*.d.ts", "**/test/**", "**/__tests__/**" ] } } -
测试执行策略:
- 每次代码变更后自动运行覆盖率检查
- 未达到 100% 时阻止提交
- 提供具体的未覆盖代码行列表作为 AI 的待办事项
-
覆盖率报告优化:
- 实时覆盖率仪表板
- 增量覆盖率跟踪
- 历史趋势分析
当模型添加或修改代码时,我们强制它演示该行代码的行为。它不能停留在 "这看起来正确" 的阶段,必须用可执行的示例来支持其实现。
文件结构与命名:AI 导航的工程优化
AI 代理导航代码库的主要机制是文件系统。它们列出目录、读取文件名、搜索字符串,并将文件拉入上下文。因此,应该像对待任何其他接口一样,深思熟虑地处理目录结构和文件命名。
语义化命名策略
一个名为./billing/invoices/compute.ts的文件比./utils/helpers.ts传达的信息要多得多,即使内部代码完全相同。帮助 LLM,给你的文件提供深思熟虑的组织结构。
命名约定参数:
- 业务领域优先:
{domain}/{entity}/{action}.ts - 避免通用名称:
utils、helpers、common - 使用具体动词:
calculate、validate、transform而非process、handle
小型化文件架构
优先选择许多小型、范围明确的文件。这改善了上下文的加载方式。AI 代理在将大文件拉入工作集时经常进行总结或截断。小文件减少了这种风险。如果一个文件足够短,可以完整加载,模型可以将其全部内容保持在活动上下文中。
文件大小约束:
- 目标:每个文件≤200 行代码
- 最大限制:≤500 行代码
- 函数 / 方法:每个≤50 行
在实践中,这将加速 AI 代理的工作流程,并消除一整类性能下降问题。
快速测试环境:AI 时代的开发基础设施
在传统世界中,你生活在一个开发环境中。这是你精心制作完美解决方案、调整事物、运行命令、重启服务器并逐渐收敛到解决方案的地方。
使用 AI 代理时,你做的更像是养蜂,跨进程进行编排,而不知道每个进程中具体发生了什么。因此,你需要培养一个良好健康的蜂巢。
测试执行性能参数
你需要自动化防护栏快速运行,因为你需要经常运行它们。目标是让 AI 代理保持短链:做一个小改动,检查它,修复它,重复。
性能基准:
- 10,000 + 个断言应在约 1 分钟内完成
- 数据库创建和迁移:≤10 秒
- 测试隔离:完全隔离,无状态污染
在我们的设置中,每个npm test都会创建一个全新的数据库,运行迁移,并执行完整的测试套件。这之所以对我们有效,是因为我们使每个阶段都异常快速。我们使用高并发性、强隔离性运行测试,并为第三方调用提供缓存层。
如果没有缓存,测试需要 20-30 分钟,如果你期望 AI 代理每个任务运行几次测试,这将增加数小时。
多环境并行执行
一旦你习惯了 AI 代理,你自然会开始运行许多代理。你将在一天内多次启动和拆除许多开发环境。这一切都必须完全自动化,否则你会避免这样做。
环境配置参数:
# 一键创建新环境
new-feature <name>
该命令创建一个新的 git 工作树,复制不在 git 中的本地配置(如.env文件),安装依赖项,然后启动你的 AI 代理,提示它与你一起编写 PRD。如果功能名称足够描述性,它甚至可能直接要求开始工作,假设它可以自行找出其余上下文。
关键延迟指标:
- 环境创建:≤5 秒
- 依赖安装:缓存优化,≤30 秒
- 代理就绪:≤2 秒
如果这需要几分钟并涉及大量调整和手动配置,你就不会这样做。如果它是一个命令并需要 1-2 秒,你会经常这样做。
最终的要求是能够同时运行每个环境。拥有一堆工作树没有帮助,如果你一次只能激活其中一个。这意味着任何可能冲突的东西(例如端口、数据库名称、缓存、后台作业)都需要是可配置的(最好通过环境变量)或以某种无冲突的方式进行分配。
类型系统:缩小 AI 搜索空间的约束引擎
更广泛地说,自动化强制执行尽可能多的最佳实践。从 LLM 中移除自由度。如果你还没有使用自动 linter 和格式化程序,从那里开始。使它们尽可能严格,并配置为在 LLM 完成任务或即将提交时自动应用修复。
但你还应该使用类型化语言。整个类别的非法状态和转换可以被消除。类型缩小了模型可能采取的行动的搜索空间,同时作为描述每层数据流的确切类型的真实来源文档。
类型系统配置策略
我们严重依赖 TypeScript。如果某物可以在类型系统中合理、清晰地表示,我们就这样做。
类型命名约定:
- 将语义意义推入类型名称
- 目标:使 "这是什么?" 和 "它去哪里?" 一目了然
- 示例:
UserId、WorkspaceSlug、SignedWebhookPayload
通用名称如T在编写小型自包含的通用算法时是可以的,但在真实业务系统中传达意图时帮助不大。
API 和数据类型一致性:
- 在 API 端,使用 OpenAPI 并生成良好类型的客户端,使前端和后端在形状上达成一致
- 在数据端,尽可能使用 Postgres 的类型系统,并为不适合简单列类型的不变量添加检查和触发器
- 使用Kysely为我们生成良好类型的 TypeScript 客户端
所有其他第三方客户端要么给我们提供良好的类型,要么我们包装它们以提供良好的类型。
工程化约束引擎架构
基于上述原则,我们可以设计一个完整的 AI 代码质量强制引擎:
1. 静态分析层
- 代码覆盖率强制:实时覆盖率监控和强制执行
- 复杂度分析:圈复杂度、认知复杂度阈值
- 依赖分析:循环依赖检测和预防
2. 结构约束层
- 文件结构验证:目录结构、命名约定检查
- 模块边界:强制模块化,防止上帝对象
- 接口一致性:API 契约验证
3. 类型安全层
- 类型覆盖率:确保所有代码路径都有类型定义
- 类型推断优化:帮助 AI 理解类型约束
- 运行时类型检查:开发环境中的额外安全层
4. 测试基础设施层
- 快速测试执行:并行化、缓存、增量测试
- 测试数据管理:隔离的测试数据生成和清理
- 环境管理:一键环境创建和销毁
5. AI 代理集成层
- 上下文管理:优化 AI 代理的代码库上下文
- 反馈循环:实时质量反馈给 AI 代理
- 约束学习:AI 代理学习并适应质量约束
实施路线图与风险控制
阶段化实施策略
阶段 1:基础约束(1-2 周)
- 配置 100% 覆盖率要求
- 设置基本 linter 和格式化程序
- 建立快速测试环境
阶段 2:结构优化(2-4 周)
- 重构文件结构为语义化命名
- 实施小型化文件策略
- 建立模块边界约束
阶段 3:类型系统强化(3-6 周)
- 增强类型定义覆盖率
- 实施 API 契约验证
- 建立运行时类型检查
阶段 4:AI 代理集成(4-8 周)
- 优化 AI 代理的上下文管理
- 建立质量反馈循环
- 实施约束学习机制
风险控制与监控
-
过度约束风险:
- 监控开发速度变化
- 定期收集开发者反馈
- 保持约束的可调整性
-
AI 代理适应风险:
- 监控 AI 代理的代码质量趋势
- 建立 AI 代理性能基准
- 定期调整约束参数
-
技术债务转移风险:
- 监控约束绕过模式
- 建立质量指标仪表板
- 定期进行代码质量审计
结论:从可选到强制的范式转变
AI 代理是不知疲倦且通常聪明的编码者,但它们只有在放置的环境中才有效。一旦意识到这一点,"好代码" 就不再感觉多余,而开始感觉必不可少。
是的,前期工作感觉像是一种税,但这是我们多年来一直在逃避的同样的税。所以有意地支付它。将其放在你的 AI 代理路线图上,让工程领导层资助它,最终交付你一直希望的代码库。
通过工程化的质量约束引擎,我们不仅强制 AI 编写好代码,更重要的是建立了一个可持续的代码质量文化。在这个 AI 代理时代,质量不再是可选项,而是工程效率的基础设施。
关键收获:
- 100% 覆盖率不是目标,而是 AI 验证机制
- 文件结构是 AI 导航的接口,需要精心设计
- 快速测试环境是 AI 效率的关键基础设施
- 类型系统是缩小 AI 搜索空间的约束引擎
- 质量约束需要工程化实施和持续优化
在 AI 强制我们编写好代码的时代,最好的防御是精心设计的工程化约束。这些约束不是限制,而是使 AI 代理能够发挥最大潜力的赋能框架。
资料来源: