AI Coding 之后,我为什么越来越少亲自写代码,却越来越重视测试和可观测性
当代码生成变得便宜,验证就变得昂贵:从代码 Review 转向系统验证,用集成测试与可观测性建立反馈闭环。
AI 编程工具刚开始普及时,大部分讨论集中在一个问题:
AI 写代码到底有多强?
但我实际在生产项目里使用一段时间以后,越来越觉得,这个问题的重要性正在下降。
因为代码生成已经足够便宜。
真正开始变贵的是:
验证。
也就是:
AI 写出来的代码,我凭什么相信它?
这件事正在改变我的工程习惯。
「能写出来」已经不是主要瓶颈
以前开发一个模块,代码本身就是主要工作量。
例如一个后端功能可能需要:
- 设计接口;
- 建表;
- 写 Repository;
- 写 Service;
- 写参数校验;
- 写错误处理;
- 写测试。
很多工作都需要程序员亲自完成。
今天,如果需求明确、项目结构清楚,这些实现类工作已经可以大量交给 AI。
尤其是:
- CRUD;
- 数据转换;
- 常规业务逻辑;
- 重构;
- 测试样板;
- 重复代码修改。
AI 的效率已经很高。
这时候继续优化“代码生成速度”,收益开始下降。
因为一个新的问题出现了:
AI 可以一次修改很多代码,但我没有能力用同样的速度 Review。
于是软件开发里的生产能力开始失衡:
Generation Capacity
>>>>>>>>>
Verification Capacity
生成能力快速增加。
验证能力没有同步增加。
这就是新的瓶颈。
为什么 AI 很容易制造「看起来正确」的代码
AI 生成代码最大的风险,并不是代码一定很差。
相反,它经常:
看起来非常合理。
变量名正确。
代码结构正确。
测试甚至可以通过。
但它仍然可能违反系统真实约束。
例如一个会员系统中的“增加会员时长”,表面逻辑可能非常简单:
查询当前会员
+
增加时长
+
保存
但生产环境真正的问题可能包括:
- 同一个 Webhook 是否会重复到达?
- 一个事件重复执行两次怎么办?
- 更新失败以后是否会重试?
- 重试是否会产生重复发放?
- 老系统是不是还在写同一份数据?
- 数据迁移过程中两个系统是否同时存在?
- 用户已经过期和仍然有效的处理逻辑是否一样?
- 第三方状态和本地状态不一致怎么办?
这些信息并不一定存在于某一个函数里。
它们分散在:
- 历史代码;
- 数据库;
- 产品规则;
- 第三方协议;
- 线上运行状态;
- 运维配置。
所以即使 AI 完全理解了当前文件,也不意味着它理解了整个系统。
这就是为什么:
局部代码正确,不等于系统正确。
我开始减少代码 Review,增加系统验证
传统代码 Review 很大程度上依赖:
人阅读代码,然后判断它有没有问题。
但当 AI 一次产生大量代码时,这种模式开始变得昂贵。
例如 AI 修改了:
- 15 个文件;
- 800 行代码;
- 20 个测试。
如果我要逐行阅读,我实际上并没有节省多少认知资源。
于是我开始改变策略。
相比于问:
这 800 行代码是不是每行都写得很好?
我更愿意问:
这个系统必须满足哪些不变量?
例如:
同一个订单不能重复发权益。
会员时长不能因为重试重复增加。
余额不能出现负数。
迁移前后的总会员时长必须一致。
旧接口不能继续修改新系统的数据。
然后尽可能通过自动化测试证明它们。
这实际上是在从:
Code-oriented Verification
转向:
Invariant-oriented Verification。
我认为这非常适合 AI Coding。
Hurl 和 Testcontainers 为什么对我很重要
后来我开始大量使用集成测试。
其中两个工具对我的工作方式影响比较明显。
一个是 Hurl。
它适合快速描述 HTTP 场景,例如:
注册
↓
登录
↓
兑换激活码
↓
查询会员状态
↓
验证权益
相比于手动打开 Postman 点接口,这种测试:
- 能进入 Git;
- 可以重复执行;
- 可以被 AI 修改;
- 可以进入 CI;
- 可以作为系统行为文档。
另一个是 Testcontainers。
它解决的是另外一个问题:
测试不能只 Mock 掉所有真实基础设施。
真实项目经常依赖:
- PostgreSQL;
- Redis;
- MQ;
- 其他服务。
如果所有东西都 Mock 掉,很容易出现:
单元测试全绿,上线以后 SQL、事务、约束或者序列化直接出错。
Testcontainers 让我可以用真实数据库执行测试。
这对 AI 代码尤其重要。
因为 AI 经常能生成“逻辑上正确”的数据库代码,却容易忽略:
- 真实 schema;
- constraint;
- transaction;
- nullable;
- index;
- isolation;
- migration。
这些问题必须让真实环境参与验证。
测试仍然不够
后来我又碰到另外一个问题:
测试通过,也不能证明生产环境没有问题。
生产系统会多出非常多变量:
- 用户历史数据;
- 网络抖动;
- 第三方异常;
- 重试;
- 并发;
- 长尾请求;
- 配置错误;
- 部署差异。
所以测试之后,下一层能力自然变成:
Observability。
也就是系统上线以后,我能否快速回答:
- 哪个用户出了问题?
- 哪个请求失败?
- 失败经过了哪些服务?
- 错误比例是否突然增加?
- 哪个版本开始出现问题?
- 数据库是不是变慢?
- 某个业务错误到底发生了多少次?
这也是为什么我开始关注:
- Error Tracking;
- Logs;
- Metrics;
- Trace。
过去我会把这些东西理解成:
运维设施。
现在我更倾向于把它理解成:
AI Coding 的验证系统。
AI 让 Change Velocity 上升。
Change Velocity 越高,对 Feedback Loop 的要求就越高。
一个简单的公式
我现在会这样理解 AI Coding:
有效生产力
=
代码生成速度
×
正确率
×
反馈速度
如果只提高第一项:
代码生成速度 × 10
但是:
错误发现需要 3 天
那么整体效率未必提高。
甚至可能因为变化过快,制造更多问题。
真正理想的系统是:
AI 10 分钟完成修改
↓
自动测试 3 分钟发现问题
↓
AI 自动修复
↓
CI 验证
↓
上线
↓
Observability 持续观察
人的任务主要集中在:
定义什么叫正确。
这比不断亲自写代码重要得多。
AI 时代,测试的价值反而提高了
以前很多开发者不愿意写测试,一个现实原因是:
写测试也需要大量时间。
现在这个约束已经发生变化。
AI 非常适合:
- 根据现有代码生成测试;
- 枚举边界条件;
- 建立测试数据;
- 补充失败案例;
- 修改大量重复测试。
也就是说:
测试代码本身的生产成本正在降低。
那测试的 ROI 就会明显提高。
以前:
写功能 2 小时
写测试 1 小时
很多人会觉得成本太高。
未来可能变成:
AI 写功能 15 分钟
AI 写测试 10 分钟
CI 自动验证
这个时候,不建立测试体系反而越来越不合理。
我现在更关心「反馈闭环」
现在评价一个项目工程质量时,我越来越少只看:
- 代码漂不漂亮;
- 抽象是不是高级;
- 架构是不是复杂。
我反而更关心几个非常朴素的问题:
改坏以后多久能知道?
出事故以后多久能定位?
重复执行会不会产生脏数据?
有没有办法自动证明关键业务链路正常?
某个用户反馈问题以后,能不能追溯完整上下文?
这些问题决定了:
AI 到底能不能安全地提高开发速度。
如果答案都是否定的,那么 AI 越强,风险反而越高。
最后的判断
AI Coding 让我越来越少亲自写机械代码。
但与此同时,我开始越来越重视:
- 测试;
- 数据一致性;
- 幂等;
- 日志;
- Trace;
- Metrics;
- Error Tracking;
- CI/CD;
- 发布验证。
这看起来像是从“开发”走向了“工程治理”。
但我认为这恰恰是 AI 编程发展以后很自然的结果。
因为:
当代码本身越来越便宜,证明代码正确的能力就越来越贵。
未来一个高效的软件工程师,也许并不是一天能亲手写多少代码。
而是:
能以多低的认知成本,让大量机器生成的代码持续保持正确。