页面打开了,按钮点了也没报错,说明刚才那条路径走通了。至于输出对不对、换一组输入会不会出错,还得接着验。
到了维护这一步,还要知道下一次需求该改哪里,改完会影响谁。一个函数看着只管价格筛选,调用完,旁边列表的顺序却变了,这就得有人顺着代码往回找。
AI 可以很快写出一段实现。审查如果只盯着眼前的效果,理解代码关系的工作就容易留给下一位。下一位可能是过两周的自己,到时候代码不认识你,你也不太认识代码,连甩锅都缺个合适的对象。
筛个价格,怎么把原数组也改了
假设需求很简单:给商品列表加价格区间筛选,保留原来的展示顺序,不改原始数组。
在这个简化示例里,价格和区间上下界都是有效数值。实现却写成了这样:
js
function filterProductsByPrice(products, minPrice, maxPrice) {
products.sort((a, b) => a.price - b.price);
return products.filter(
(product) => product.price >= minPrice && product.price <= maxPrice,
);
}
它能运行,也能筛出价格范围内的商品。如果测试只断言"返回的价格都在区间内",这段实现可以通过。
麻烦出在排序。JavaScript 的 sort() 会原地修改数组,MDN 对这一点有明确说明。其他地方如果也持有这个数组,读到的顺序就可能跟着变。你让它装个筛网,它连货架也重新摆了一遍。

只做需求里的筛选,代码其实是这样:
js
function filterProductsByPrice(products, minPrice, maxPrice) {
return products.filter(
(product) => product.price >= minPrice && product.price <= maxPrice,
);
}
前一段违反了"不改原数组"和"保留顺序"的要求,已经有正确性问题。难维护的地方在于,函数名没告诉你它还负责排序。另一个列表出了问题,排查的人得追着调用链,才能找到这个藏起来的副作用。
类似的麻烦也会藏在模块之间。页面和公共层各请求一遍数据,组件里的状态又复制进全局 store;同一种业务状态,在不同页面还各有一套判断。每一处单独看都能工作,等规则要改时,得先把这些地方找齐。
为什么又多了一套请求封装
项目里往往已经有了约定:请求在哪里封装,权限由谁判断,状态归哪里,错误怎么处理。
只给 AI 当前页面和一句需求,它可能就在页面里补一个请求,再加一套错误提示。局部确实能跑,项目里却多出一条需要维护的路径。以后认证方式或者错误码变了,公共封装已经改好,这个页面还得单独照顾。
带检索和工具调用的 Agent 能查仓库,也能读项目规则。不过,这次到底有没有找到相关实现,要看它实际读了什么。工具有这个功能,不能直接当作这一步已经做完。把整个仓库塞进上下文,也不能替它挑出该复用的代码。
可以先让它找出已有的类似功能,再说明准备复用哪一层、改哪些文件,理由是什么。答不清楚就继续读。改完再发现用错了数据入口,还得退回这一步。
一个页面几千行,怎么还在往里塞
一个商品列表,把筛选区、表格、编辑抽屉都写在同一个文件里,请求、权限判断和样式也全住在这儿。写到几千行,功能确实齐了,滚动条也快瘦成一粒芝麻了。改个表单校验,得先在里面找半天。

