K3.5.2 Task 3.5

先写测试:3 轮迭代到 15/15。先写实现:7 轮到 12/15

先写测试再让 Claude 实现。测试失败给 Claude 具体、可操作的反馈。没有测试,Claude 迭代时没有清晰的通过/失败信号,浪费周期和遗漏边缘情况。

数据

一个引用提取器用两种方法在同一个 15 个测试的套件上构建:

方法迭代次数通过测试时间
先写测试315/1545 分钟
先写实现,后加测试712/1590 分钟

先写测试收敛更少轮次,因为 Claude 从一开始就有具体目标。先写实现需要 7 轮无聚焦的迭代,仍然失败了 3 个边缘情况——同样的测试在先写测试的方法里第 2 轮就被处理了。

工作流

  1. 写测试覆盖预期行为、边缘情况和性能要求
  2. 把测试分享给 Claude让它实现
  3. 跑测试——把完整输出分享给 Claude
  4. Claude 迭代——修失败的同时维持已通过的
  5. 重复直到所有测试通过

测试定义实现应该做什么。Claude 决定怎么做。即使 Claude 在迭代之间完全改变实现方法,测试仍然有效因为它们描述需求,不是方案。

反馈质量决定收敛速度

两个开发者在同一个 20 个测试套件上构建相同的工单分类器:

开发者分享的反馈迭代次数通过测试
开发者 A每轮完整测试输出320/20
开发者 B只说”X 个测试失败”817/20

开发者 A 分享了实际值 vs 期望值、堆栈跟踪和断言消息。Claude 能精确诊断根因。

开发者 B 只分享”3 个测试失败”。Claude 只能猜哪里出了问题。同样 3 个测试从第 4 轮到第 8 轮一直失败——是系统性模式,不是运气不好。没有具体的失败细节,Claude 不断犯同样的错误。

永远分享完整测试输出,不只是通过/失败计数。

防止回归:每轮分享全部

一个常见错误:只分享最新失败给 Claude。Claude 修了那个案例但破了 3 个之前通过的测试,因为它没看到全貌。

每次迭代都应该包含完整测试套件结果——所有通过和所有失败。这防止 Claude 为最新测试优化却牺牲早期的。完整结果在迭代循环中充当回归防护。

测试套件应该覆盖什么

对于一个测量圈复杂度、嵌套深度和函数长度的代码复杂度分析器:

  • 正确性测试 — 已知代码样本的预期指标值
  • 边缘情况 — 空函数、深度嵌套代码、单行函数
  • 性能基准 — 必须在时间限制内处理 10K 行文件
  • 输出格式断言 — 必需的 JSON 结构

三个维度——正确性、性能和格式——都应该从一开始就可测试。省略性能测试意味着 Claude 可能产出正确但很慢的实现。3 轮迭代后正确性通过但性能在 12 秒失败(目标 5 秒),分享两个结果让 Claude 在保持正确性的同时优化。不要放松基准——Claude 应该达到目标。

反模式

先实现后写测试。 写验证 Claude 构建的任何东西的测试锁定了可能不正确的行为。测试应该预先定义预期行为,不是给 Claude 的解释盖章。

手动抽查代替测试。 手动验证不一致、不可重复、无法跨迭代检测回归。测试套件每轮提供相同的可靠反馈。

让 Claude 同时写实现和测试。 测试编码的是你的需求。如果 Claude 写它们,它们编码的是 Claude 的解释——而那正是你要验证的,不是你要指定的。

只做增量测试添加。 从 1 个测试开始每次加的做法导致回归连锁。预先覆盖标准案例和关键边缘情况能防止这个。

迭代中扩展套件

当 Claude 的实现揭示之前没考虑到的边缘情况时加新测试用例是健康的迭代。套件增长以捕获新模式。这和从太少测试开始的反模式不同——它扩展的是一个全面的基础,不是从极简的开始建设。


一句话总结: 先写测试,每轮分享完整测试输出,让测试失败驱动迭代——3 轮收敛而不是 7 轮,还能捕获先实现遗漏的边缘情况。