AI 写的功能能跑,为什么还是会返工?它漏掉的往往是这 7 类边界
前两篇解决的是“先读代码”和“怎样判断 Codex 已经读懂项目”。
项目读清楚之后,下一步是不是就可以按照需求直接实现?
还差一件事:把需求边界找出来。
前端需求里最容易被忽略的内容,通常不是页面上有没有按钮,而是条件发生变化以后,页面到底应该怎么表现。
“增加一个用户编辑功能”,正常路径很清楚:
点击编辑 → 打开弹窗 → 修改字段 → 保存成功 → 刷新列表
Codex 完全可以很快把这条路径写出来。
可真正导致返工的问题,往往藏在主路径旁边:
-
连续点击保存会不会发两次请求?
-
保存失败后,用户刚填的内容是否保留?
-
刷新列表时,搜索条件和当前页要不要保留?
-
记录已经被别人删除时,弹窗怎么处理?
-
用户权限在页面打开后发生变化怎么办?
-
关闭弹窗再打开,校验信息是否残留?
-
姓名为空字符串、全是空格或超长时是否算有效?
这些内容不会因为我们写了“请完善异常处理”就自动得到正确答案。
AI 可以补一个技术上常见的处理方式,但哪个结果符合当前业务,仍然要由人确定。
什么叫需求边界
我现在把需求边界理解为:
当输入、状态、时间、权限、作用范围或运行环境发生变化时,系统行为需要重新作出决定的位置。
正常路径描述“事情顺利时怎么走”,边界描述“条件不再理想时怎么选”。
例如“点击保存后调用接口”是主流程;下面这些都是边界:
-
表单未通过校验时是否调用。
-
已经处于提交中时是否允许再次调用。
-
接口返回业务失败时弹窗是否关闭。
-
请求还没结束,用户先关闭弹窗时怎样处理。
-
保存成功,但列表刷新失败时给用户什么结果。
边界之所以容易漏,不是因为开发者不知道它们存在,而是需求通常以功能名组织,代码却以状态变化运行。
产品写“新增编辑功能”,设计稿展示弹窗静态页面,接口文档描述请求和响应。真正把它们连起来的状态转换,往往落到前端开发阶段才暴露。
Codex 如果只按功能名生成实现,也会优先补齐最显眼的正常路径。
第一类:输入和值的边界
前端最常见的误判之一,是把所有“没有值”当成同一种情况。
实际接口和表单里,下面这些值含义可能完全不同:
-
字段不存在。
-
值为 null。
-
值为 undefined。
-
空字符串。
-
只有空格的字符串。
-
数字 0。
-
布尔值 false。
-
空数组。
如果 AI 使用一个简单的真假判断:
const displayValue = value || '--'
数字 0 和 false 也会被当成空值。
代码没有语法错误,页面甚至看起来更整齐,但业务含义已经改变。
输入边界需要提前确认:
-
哪些值算未填写。
-
是否需要去掉首尾空格。
-
最小值、最大值和长度限制。
-
日期范围能否只选一端。
-
多选为空时发送空数组、null,还是不发送字段。
-
枚举遇到未知值时怎样展示。
-
后端缺失字段时是容错还是报错。
我不会把“处理空值”当成完整要求。它至少要落到一个明确映射:
| 输入 | 预期行为 |
|---|---|
| null 或 undefined | 显示占位符 |
| 空字符串 | 根据字段规则决定占位或保留 |
| 0 | 正常显示 0 |
| false | 显示对应业务文案 |
| 未知枚举 | 显示原值、未知状态或告警,需业务决定 |
AI 擅长写判断,人的责任是先定义什么值得判断。
第二类:操作顺序和状态转换边界
很多前端问题只有连续操作才会出现。
单独测试查询、翻页和重置,它们都可能正常;连起来操作,状态就可能错位:
-
设置筛选条件并查询。
-
翻到第三页。
-
修改筛选条件。
-
再次查询。
-
点击重置。
这里至少有三个需要决定的边界:
-
修改条件后查询是否回到第一页。
-
重置是否立即请求。
-
重置后的页码和每页数量恢复到什么状态。
同样,新增和编辑共用弹窗时,也不能只检查“分别能打开”:
-
打开编辑并触发表单校验。
-
不保存,直接关闭。
-
再打开新增。
-
检查字段、校验信息和临时状态。
前一个操作会不会污染后一个操作,这就是状态转换边界。
我会要求 Codex 按“动作前状态 → 用户动作 → 动作后状态”来描述,而不是只列方法名。
例如:
动作前:pageNum = 3,存在筛选条件
动作:点击重置
动作后:筛选恢复默认值,pageNum = 1,重新请求列表
保持不变:pageSize
只有把“变什么”和“不变什么”同时写出,状态边界才算清楚。
第三类:异步和时间边界
前端页面不是按代码行一次执行完的,用户操作、网络响应和组件生命周期会交错发生。
常见的时间边界包括:
-
用户连续点击按钮。
-
前一次请求比后一次更晚返回。
-
请求期间切换路由。
-
弹窗关闭后详情请求才返回。
-
上传或导出耗时较长。
-
页面重复进入触发多次初始化。
假设用户先查询“已启用”,马上又查询“已禁用”。第二个请求先返回,页面正确显示已禁用数据;随后第一个请求返回,又把列表覆盖成已启用。
每一次请求都成功了,但最终页面是错的。
这类问题不能只写“增加 Loading”。Loading 解决的是反馈和部分重复操作,不一定解决响应乱序。
异步边界要确认:
-
哪些操作在请求期间必须禁用。
-
哪些请求允许并行。
-
是否需要取消旧请求或忽略过期结果。
-
组件卸载后是否继续更新状态。
-
成功、失败、取消三种结果怎样恢复 UI。
-
Loading 属于整个页面、局部区域还是单个按钮。
AI 可以根据现有项目方式选择实现,但必须先知道要保护哪一种业务结果。
第四类:成功一半和失败恢复边界
前端任务经常不是只有“成功”和“失败”两个状态。
例如编辑保存包含两个动作:
-
保存表单。
-
刷新列表。
可能出现:
-
保存成功,刷新也成功。
-
保存失败,列表不刷新。
-
保存成功,但列表刷新失败。
-
服务端已成功,前端因超时认为失败。
第三和第四种情况最容易让用户困惑。
如果保存成功后直接关闭弹窗,但刷新失败,用户可能看不到新数据;如果提示“保存失败”,用户再次点击,又可能重复提交。
所以我会把组合流程拆开确认:
| 阶段 | 失败时需要决定 |
|---|---|
| 表单校验 | 是否保留输入,焦点放在哪里 |
| 保存请求 | 弹窗是否保留,按钮何时恢复 |
| 保存成功后的刷新 | 是否提示已保存但刷新失败,能否手动重试 |
| 后续跳转或关闭 | 未完成动作是否会丢失 |
“接口失败时提示错误”远远不够。真正的边界是失败发生在哪一步,以及已经成功的部分能不能撤销。
第五类:权限和数据变化边界
前端看到的数据不是静止的。
页面打开后,记录可能被其他人删除、状态可能被修改,当前用户的权限也可能变化。
常见边界包括:
-
列表里能看到记录,打开编辑时记录已不存在。
-
打开弹窗时有权限,提交时权限已被收回。
-
当前页最后一条记录被删除。
-
服务端返回的状态不再允许当前操作。
-
按钮被隐藏,但用户仍可通过旧链接进入页面。
前端权限不能替代后端鉴权,但前端仍要定义展示和失败后的行为。
例如删除当前页最后一条数据后:
-
保留当前页,显示空列表。
-
回到上一页。
-
回到第一页。
三种实现都可能合理,取决于产品体验和项目既有规则。
AI 不能从“增加删除功能”这句话中知道答案。
我会提前确认:
-
权限影响的是可见、可点、可提交还是可访问。
-
服务端拒绝后页面状态怎样恢复。
-
数据已变化时提示什么,是否自动刷新。
-
删除、禁用、审核等操作后页码如何处理。
-
前端展示权限与后端真实权限的责任边界。
第六类:生命周期和返回路径边界
页面打开时状态正确,不代表再次进入仍然正确。
前端状态会跨越:
-
弹窗打开与关闭。
-
组件挂载与卸载。
-
路由进入与离开。
-
KeepAlive 激活与停用。
-
标签页切换。
-
从详情页返回列表。
需要决定:
-
搜索条件是否保留。
-
页码是否保留。
-
临时表单是否清空。
-
校验信息是否清理。
-
未完成请求是否取消。
-
返回列表是否自动刷新。
这些边界尤其依赖项目现有导航和缓存机制。
如果 Codex 只读取目标组件,不查路由、缓存或父级容器,就很容易把初始化逻辑放在错误的生命周期中。
所以涉及“返回”“再次打开”“切换页面”时,我会把它们当成独立路径写进需求,而不是默认框架会处理好。
第七类:作用范围和兼容边界
最后一类边界不一定发生在页面交互里,而是发生在代码影响范围中。
例如:
-
修改公共组件默认值。
-
改变工具函数返回类型。
-
调整接口类型定义。
-
修改全局样式。
-
更新依赖版本。
-
重构共享状态。
当前页面可能因此变得更好,其他调用方却一起改变。
作用范围边界要回答:
-
本次只修当前页面,还是统一所有同类页面。
-
公共 API 能否改变。
-
旧调用方式是否需要兼容。
-
默认行为改变后谁会受到影响。
-
本次是否允许顺手清理历史代码。
我最警惕的需求词是“统一处理一下”。
它听起来像小优化,实际上可能把局部需求升级成全局行为变更。没有调用方清单、兼容策略和回归范围时,不应该让 Codex 自行扩展。
我如何在需求里主动找边界
等待 AI 写完再找边界,成本已经偏高。
我会在任务开始前做四轮检查。
第一轮:圈出需求里的动作
找出查询、重置、保存、删除、关闭、返回、切换、导出等动词。
每个动作都问:
-
触发前是什么状态。
-
成功后改变什么。
-
失败后保留什么。
-
连续执行会怎样。
第二轮:圈出需求里的状态
找出筛选条件、页码、Loading、表单、权限、选中项、缓存等状态。
每个状态都问:
-
唯一来源在哪里。
-
谁能修改。
-
何时初始化。
-
何时清理。
-
跨页面是否保留。
第三轮:圈出外部依赖
找出接口、公共组件、全局状态、路由、权限和工具函数。
每个依赖都问:
-
失败时当前功能怎样退化。
-
行为变化会影响哪些调用方。
-
是否存在项目统一封装。
-
哪些结论需要从代码中确认。
第四轮:按用户路径串起来
不要只检查单个按钮,要组合操作:
-
查询 → 翻页 → 重置。
-
编辑 → 校验失败 → 关闭 → 新增。
-
提交 → 失败 → 修改 → 再提交。
-
删除当前页最后一条 → 列表刷新。
-
发起请求 → 切换页面 → 请求返回。
只要路径中出现状态切换,就值得问一次边界。
一份可以交给 Codex 的边界发现清单
## 当前任务边界检查
在修改代码前,先根据项目现状和需求检查以下内容。
### 输入和值
- null、undefined、空字符串、0、false 分别怎样处理?
- 长度、范围、空格、未知枚举怎样处理?
### 操作顺序
- 查询、翻页、重置连续执行时,哪些状态改变?
- 新增、编辑、关闭再打开时,哪些状态清理?
### 异步和时间
- 是否可能重复提交、请求乱序或卸载后回写?
- Loading、禁用、取消和恢复分别由谁负责?
### 失败恢复
- 失败发生在哪一步?
- 用户输入、旧数据、弹窗和按钮状态分别保留什么?
### 权限和数据变化
- 权限变化、记录不存在、状态已改变时怎样处理?
- 删除后页码和列表怎样恢复?
### 生命周期
- 再次进入、返回、缓存激活时哪些状态保留或刷新?
### 作用范围
- 公共组件、类型、工具或样式有哪些调用方?
- 哪些行为必须向后兼容?
### 输出要求
- 将结论分为:已确认、需要从代码验证、需要业务决定。
- 没有依据的边界不要自行补成确定规则。
- 关键边界未确认时,先报告,不要开始扩大修改。
边界不是越多越好
把所有理论上的异常都列进任务,同样会让需求失去重点。
我会根据三个维度排序:
-
发生概率:真实用户是否容易触发。
-
影响程度:触发后会不会丢数据、误操作或破坏其他页面。
-
修复成本:现在确认和交付后返工,成本差多少。
高概率、高影响或高返工成本的边界,必须在实现前确定。
低概率、低影响且当前没有事实依据的情况,可以记录成未覆盖项,不需要为了显得完整而虚构处理方案。
工程判断不在于列出最多的边界,而在于先兜住最危险的边界。
人要负责决定边界,AI 可以负责把边界变成检查
Codex 很适合帮我做三件事:
-
从代码中找出状态、调用方和现有异常处理。
-
根据已确认边界补充实现和测试。
-
把用户路径转成检查步骤并执行可运行的验证。
但它不能替我决定业务上唯一正确的结果。
“失败后是否保留旧数据”“删除后回到上一页还是第一页”“权限不足时隐藏还是禁用”,这些都可能有多个合理答案。
人的工作不是把每行代码写出来,而是在关键分岔点给出可承担的决定。
下一篇我会继续把边界落成验收标准。重点不是再写一句“功能正常”,而是用行为、数据、范围、自动检查和页面证据,提前定义 Codex 到底要拿什么证明任务完成。
本系列持续更新。Day 5 的第二篇会把“哪些地方容易漏”转成一份可直接使用的前端验收模板。
浙公网安备 33010602011771号