Hotdry.
ai-systems

AI强制编写好代码:工程化质量约束引擎设计

面向AI代理时代,设计基于100%覆盖率、语义化命名、快速测试环境的代码质量强制引擎,通过工程化约束减少技术债务积累。

AI 强制编写好代码:工程化质量约束引擎设计

在传统软件开发中,"好代码" 的实践 —— 全面的测试、清晰的文档、小型化模块、静态类型、可快速启动的开发环境 —— 往往被视为可选项。时间压力下,这些 "可选项" 通常被优先削减。然而,随着 AI 编码代理的普及,这一范式正在发生根本性转变。AI 代理不会在制造混乱后再进行清理,它们更像是 "会滚过狗屎并将其拖遍整个房子的 Roomba"。唯一的防护栏是我们设定并强制执行的约束。

本文基于 Logic.inc 团队的实践经验,探讨如何通过工程化手段设计一个 AI 时代的代码质量强制引擎,将原本 "可选" 的最佳实践转变为 "强制" 的技术约束。

100% 覆盖率:从度量指标到 AI 验证机制

最具有争议性也最具价值的约束是:强制要求 100% 代码覆盖率。这一要求初听之下令人怀疑,但实际使用后却成为秘密武器。

覆盖率作为 AI 验证工具

覆盖率在这里并非严格意义上的缺陷预防工具,而是确保 AI 代理双重验证其编写的每一行代码行为的机制。常见的误解是认为 100% 覆盖率意味着 "无 bug",或者我们只是在追逐一个容易被操纵的指标。这两种理解都不正确。

为什么必须是 100%?

  • 95% 覆盖率:你仍在决定什么 "足够重要" 需要测试
  • 99.99% 覆盖率:你无法确定./src/foo.ts中未覆盖的那行代码是在你开始新功能工作之前就存在的
  • 100% 覆盖率:发生相变,所有模糊性消失

在 100% 覆盖率下,如果某行代码未被覆盖,那一定是因为你刚刚主动做了某些事情。覆盖率报告变成了一个简单的待办事项列表 —— 列出你仍需编写的测试。这也减少了我们需要给予 AI 代理进行推理的自由度。

技术实现参数

  1. 覆盖率阈值配置

    {
      "coverage": {
        "global": {
          "statements": 100,
          "branches": 100,
          "functions": 100,
          "lines": 100
        },
        "exclude": [
          "**/*.d.ts",
          "**/test/**",
          "**/__tests__/**"
        ]
      }
    }
    
  2. 测试执行策略

    • 每次代码变更后自动运行覆盖率检查
    • 未达到 100% 时阻止提交
    • 提供具体的未覆盖代码行列表作为 AI 的待办事项
  3. 覆盖率报告优化

    • 实时覆盖率仪表板
    • 增量覆盖率跟踪
    • 历史趋势分析

当模型添加或修改代码时,我们强制它演示该行代码的行为。它不能停留在 "这看起来正确" 的阶段,必须用可执行的示例来支持其实现。

文件结构与命名:AI 导航的工程优化

AI 代理导航代码库的主要机制是文件系统。它们列出目录、读取文件名、搜索字符串,并将文件拉入上下文。因此,应该像对待任何其他接口一样,深思熟虑地处理目录结构和文件命名

语义化命名策略

一个名为./billing/invoices/compute.ts的文件比./utils/helpers.ts传达的信息要多得多,即使内部代码完全相同。帮助 LLM,给你的文件提供深思熟虑的组织结构。

命名约定参数

  • 业务领域优先:{domain}/{entity}/{action}.ts
  • 避免通用名称:utilshelperscommon
  • 使用具体动词:calculatevalidatetransform而非processhandle

小型化文件架构

优先选择许多小型、范围明确的文件。这改善了上下文的加载方式。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。如果某物可以在类型系统中合理、清晰地表示,我们就这样做。

类型命名约定

  • 将语义意义推入类型名称
  • 目标:使 "这是什么?" 和 "它去哪里?" 一目了然
  • 示例:UserIdWorkspaceSlugSignedWebhookPayload

通用名称如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 代理的上下文管理
  • 建立质量反馈循环
  • 实施约束学习机制

风险控制与监控

  1. 过度约束风险

    • 监控开发速度变化
    • 定期收集开发者反馈
    • 保持约束的可调整性
  2. AI 代理适应风险

    • 监控 AI 代理的代码质量趋势
    • 建立 AI 代理性能基准
    • 定期调整约束参数
  3. 技术债务转移风险

    • 监控约束绕过模式
    • 建立质量指标仪表板
    • 定期进行代码质量审计

结论:从可选到强制的范式转变

AI 代理是不知疲倦且通常聪明的编码者,但它们只有在放置的环境中才有效。一旦意识到这一点,"好代码" 就不再感觉多余,而开始感觉必不可少。

是的,前期工作感觉像是一种税,但这是我们多年来一直在逃避的同样的税。所以有意地支付它。将其放在你的 AI 代理路线图上,让工程领导层资助它,最终交付你一直希望的代码库。

通过工程化的质量约束引擎,我们不仅强制 AI 编写好代码,更重要的是建立了一个可持续的代码质量文化。在这个 AI 代理时代,质量不再是可选项,而是工程效率的基础设施。

关键收获

  1. 100% 覆盖率不是目标,而是 AI 验证机制
  2. 文件结构是 AI 导航的接口,需要精心设计
  3. 快速测试环境是 AI 效率的关键基础设施
  4. 类型系统是缩小 AI 搜索空间的约束引擎
  5. 质量约束需要工程化实施和持续优化

在 AI 强制我们编写好代码的时代,最好的防御是精心设计的工程化约束。这些约束不是限制,而是使 AI 代理能够发挥最大潜力的赋能框架。


资料来源

  1. Logic.inc 团队实践:https://bits.logic.inc/p/ai-is-forcing-us-to-write-good-code
  2. SonarQube AI CodeFix:https://docs.sonarsource.com/sonarqube-server/latest/ai-capabilities/ai-codefix
  3. AI 驱动的代码重构:https://www.createq.com/en/software-engineering-hub/ai-driven-code-refactoring
查看归档