Appearance
重构与解释技巧
让 AI 帮你解释代码和重构代码,是 Vibe Coding 里很实用的一步。但这两个任务很容易被问坏:你问“解释一下这段代码”,它可能逐行翻译;你说“帮我重构”,它可能顺手改掉接口、目录和测试。
比较稳的做法是先让 AI 解释“意图和风险”,再让它按你指定的目标重构。不要一上来就让它大改。
解释代码时,不要只要逐行说明
逐行解释适合学习语法,但对理解项目帮助有限。你真正需要知道的是:这段代码为什么存在、输入输出是什么、依赖了哪些外部状态、改它会影响哪里。
比如你接手一个订单计算函数,可以这样问:
text
@src/modules/order/calculateTotal.ts
请帮我解释这个文件。
我不需要逐行翻译,请重点说明:
- 这个函数在业务流程里负责什么
- 输入数据有哪些假设
- 哪些分支会影响最终金额
- 如果我要加优惠券逻辑,最可能改哪里这种问法会把 AI 从“代码讲解器”拉回“项目理解助手”。如果它开始泛泛总结,你可以继续追问:
text
请结合具体函数名和字段名说明,不要只说“处理订单数据”。解释代码时还有一个小技巧:让 AI 先画调用关系,再解释细节。
text
先列出这个文件被谁调用、它又调用了哪些函数。
然后再解释主流程。如果项目里有测试,顺手把测试文件也给它。测试往往比实现更能说明真实意图。
重构前先限定目标
“帮我重构一下”太宽了。AI 会自己决定什么叫重构,结果可能是拆函数、改命名、换库、调整目录一起上。你可能只想让代码可读一点,它却把行为也改了。
重构提示词里至少说清楚四件事:
- 当前问题是什么
- 这次重构的目标是什么
- 哪些行为必须保持不变
- 哪些文件或接口不能动
比如:
text
@src/modules/order/createOrder.ts
这个函数能跑,但现在太难读。
这次重构目标:
- 把参数校验、库存检查、订单创建拆成更清楚的小函数
- 保持 createOrder 的函数签名不变
- 不改变数据库表结构
- 不改路由层和前端调用
请先给重构计划,不要直接修改。先要计划,是为了防止 AI 把“可读性重构”做成“架构迁移”。计划里如果出现你没要求的动作,比如“新增 repository 层”“改 API 返回格式”,要先删掉再让它执行。
保留行为比改漂亮更重要
重构不是重写。尤其是正在跑的产品代码,最重要的是行为不变。
你可以明确要求 AI 用这些方式约束自己:
text
重构时请遵守:
- 外部调用方式不变
- 错误信息不变
- 返回字段不变
- 如果需要改测试,只能补充覆盖,不要降低原有断言如果原本没有测试,也可以让 AI 先补“锁行为”的测试,再重构:
text
请先根据当前实现补 3 个测试,用来固定现有行为。
测试通过后,再重构实现代码。这一步看起来慢,但它能帮你判断 AI 有没有偷偷改业务逻辑。对于金额计算、权限判断、状态流转这类代码,我会优先这样做。
一个完整示例对话
假设你有一个“能跑但难读”的函数:
ts
export async function createOrder(input, user) {
if (!user) throw new Error("unauthorized");
if (!input.items || input.items.length === 0) throw new Error("empty order");
let total = 0;
for (const item of input.items) {
const product = await db.product.findUnique({ where: { id: item.productId } });
if (!product) throw new Error("product not found");
if (product.stock < item.quantity) throw new Error("stock not enough");
total += product.price * item.quantity;
}
const order = await db.order.create({
data: {
userId: user.id,
total,
status: "pending",
items: {
create: input.items.map((item) => ({
productId: item.productId,
quantity: item.quantity,
})),
},
},
});
return order;
}第一轮先解释:
text
@src/modules/order/createOrder.ts
请解释 createOrder 的业务流程。
不要逐行翻译,重点说:
- 它做了哪些校验
- 哪些地方访问数据库
- 哪些错误会抛给调用方
- 哪些行为在重构时必须保持不变等 AI 解释完,再给重构目标:
text
现在请重构 createOrder。
目标:
- 拆出 validateOrderInput、loadAndValidateProducts、calculateTotal 三个辅助函数
- 保持 createOrder(input, user) 的签名不变
- 保持现有错误信息不变
- 不改数据库调用方式
- 不新增依赖
请只修改 @src/modules/order/createOrder.ts。如果它提出要顺手改返回结构,你要立刻收窄:
text
不要改返回结构,也不要新增 DTO。
这次只做函数拆分和命名整理。最后再让它解释 diff:
text
请检查这次 diff:
- 行为是否保持不变
- 有没有新增未要求的抽象
- 哪些地方需要我手动重点 review这一轮对话的关键不是提示词写得多,而是每一步都只推进一个目标。解释先服务于理解,重构只服务于指定问题,检查只服务于确认风险。
什么时候不要让 AI 直接重构
如果你还不理解这段代码在系统里的角色,不要直接让 AI 改。先解释,再找调用方,再决定是否动手。
下面几类代码尤其要谨慎:
- 支付、退款、金额计算
- 鉴权、权限、租户隔离
- 数据迁移和批处理脚本
- 线上兼容逻辑
这些地方不是不能用 AI,而是不能只靠“看起来更清爽”来判断重构成功。你需要测试、日志或明确的输入输出样例来兜住行为。
下一篇 给 AI 提供项目上下文 会继续讲:当项目变大时,怎么让 AI 更快理解你的目录、约定和边界。
重构与解释技巧
让 AI 解释代码和让 AI 重构代码,是两类看起来相似、实际要求不同的任务。解释代码时,你要让它帮你建立理解;重构代码时,你要让它在不破坏行为的前提下改变结构。
这两件事都不能只说“解释一下”或“优化一下”。前者容易得到逐行翻译,后者容易得到一堆自作主张的改动。
让 AI 解释代码,不要只要翻译
你看到一段陌生代码时,第一反应可能是:
text
解释一下这段代码。AI 会照着代码从上到下讲一遍,但你读完还是不一定知道它为什么这么写。更好的问法是让它围绕你的目的解释:
text
@src/cache/sessionStore.ts
我第一次看这个文件。
请帮我解释:
- 这个模块负责什么
- 外部通常怎么调用它
- 哪些状态会被持久化
- 如果我要改登录过期时间,应该看哪里
不要逐行翻译,先讲整体流程。解释代码时,最有价值的不是“第 12 行定义了一个变量”,而是它在系统里的位置。你可以要求 AI 画出调用关系、数据流,或者指出改动入口。
如果代码很长,可以先让它回答两个问题:
text
先不要重构。
请只回答:
1. 这个文件对外暴露了哪些能力?
2. 哪几段代码最容易影响用户可见行为?这能避免它一上来就建议改代码。
重构前先说清楚目标
重构不是“把代码变好看”。你需要告诉 AI,当前代码哪里让你不舒服,以及这次重构要解决什么。
常见目标可以很具体:
- 减少重复逻辑
- 拆出纯函数,方便测试
- 把 UI 和数据请求分开
- 降低单个函数的长度
- 保持行为不变,只改善命名和结构
如果目标没说清楚,AI 会自己决定什么叫“更好”。它可能换库、改组件层级、调整接口返回格式,最后代码看起来变整齐了,但风险更高。
一个更可控的提示词是:
text
@src/components/CheckoutForm.tsx
请重构这个组件。
当前问题:
- 表单校验、价格计算、提交请求都写在同一个组件里
- handleSubmit 太长,很难测试
重构目标:
- 把价格计算抽成纯函数
- 把请求提交保留在当前组件里
- UI 展示不要变化
- props 和对外行为保持兼容
请先给出修改计划,等我确认后再改。“先给计划,再改代码”很适合风险高的重构。你可以在计划阶段发现它是否准备越界。
要明确保留什么不动
AI 重构时最容易踩的坑,是把你不想动的东西一起动了。不要只写“不要大改”,这句话太模糊。
可以直接列出边界:
text
不要改:
- 路由路径
- API 请求参数
- 组件 props
- CSS class 名
- 已有测试文件的测试意图如果你正在维护一个线上项目,兼容性边界尤其重要。比如组件 props 已经被多个页面使用,重构内部实现可以,改 props 就会牵一发而动全身。
一个完整示例对话
下面是一个“能跑但难读”的函数。你可以把它交给 AI,但不要只说“帮我优化”。
ts
async function submitOrder(cart, user, coupon) {
let total = 0
for (const item of cart.items) {
total += item.price * item.count
}
if (coupon && coupon.type === "percent") {
total = total * (1 - coupon.value / 100)
}
if (coupon && coupon.type === "fixed") {
total = total - coupon.value
}
if (total < 0) total = 0
if (!user.address) {
throw new Error("address required")
}
const order = await db.order.create({
data: {
userId: user.id,
total,
address: user.address,
items: cart.items,
},
})
await email.send(user.email, "order created")
return order
}可以这样对话:
text
请帮我重构这段 submitOrder 代码。
目标:
- 行为保持不变
- 把金额计算抽成一个纯函数,方便单元测试
- submitOrder 里保留数据库写入和发邮件流程
- 不要引入新库
请先解释这段代码现在做了什么,再给出重构后的代码。如果 AI 给出的版本顺手改了优惠券逻辑,你可以继续追问:
text
你刚才把 fixed coupon 的处理顺序改了。
这次要求行为完全一致,请恢复原来的计算顺序,只做函数拆分。重构对话通常需要一两轮。第一轮看它是否理解行为,第二轮再让它收窄改动。不要急着接受一个“看起来更优雅”的结果,先确认输入输出和边界有没有变。
解释和重构可以分开做
如果你对代码还没理解,先让 AI 解释,不要马上让它改。等你知道哪些行为不能动,再开重构任务。
在大项目里,我更建议这样走:
text
第一步:解释这个模块的职责和调用关系,不改代码。
第二步:指出最适合重构的两个位置,说明原因。
第三步:只重构其中一个位置,并保持测试通过。把理解、判断、修改分开,Cursor 的结果会更像一个谨慎的同事,而不是一个拿到文件就开始整理的人。