如果任务只写"把功能做出来",没交代分层和组件边界,AI 就可能继续往现有入口里添。再把"少改文件"理解成"最好只动一个文件",连该新增的组件都一起省了。拆出几个有明确职责的组件,照样可以是一次小范围改动。
没有自定义 rules,它也会受工具默认指令、现有代码和当前上下文影响,也可能主动拆分。只是团队希望页面管到哪、哪些逻辑该独立,不能每次都靠它猜。
以 Vue 页面为例,先找已有公共组件和相似实现。确实需要新增时,可以按职责考虑这样的组织方式;这是示意,不必照着新建一套:
text
product-list/
index.vue 页面组装与区域协作
components/
ProductFilters.vue 筛选交互
ProductTable.vue 列表展示与行操作事件
ProductEditDrawer.vue 编辑抽屉与表单校验
composables/
useProductList.ts 列表加载、分页与刷新状态
独立交互区域可以抽成业务组件,有状态逻辑按职责封装,请求继续复用项目已有的 API 层。Vue 官方文档也说明,composable 除了复用有状态逻辑,还能按逻辑关注点组织复杂组件的代码。
拆完还得看状态和依赖。筛选条件保留一个来源,组件通过明确的输入和事件协作;把整份页面状态复制到每个子组件,照样会出同步问题。几千行全搬进一个巨大的逻辑文件,也只是给原来的大文件换了个门牌。
行数可以当预警,是否拆得合理,还得看职责能不能说清、相关修改能不能落在该去的地方。把文件切小以后,如果调用关系更绕了,这次拆分就得重新想。
rules 要写具体,也要确认它真在起作用
"注意模块化""代码要优雅"给的余地太大。项目规则可以把长期有效的约束写得更具体,例如:
text
修改页面前,读取当前工具实际支持、且对当前目录生效的项目规则。
先查已有公共组件和相似实现,再列出拟新增或修改的文件及各自职责。
页面入口以组装为主,独立交互区域按职责评估组件拆分。
可独立维护的有状态逻辑按职责封装,请求复用已有 API 层。
状态保留单一来源,组件通过明确的输入和事件协作。
触及项目约定的文件或函数预警值,先说明拆分方案或保留理由。
不得仅为压行数随意切碎代码,或把所有逻辑搬进另一个大文件。
完成后运行适用检查,不得关闭规则或放宽门槛来消除告警。
规则放在哪里、对哪些目录生效,要按当前工具的文档确认。不能随手放一个叫 rules.md 的文件,就默认它一定会读。让它复述适用条目可以帮助核对,后续仍要检查 diff:文件到底建在哪一层,状态有没有复制,请求有没有绕开公共入口。
而且,写进规则不等于自动拦截。Claude Code 的文档说明,CLAUDE.md 等内容会作为上下文指令加载;需要强制阻止动作时,应使用相应的 hook 等执行机制。
能自动检查的要求,也可以接进实际门禁。比如用 ESLint 的 max-lines 规则检查文件长度,阈值和例外按项目约定;Vue 单文件组件还要配好适用文件和 parser,并在项目里实际跑通。需要阻止合并的检查,要设为失败条件、接入 CI,并确认脚本没有忽略失败退出码。
超过行数只是提醒你回来检查设计,lint 不会替你决定组件怎么分。规则文档、自动检查和代码审查各管一部分,光让 AI 在结尾保证一句"已遵循规范",还得再看看它交了什么。
补丁加多了,得看看这个函数到底管什么
假设一个页面先加价格筛选,又加品牌筛选,之后补库存状态。每轮都往现有函数里添条件,后来这个函数还管起了数据转换、权限判断和错误提示。一个筛选函数,逐渐把门卫和售后的活也接了。
如果每次给 AI 的要求都是"这里再兼容一下""测试别报错就行",它就可能一直围着当前症状打补丁。每次改动都很小,合起来却越来越难说清这个函数负责什么。
小 diff 值得争取,但文件少、行数少,不能证明设计合理。某个分支反复被改,就该回头看看这些判断是否真的属于这里,有没有重复的业务规则。如果要重构,也得说清楚这次能解决什么问题。
同样,看到重复就抽通用框架,也未必划算。价格筛选还没交付,先收到一份插件系统设计书,这热情多少有点超出预算。
Google 的代码审查指南专门提醒评审者留意过度设计,包括没有现实需求的通用化和额外功能。先把当前问题解决清楚。未来需求真来了,再按它的具体样子决定怎么改。
测试全绿,也可能一起误会了需求
如果测试只检查返回商品的价格范围,刚才的排序副作用就不会暴露。再添几组同样思路的数据,也补不上"原数组有没有被修改"这个缺口。
让 AI 写实现,再让它写测试,可以节省工作。但两边如果沿用了同一个误解,就可能在错误答案上愉快会师,谁也不觉得有事。

验收条件最好在看实现之前定好。筛选结果要正确,区间两端的价格也要包含。返回顺序、原数组是否保持不变,以及没有匹配项时是否返回空结果,也都得检查。
Google 的同一份审查指南还要求检查测试本身是否有效:代码出错时,它真的会失败吗?断言有没有用?测试也需要维护,不能只数写了多少个。
功能没过,就让 AI 一直"修到测试通过",也得看它到底修了什么。它可能改对实现,也可能改了断言、跳过用例,或者把需求悄悄改成当前代码能满足的样子。测试文件的 diff 不能漏看。
审查要看依赖关系,不能只看排版
类型名起得漂亮,目录整整齐齐,注释还把每一步都解释了,确实很容易让人放心。
轮到维护,就得问些不那么客气的问题。组装页面的函数为什么能改账户余额?两个模块各存一份相同状态,更新时由谁同步?异常被统一转成成功结果以后,调用方还能不能知道失败了?
格式化工具回答不了这些问题,补几段注释也修不好职责和数据流。
GitHub 关于 Copilot Agents 的说明明确列出了限制:代码审查可能漏掉问题,尤其是大型或复杂改动;生成的建议也可能不准确,需要人工审查和测试。再找一个 AI 看一遍,多了一次检查机会,合并前仍然得有人理解这次改动。
人写的代码也得这么查。AI 让代码更容易产出了,团队还得留出时间读它,不能把省下来的时间全排给下一个需求。
把维护要求写进任务
"写得优雅一点""注意可维护性"听着都对,验收时却不好使。商品筛选这个任务,可以交代得具体些:
text
先阅读商品列表、相关数据入口,以及已有的类似筛选实现。
先说明准备复用哪里、预计改哪些文件,暂时不要修改代码。
确认方案后再实现价格区间筛选:
- 筛选基于现有数据,不新增一套请求。
- 不修改原数组,保留原来的展示顺序。
- 本次筛选状态只属于当前组件,不复制到全局 store。
- 不改公共组件接口,不升级依赖,不顺手重构其他模块。
- 覆盖边界价格、空结果、顺序保留和原数组不变的测试。
完成后说明数据怎么流转、状态放在哪里,以及实际执行了哪些检查。
如果发现需要扩大改动范围,先说明原因。
这里把筛选状态留在当前组件,是这个小任务的要求。换成需要跨页面共享的条件,就得重新判断状态放在哪里,别把示例里的约束当成所有项目的标准答案。
真要改公共接口或者重构,可以单独讨论影响范围和验收方式。功能必须改哪些、哪些只是额外建议,分开看比较清楚。
合并前,还可以提一个小的后续需求,例如"再加品牌筛选"。先不实现,让它说说准备改哪里、补哪些测试。如果这也要牵动权限、请求封装和全局状态,就值得先检查这些依赖是否必要。
最后让它交代清楚数据从哪来、状态归谁、哪些行为不能动,能一项项指到实际代码。至于"已全面优化,具有良好扩展性",这句可以省了。真要查 bug,还得从文件和调用关系里找,交付感言帮不上忙。