02

第 2 章:Test–Code 循环——为什么测试代码比功能代码更重要

你一定要花非常多,超过 50% 的时间来写 testing code。

Cover Image for 第 2 章:Test–Code 循环——为什么测试代码比功能代码更重要

“你一定要花非常多,超过 50% 的时间来写 testing code。”

延伸阅读:在无状态世界里,为什么测试案例库是唯一可靠资产,参见AI 的无状态性与 Context Window

1. 生产力悖论:为什么过去大家都不写测试

在 AI 之前,大家其实都知道 test code 很重要,但实际上 99% 的团队都不会去写 test code。原因在于:

  • 人力既充足又不充足
  • 人力“充足”体现在:有测试工程师帮我们测试
  • 不充足体现在:需求太多了,你光是写功能代码就已经来不及了,更不要说写这个测试代码了
  • 如果有人能帮你手工测试,为什么还要写测试代码?

这个逻辑在过去是说得通的,但是在今天就不行了。

2. AI 把团队“压扁”了:脑容量 vs 代码量

今天有了 AI 的帮助,每个人的产能都太高了,直接导致团队人数的减少。

“不管 AI 再怎么强,人脑还是要对项目有掌控力的。你一旦放松了掌控力马上就会崩溃。”

过去,一个项目有十个人的团队,每个人都可以分担一部分,而且大家做得又慢,时间周期又长,这样大家有充分的时间去理解、掌握代码里的每一个细节。

但在今天,过去十个人做的事,可能一个人就能做完,而且时间只有以前的三分之一。
这就意味着:你要在一颗大脑、三分之一的时间里,去记住和理解过去“一个团队 × 三倍时间”才能掌握的信息。

对 AI-native 团队来说,这是一个非常巨大的挑战。

3. 用海量测试覆盖人脑覆盖不了的地方

在这样的情况下,如何保证软件质量?一个非常自然的想法就是:

“用海量的 test code 去 cover 你人脑 cover 不了的部分。”

  • AI 非常善于写 test code,因为 test code 没什么难度,纯粹是苦工
  • 人是不喜欢干苦工的,但 AI 没有任何问题
  • 我们已经非常清楚 AI 写出来的代码是不可靠的,人脑也不可能完全 review 得过来

在这种前提下,唯一有效的自我保护方式就是用海量的 test case 去 cover:

  1. 你在写新 feature 的时候,就已经保证新 feature 大概率是对的
  2. 未来你在改别的代码的时候,如果不小心改到了这里,你也会马上知道——通过测试用例来发现 AI 越界,动了不该动的东西

A test safety net catching the code waterfall

4. 在混沌中编程:Test–Code 的本质

"今天的 AI 编程,你可以理解成是一种在不确定性中、在混沌中、在混乱中的一种编程。我们已经非常清楚地知道 AI 写出来的代码是不可靠的。"

延伸阅读:人类评审如何与测试驱动的质量保证相结合,参见Human-in-the-loop 与新人培养

但我们又不得不让 AI 来写代码,因为它的产能实在太高了。我们不得不接受这种混乱,不得不接受这种不确定性。

在这种情况下,你唯一有效的保护自己的东西,就是你的测试用例:

  • 只要你保证你对测试用例足够尊重
  • 在评审测试用例的时候足够用心
  • 不随便修改测试用例

测试用例就可以始终保护好你各个层级、各个 scope、各个环节。

5. 管理 AI 生成的测试:框架与约束

第一点,test code 当然要符合你的要求,最开始的时候还是要定好测试的框架和测试的 convention。

因为 AI 写代码的时候,它会去参考临近的或者相关的代码:
你已经有了一些测试案例放在那里,它就会用类似的 pattern 去写,这样大概率是没有问题的。

第二点,你在修改代码、评审代码的时候,一定要花非常多的时间和注意力去看:AI 到底有没有动测试案例?

  • 如果测试代码真的被动了,你一定要想清楚:
    • 凭什么动?
    • 能不能动?
    • 是不是应该动?
  • 如果动测试用例都解释不清楚,说明你这次工作可能就应该重新来过

6. 缺陷爆炸:为什么“只多写一点代码”会变成灾难

在硅谷有很多团队是做 AI 测试相关的,他们有一张曲线图:

  • 一条曲线是“全人类的代码量/工程师产能”,随着时间逐步上升,斜率比较低而且比较稳定
  • 代码产能上升意味着工程师人数在上升,测试工程师数量也同步上升
  • 所以随着代码产量增加,你能够解决的 bug 数量也在增加,这三条线相对保持一致

但出现了 AI 之后,这个情况变了:

  • AI 让人类的代码产量这条线被直接拉升,斜率变得非常大
  • 随着代码产量上升,缺陷数也会被直线拉升
  • 由于是 AI 写的、写得太快,而且团队人数减少,缺陷上升的速度可能比代码上升的速度还要快

于是就变成了:

缺陷数量的斜率最高,其次是代码数量,最后才是测试工程师数量。

测试工程师数量不可能一夜之间扩大 3–4 倍。
这意味着:一夜之间多出来几倍的缺陷数,是人类无法 cover 的。

这是一个非常危险但必然发生的趋势,我们已经在今年看到很多云厂商、互联网基础设施厂商出了很多事故,原因就在于此。

7. Test–Code 循环:从“代码为主”到“测试为主”

“test 到 code 的循环,本质上是因为 code 产能上升太快了。”

在这个情况下,你既要 cover 上升更快的缺陷数量,又要接受“人脑更少、人力更少”的现实。
解决方案只有一个:用海量的 test case 去填。

这也解释了为什么在这个背景下,测试代码会比功能代码更重要:

  • 以前:功能代码是最重要的,测试代码大家口头上说重要但实际一行都不写
  • 现在:测试代码比功能代码重要得多

因为很多时候,AI 修改的功能代码已经远远超出了你的认知负担和认知极限,你根本把握不住。
这个时候,你唯一能够信赖的,就是你那一整套海量的测试代码:测试代码通过,说明变更大概率还是可控的;测试代码不过,建议认真评审,甚至尝试重新做一遍这件事。

在深水区 debug 场景下,测试如何与日志和文档配合使用,参见Debugging:在深水区和 AI 一起查 bugAI-Native 工作流:Plan–Act、Test–Code、Doc–Code–Doc