别再 tokenmaxxing:用「模块化 + 验证循环」把 AI 写代码变成靠谱工程
你肯定见过这种场景:
- 领导看 AI 写代码很快,兴奋到想“全员上 Agent”。
- 开发也爽,需求来了就让 AI 生成一坨,能跑就合。
- 两周后开始加班:改一个按钮,炸三个接口;修一个 bug,冒出十个新 bug。
问题不在 AI 会不会写代码。
问题在你给它的工程环境,像不像一个能“驯化”它的训练场(harness)。
所谓 harness engineering,听着新潮,骨子里就是一句老话:回到优秀工程实践。
- 边界清楚的模块化
- 严密的验证循环(verification loop)
这俩做不好,AI 输出越多,你离屎山越近。真的。🤦♂️
先把话说透:tokenmaxxing 到底在害你什么?
“tokenmaxxing”我用大白话翻译一下:
不看代码,只让 AI 拼命产出;你只在外面点点点,看能不能跑。
短期很爽。
- 你今天能早点下班一小时。
- Demo 能在会上跑起来。
中期开始掉坑:
- 代码风格不统一,逻辑重复,接口偷懒硬编码。
- 模块互相引用乱成一锅粥,谁都不敢删。
- 测试欠债爆炸,改动靠祈祷。
长期就是经典结局:团队被代码绑架。
你现在要下“血本”的两件事
别把钱都砸在买更贵的模型、更多的 token。
真正该花钱(和时间)的地方是:
-
模块化(Modularity):把系统拆成 AI 也不敢乱来的形状
-
验证循环(Verification loop):让 AI 每次改动都必须过关
你把这两件事做到位,AI 才能变成“靠谱队友”,而不是“自走式代码喷泉”。
模块化:给 AI 画地盘,它才不会乱跑
模块化不是“把文件夹分一下”。
模块化是:每个模块有明确职责、输入输出、依赖方向。
你可以照着这 4 条来拆
1)模块用“能力”命名,不用“页面/功能点”命名
- ✅
billing、inventory、auth、notification - ❌
userPage、orderFeatureV2、misc
AI 最怕 misc,因为它会往里面塞一切。
2)模块之间只通过接口说话
你要逼自己写出这种东西:
- DTO/Schema(入参出参结构)
- Service Interface(行为定义)
- Error Contract(错误码/异常约定)
AI 一旦有了“契约”,它就不敢随便改字段。
3)依赖只能单向
常见安全的方向:
api -> application -> domain -> infrastructure
最危险的方向:
- 领域层去 import 数据库 ORM
- 核心模块互相 require
你可以用工具强制约束(后面给清单)。
4)每个模块都要有“可替换性”
问自己一个问题:
我能不能把支付供应商从 A 换成 B,只改一个模块?
答不上来,模块就还不够干净。
推荐的仓库结构(给 AI 也能看懂的那种)
以常见后端项目为例(语言无所谓,结构思路通用):
repo/
docs/
architecture.md
module-contracts/
billing.md
inventory.md
src/
modules/
billing/
api/
application/
domain/
infra/
tests/
inventory/
...
scripts/
.github/workflows/ci.yml
docs/architecture.md 这份文件很关键。
它不是写给人看的,它是写给 AI 和新人 的。
内容别写玄学,写“禁令”和“边界”。
比如:
billing不允许直接访问inventory的数据库- 跨模块只能走
api层定义的接口 domain层禁止 importinfra
验证循环:让 AI 每次提交都被“自动审判”
验证循环的目标很简单:
任何一段 AI 生成的代码,都必须通过一套机器化的审查流程。
你不想每天靠肉眼审查几千行“AI 友情赞助代码”。
一条好用的验证循环长什么样?
我给你一个实战顺序(本地 + CI 都能用):
- 格式化(format)
- 静态检查(lint / type check)
- 单元测试(unit)
- 合约测试(contract)
- 集成测试(integration)
- 安全扫描(SAST / dependency audit)
- 构建产物校验(build)
越靠前越快,越靠后越贵。
你要让错误尽量在“最便宜”的阶段暴露。
测试分层怎么写才不痛苦?
很多团队写测试写到崩溃,是因为方向反了:
- 业务逻辑全塞集成测试
- 单元测试写一堆 mock 地狱
更舒服的做法:
单元测试:专打纯逻辑
- 金额计算
- 状态机流转
- 规则校验
这类测试写起来快,跑起来更快。
合约测试:专管“接口别乱改”
跨模块、跨服务最容易翻车。
你要把接口契约钉死:
- request/response schema
- 必填字段
- 枚举值
- 错误码
这样 AI 改接口时,会被测试直接按住。
集成测试:只覆盖关键链路
别贪。
选 3~10 条最赚钱、最要命的链路:
- 下单 → 支付 → 发货
- 登录 → 鉴权 → 权限校验
覆盖到位就够用。
把 AI 变成“守规矩的实习生”:提示词直接套用
你要明确告诉 AI:
- 改动范围
- 依赖禁区
- 必须补测试
- 必须过验证
模板 1:让 AI 在模块边界内改代码
把下面这段丢给你的 AI,记得填空:
你在 repo 中工作,只允许修改 src/modules/<模块名>/ 下的代码。
禁止跨模块直接调用实现层;跨模块交互只能通过 src/modules/<模块名>/api 暴露的接口。
任务:<你的需求>
输出要求:
1) 先给出你计划修改的文件清单(含路径),以及每个文件的改动目的。
2) 再输出代码 diff。
3) 必须新增或更新对应的测试:
- 业务逻辑写单元测试
- 跨模块接口写合约测试
4) 给出本地验证命令(lint/test/build)。
模板 2:让 AI 先写测试再写实现(超好用)
按 TDD 风格完成任务。
先写失败的测试(解释为什么失败),再写最小实现让测试通过。
禁止一次性生成大段实现代码。
每次输出都要附带:新增测试列表 + 运行方式。
任务:<你的需求>
AI 特别喜欢“一口气写完”,你得打断它。
一套可以直接抄的 CI 清单(不做这些就别放 AI 上线)
你不需要一开始就搞得像大厂。
把下面这些做起来,就能挡住 80% 的坑。
- [ ] 每次 PR 必跑:format + lint + type check + unit tests
- [ ] PR 必须有测试变更,除非打上
no-test-needed且写明理由 - [ ] 覆盖率不追求漂亮数字,但要守住底线(例如核心模块 70%+)
- [ ] 依赖安全扫描(npm audit / pip-audit / osv-scanner)
- [ ] 变更影响分析:跨模块改动必须 @ 对应模块 owner
- [ ] 合并后自动部署到预发,跑关键链路的集成测试
再加一条很“反人性”但极有效的规矩:
CI 红了,任何人都不许“先合再说”。
不然验证循环形同虚设。
避坑清单:你八成会踩的雷,我提前替你骂一遍 😅
- 把模块化理解成“多建几个文件夹”:边界和依赖没约束,等于没拆。
- 接口没契约:字段今天叫
userId明天叫uid,AI 改得开心,你线上哭得响。 - 测试只写 UI/端到端:跑一次十几分钟,团队很快就把它关了。
- 让 AI 大范围重构:没有合约测试的前提下,大重构就是抽盲盒。
- 把验证交给人工抽查:抽查=抽奖,迟早抽到事故。
一天内能落地的最小方案(真能照做)
如果你现在项目已经被 AI 生成代码塞满,也别慌。
今天就干三件事:
- 选一个最核心的模块(比如
billing),把它的边界写进docs/module-contracts/billing.md - 给这个模块补 10 条单元测试,专打核心规则
- CI 加上:lint + unit tests + dependency audit
这三件事做完,你会明显感觉:
- AI 乱改的空间变小了
- 出问题更早暴露
- 线上事故少一截
后面再慢慢扩到其它模块。
结尾:harness engineering 没那么玄乎
别把它当新概念。
它就是工程基本功回魂:模块化 + 验证循环。
你把地盘画清楚,把关卡搭起来,AI 才会变成生产力。
不然你会得到一座壮观的屎山,代码写得越快,埋得越深。