AI 生成代码的真正风险是认知债务——代码能跑但无人真正理解;建议通过机制审查、关键路径手写、架构检查点等方式维持团队对代码的所有权。
上线 AI 生成的代码危险不在于模型会犯语法错误——那部分很容易 catch。真正的风险更隐蔽:团队合并了一段能工作的代码,但没有人真正拥有它。代码通过了,功能上线了,六周后一个小改动变成了一场 forensic 调查,因为批准它的工程师从未对其建立起真正的 mental model。
这就是认知债务。它比技术债务叠加得更快,因为它攻击的是团队日后用来偿还技术债务的东西:理解。
如果你使用 Claude Code、Codex 或 Cursor,答案不是禁用它们。答案是收紧生成和理解之间的循环。保持所有权的团队做法略有不同:他们审查机制而不是风格,选择性重打关键路径,强制架构检查点,以及从测试出发而不是凭感觉 prompt。
大多数团队首先注意到的是错误の失败模式。他们担心 AI 会生成糟糕的代码。但在实践中,现代 coding agents 生成的代码通常表面上看起来不错:清晰的命名、良好的结构、测试通过,也许甚至比匆忙编写的工程师的平均水平更好的格式。
问题始于工程师成为生成的操作者而不是系统的作者。如果你的交互模式是"描述任务、接受 diff、粗略浏览输出、合并",那么你外包的不仅仅是打字。你正在外包通常建立架构记忆的推理链条。
那种缺失的推理在后面以可预测的方式出现:
小型编辑感觉比应有的风险更大。
Review 意见漂移到格式化而不是行为。
工程师信任他们没有设计的测试。
Bug 需要更长时间才能定位,因为没人知道哪些假设是深思熟虑的。
重构停滞,因为团队记得表面,而不是结构。
一个有用的规则:如果你的团队无法解释为什么生成的实现是这样而不是两个附近的替代方案,你已经承担了认知债务。
这就是为什么这不是一个风格问题。这是一个所有权问题。
传统的代码审查习惯对 agent 生成的代码来说太弱了。快速浏览可能足以处理来自可信队友的人类编写 patch,因为作者可能在工作中带来了意图。对于 AI 生成的代码,patch 可能是连贯的,而其背后的推理是薄的、不一致的或完全缺失的。
所以审查流程需要一个更强的标准。不是更重的流程。是更好的问题。
一个有价值的 review comment 不是"我们可以重命名这个 helper 吗?"一个有用的 review comment 是:
为什么状态在这里派生而不是在边界处?
这个缓存依赖什么不变量?
如果这个异步步骤解析两次会怎样?
为什么这个 controller 在验证和映射而不是交给 action 或 service?
这些问题迫使 reviewer 重建设计。如果他们不能,这个 patch 就没有准备好,即使它在技术上是正确的。
对于任何涉及并发、持久化、缓存、授权、后台 jobs 或跨服务影响的东西,要求驾驶 agent 的工程师提供一个简短的说明。不是写小说。只要足够证明他们拥有代码的形状。
一份好的说明看起来像这样:
Implementation note:
- Validation stays at the HTTP boundary.
- Domain mapping happens in OrderDraftFactory so jobs and controllers share one path.
- Idempotency is enforced with a unique database key on external_event_id.
- Retry safety matters more here than raw throughput.
这个说明做了两件事。它给 reviewers 真正的钩子,并迫使工程师在合并前将生成的输出压缩成人类模型。
如果你的团队跳过这一步,review 就变成了走过场。
对认知债务最被低估的防御是选择性重打。
不是整个文件。不是辛苦活。只是编码系统实际决策的部分。
当人们听到这个时,他们通常反对说重打会浪费速度优势。这是错误的优化目标。稀缺资源不是击键次数。它是变更时刻的理解力。
在这些情况下重打代码:
事务边界
带有微妙过滤条件或连接的查询组合
重试或幂等行为
下游系统依赖的数据转换
Agent 工作流的 prompt 构建
这些是手工接触有意义的地方。重打会足够慢下来,让你在有问题、过于聪明或有坏假设时注意到。
不要把手动编码神圣化。让工具写无聊的部分:
DTO 和基本 schema
重复的 CRUD 管道
行为明显的简单适配器
重点不是纯粹性。重点是让工程师在心理上依附于塑造行为的代码。
一个实用的团队规则:自由接受生成的脚手架,但手动重写决策核心。
AI 工具擅长让本地决策看起来已完成。这正是为什么它们需要明确的架构检查点。没有它们,你最终会批准在隔离环境中工作但将复杂度推向错误层的代码。
在 Laravel 和全栈代码库中,这通常表现为 controller 膨胀、重复的编排逻辑、弱的领域边界,以及验证当前实现而不是预期行为的测试。
在合并一个非平凡的 AI 辅助 patch 之前,回答这五个问题:
验证应该放在哪里?
编排应该放在哪里?
这里稳定的领域边界是什么?
哪部分必须幂等或重试安全?
三个月后什么会变得痛苦?
如果这些答案模糊,停止生成更多代码。设计还没有准备好。
这就是当你在 prompt 时过于宽泛,agents 经常产生的东西:
public function store(Request $request)
{
$validated = $request->validate([
'email' => ['required', 'email'],
'plan' => ['required', 'string'],
]);
$user = User::firstOrCreate(
['email' => $validated['email']],
['password' => Str::random(32)]
);
if (! $user->hasStripeId()) {
$user->createAsStripeCustomer();
}
$subscription = $user->newSubscription('default', $validated['plan'])->create();
AuditLog::create([
'event' => 'subscription_created',
'email' => $user->email,
]);
dispatch(new SendWelcomeSequence($user->id));
return response()->json([
'subscription_id' => $subscription->id,
], 201);
}
它能工作。但它在一个 HTTP action 中承载了验证、用户创建策略、计费编排、审计日志和副作用。那不是一个 controller。那是未来的维护问题。
一个更紧凑的版本不仅仅是更好看。它有更清晰的所有权:
public function store(CreateSubscriptionRequest $request, CreateSubscription $action)
{
$subscription = $action->handle($request->validated());
return response()->json([
'subscription_id' => $subscription->id,
], 201);
}
现在真正的逻辑存在于一个带有显式测试的命名 action 中。Agent 仍然可以帮助编写它,但架构有了一条脊梁。
这就是重要的区别:生成的代码应该填充一个设计,而不是在一个 diff 中默默发明一个。
如果你想要更少的认知债务,不要以"构建功能 X"开始。从测试和约束开始。
模糊的 prompt 给你快速的代码和弱的所有权。先测试的 prompt 给你更慢的前期输出,但对行为、接口和失败模式有更强的控制。
Build a webhook handler for payment events and make it production ready.
Write Pest tests first for a Laravel webhook handler.
Constraints:
- Must be idempotent on provider event ID.
- Invalid signatures return 400 and never enqueue jobs.
- Processing should happen in an action class, not the controller.
- Retries must be safe.
- Use a fake event payload factory in tests.
After the tests, implement the minimal code to pass them.
Then explain the chosen boundaries in 5 bullets.
那个 prompt 做了三件重要的事:
它在实现前定义失败模式。
它约束架构而不是让模型去猜测。
它迫使 agent 产生一个你可以审查的解释。
这个模式适用于各种技术栈。对于前端工作,先定义渲染状态和交互测试。对于后端 jobs,先定义重试语义和副作用。对于 agent 工作流,先定义工具契约和恢复行为。
先测试的 prompt 不仅仅是为了正确性。它给工程师一个更好的记忆痕迹。首先编写或审查测试创建了预期行为的叙事。那个叙事比这周满足它的任何生成实现存活得更久。
跳过这一点的团队通常最终得到相反的东西:好看的代码和浅薄的记忆。
最健康的 mental model 不是"AI 替我写代码"。而是"我在与一个快速、自信但对后果不可靠的人结队编程"。
这个框架改变了你的工作方式。
你不会给初级人员一张模糊的 ticket,然后合并任何返回的东西。你设定边界、检查决策、重写关键部分,并坚持在行为重要的地方要有测试。
如果你想要一个你的团队下周真的能采用的方案,从这里开始:
自由使用 AI 进行脚手架、重复和搜索密集型编辑。
对非平凡工作要求先测试或先约束的 prompt。
在有风险的 diff 上要求简短的实施说明。
手动重写编码业务规则或系统边界的部分。
拒绝只讨论格式和命名的 review。
将因误解生成代码而产生的 bug 与正常缺陷分开跟踪。
最后一点很重要。如果你不衡量认知失误,团队会一直告诉自己速度很好,而所有权却在下面腐烂。
最强烈的警告信号是文化上的,不是技术上的:
工程师说"那是 agent 做的",仿佛 authorship 转移了。
人们犹豫是否触碰最近生成的模块。
同一个 reviewer 批准每个 AI 密集的 diff,因为其他人不想解开它。
合并后的修复聚集在误解的假设周围,而不是硬的边缘情况。
当这些模式出现时,你的流程是在奖励吞吐量而不是理解力。
使用 AI 移除打字,而不是移除思考。
这就是界限。如果一个工作流程让你的团队更快,并保持对行为、边界和权衡的清晰人类解释,保持它。如果它产生没有人一个月后能自信地重塑的通过代码,它太贵了,不管演示看起来多好。
实用的规则很简单:广泛生成,机械审查,选择性重写,并用测试锚定一切。
这种组合保持了杠杆作用并避免了陷阱。没有它,AI 生成的代码不只是增加技术债务。它慢慢地教会你的团队停止在头脑中持有系统,而一旦那个习惯落地,代码库每个 sprint 都会变得更难。