1. 开场:面试官的问题
面试官靠在椅背上,嘴角带着一丝坏笑,抛出了一个让我后背微微发凉的问题:
"你用 AI 编程一年了,怎么保证 Claude Code 写出来的代码是对的?"
这个问题看似简单,实则暗藏杀机。它不是在问"你会不会用 AI 编程",而是在问"你敢不敢为 AI 写的代码负责"。我深吸一口气,笑着回答:
"直接上 Claude Fable 5 啊!"
面试官愣了一下,随即笑出了声。我知道,这一关过了。
2. 为什么"AI 写的代码"让人不放心
先别急着炫技,我们得承认一个事实:AI 生成的代码,天然让人不放心。原因有三:
- AI 会"一本正经地胡说八道":它可能生成语法完全正确、逻辑却完全错误的代码,而且看起来非常合理。
- AI 不了解你的业务上下文:它不知道你的系统里有哪些隐式约定、历史包袱和边界条件。
- AI 不会为 Bug 负责:代码是你提交的,线上出问题,背锅的是你,不是 Claude。
所以,面试官的问题本质上是:你如何建立对 AI 生成代码的信任机制?
3. 我的答案:Claude Fable 5 是什么
先澄清一下,Claude Fable 5 并不是某个官方发布的模型版本,而是我给自己的一套 AI 编程质量保障方法论 起的名字。它由五个环节组成,首字母连起来正好是 FABLE:
| 环节 | 英文 | 中文含义 | 核心动作 |
|---|---|---|---|
| F | Formalize | 形式化需求 | 把模糊需求写成可验证的验收标准 |
| A | Assert | 断言先行 | 先写测试和断言,再让 AI 写实现 |
| B | Build | 小步构建 | 让 AI 一次只写一小块,不憋大招 |
| L | Lint & Review | 静态检查 + 人工审查 | 机器查语法,人查语义 |
| E | Execute & Verify | 执行验证 | 跑测试、跑集成、看覆盖率 |
这套方法的核心思想很简单:不要把 AI 当成"写代码的人",而是把它当成"打字很快的实习生"。实习生写的代码,你总要 review 一遍再合入吧?
4. 五个环节逐一拆解
4.1 Formalize:形式化需求
很多人用 AI 编程失败,第一步就错了------需求描述得太模糊。
❌ 错误示范:
"帮我写一个用户登录功能。"
✅ 正确示范:
"实现一个邮箱 + 密码登录接口,要求:1)邮箱格式校验;2)密码使用 bcrypt 比对;3)连续失败 5 次锁定 15 分钟;4)返回统一的 JSON 错误结构。"
需求越具体,AI 的幻觉空间越小。我会把需求写成 验收标准(Acceptance Criteria),每条都是可测试的。
4.2 Assert:断言先行
这是我最喜欢的一步。在让 Claude Code 写任何实现代码之前,我先让它写测试。
python
# test_login.py
def test_login_success():
result = login("user@example.com", "correct_password")
assert result["code"] == 0
assert result["data"]["token"] is not None
def test_login_wrong_password():
result = login("user@example.com", "wrong_password")
assert result["code"] == 1001
assert result["message"] == "密码错误"
def test_login_locked_after_5_failures():
for _ in range(5):
login("user@example.com", "wrong_password")
result = login("user@example.com", "correct_password")
assert result["code"] == 1002
assert result["message"] == "账号已锁定,请15分钟后再试"
测试先写好了,AI 的实现代码就"有据可依"。它写出来的代码对不对,跑一遍测试就知道。
4.3 Build:小步构建
我从来不让 Claude Code 一次性生成一个完整的模块。我会把它拆成多个小任务:
- 第一步:只写数据库模型
- 第二步:只写密码加密工具
- 第三步:只写登录逻辑
- 第四步:只写接口层
每一步之间我都会 review 并运行测试。这样做的原因是:问题越小,越容易定位。如果一次生成 500 行代码,出错了你根本不知道从哪查起。
4.4 Lint & Review:静态检查 + 人工审查
机器能查的,交给机器:
bash
# Python 项目
ruff check .
mypy src/
# TypeScript 项目
eslint .
tsc --noEmit
但机器查不出"语义错误"。比如 AI 可能把 > 写成 >=,把 and 写成 or,这些静态检查不一定能发现。所以我会逐行 review AI 生成的代码,重点看:
- 边界条件是否处理
- 空值/异常是否处理
- 是否有隐藏的副作用
- 命名是否清晰
4.5 Execute & Verify:执行验证
最后一步,也是最硬核的一步:让代码跑起来。
bash
pytest --cov=src --cov-report=term-missing
我会关注三个指标:
- 测试是否全部通过
- 覆盖率是否达标(我一般要求核心逻辑 90% 以上)
- 是否有集成测试覆盖关键路径
只有这三项都满足,我才会把代码合入主干。
5. 一个完整的实战案例
下面用一个真实场景串一遍这套流程。需求:给电商系统加一个"优惠券核销"接口。
Step 1:Formalize
实现优惠券核销接口,要求:1)校验优惠券是否存在且未过期;2)校验是否属于当前用户;3)校验订单金额是否满足使用门槛;4)核销后优惠券状态改为"已使用";5)并发场景下不能重复核销。
Step 2:Assert
python
def test_redeem_success():
result = redeem(user_id=1, coupon_id=10, order_amount=200)
assert result["code"] == 0
def test_redeem_expired_coupon():
result = redeem(user_id=1, coupon_id=11, order_amount=200)
assert result["code"] == 2001
assert result["message"] == "优惠券已过期"
def test_redeem_not_owner():
result = redeem(user_id=2, coupon_id=10, order_amount=200)
assert result["code"] == 2002
def test_redeem_below_threshold():
result = redeem(user_id=1, coupon_id=10, order_amount=50)
assert result["code"] == 2003
def test_redeem_concurrent():
# 模拟并发核销同一张券
results = [redeem(user_id=1, coupon_id=10, order_amount=200) for _ in range(10)]
success_count = sum(1 for r in results if r["code"] == 0)
assert success_count == 1
Step 3:Build
让 Claude Code 分三步实现:先写优惠券查询逻辑,再写核销逻辑,最后加数据库行锁防并发。
Step 4:Lint & Review
bash
ruff check .
mypy src/
人工重点 review 并发部分,确认用了 SELECT ... FOR UPDATE 行锁。
Step 5:Execute & Verify
bash
pytest tests/test_redeem.py -v --cov=src --cov-report=term-missing
全部通过,覆盖率 95%,合入主干。
6. 面试官追问:如果 AI 生成的代码有 Bug 怎么办?
面试官显然不会就此罢休,他追问:
"就算你 review 了,AI 写的代码还是可能有 Bug,你怎么兜底?"
我的回答是三层防线:
- 测试兜底:核心逻辑必须有单元测试和集成测试,Bug 在测试阶段暴露。
- 监控兜底:上线后接入错误监控(Sentry)和日志追踪,线上问题第一时间发现。
- 回滚兜底:每次发布都是小步快跑,出问题可以秒级回滚到上一个稳定版本。
AI 编程不是"写了就不管",而是"写了更要管"。AI 提高了代码的生产速度,但质量保障的责任一点都没减少。
7. 总结
回到面试官最初的问题:"怎么保证 Claude Code 写出来的代码是对的?"
我的答案是:不保证,但用一套流程把出错的概率降到最低。
- 需求形式化,减少 AI 的猜测空间
- 测试先行,让 AI 的实现有据可依
- 小步构建,让问题无处藏身
- 静态检查 + 人工审查,机器查语法、人查语义
- 执行验证,用测试结果说话
这套方法我用了整整一年,线上事故率比纯手写代码时期还低。面试官听完,点了点头,在评价表上写下了什么。
我猜,那应该是个"通过"。