一句话需求里有五个空洞:谁用、什么数据、边界、失败怎么办、怎么算完成。 可验证的唯一判据:能不能写成一个输入加一个期望输出。 带「健壮」「好」「顺畅」的小项都不算小项——形容词不能被判定。
上一章修 bug,这一章做功能。修 bug 的输入是错误,做功能的输入是一句话。 前者至少有现场,后者什么都没有。 为什么「拆解」这一步放在跨前后端这一章的最前面? 因为跨端的活一旦拆错,返工的不是一段代码,是一条链路——接口定义错了,前端后端都要重做。 Demo 拿一句十四个字的需求,先标出五个空洞,再拆成六条能验证的小项, 最后用三句「看起来像小项」的话说明判据。
一、一句话需求里的五个空洞
「给商品加个标签功能,能筛选。」 这句话不难懂,但它有五个关键决定是空着的。
| 空洞 | 具体在问什么 | 不定会怎样 |
|---|---|---|
| 谁用 | 运营打标签,还是用户自己打 | 权限和数据归属不清楚 |
| 什么数据 | 自由文本,还是固定词表 | 决定要不要建表 |
| 边界 | 一个商品最多几个标签 | 决定校验和索引 |
| 失败怎么办 | 重复、超长、空 | 决定错误码 |
| 怎么算完成 | 什么现象出现算做完 | 决定验收 |
五个空洞里,前四个是技术决定,第五个是验收决定。 技术决定错了可以改,验收决定空着,你连「做完了」都说不出口。 最常见的结局是「先这样吧」。 「先这样」听起来是妥协,其实是一个未定义的状态—— 它会在两个月后变成另一张需求单,而那张单子上的第一条正是当初跳过的空洞。
二、拆成可验证的小项
拆的目标不是「拆得细」,是「每条都能被判对错」。 判据只有一条:能不能写成一个输入加一个期望输出。 Demo 里六条小项,每条都带两列:「怎么验」和「该看到什么」。 比如「单个商品最多挂五个」——验法是挂第六个,该看到的是返回 400。 这两句话写出来,这条小项就没有解释空间了。
| 写着像小项的 | 为什么不算 |
|---|---|
| 标签功能要健壮 | 「健壮」不是一个能判定的状态 |
| 性能要好 | 没给数字,快慢都算好 |
| 体验要顺畅 | 没有可观察的现象 |
三句话里各有一个形容词:健壮、好、顺畅。 形容词不能被判定,这是它们唯一的问题。 把它们换成数,「性能要好」就变成「筛一千个商品两百毫秒内返回」,这时候它才能被验。 换的时候要小心一个坑:数不一定是时间。 「两百毫秒内返回」是数,「不超过五条错误日志」也是数,「重复提交两次结果一样」还是数。 可判定的形式有很多种,别只想到性能。
三、先写验收再写实现
顺序反了会怎样? 先写完实现,再去想「怎么验」,你会不自觉地按已经写出来的东西定标准——那不叫验收,叫追认。 先写验收的好处有两个,第二个更重要。 第一个:写验收的时候会发现需求里的空洞—— 你想写「挂第六个返回 400」,才发现没人说过上限是五。 第二个:验收标准可以直接变成测试。 Demo 里那六条「怎么验」,每一条都是一条测试的雏形——输入、期望输出、判定方式,三样齐了。 这意味着拆解和写测试是同一件事的两个说法,不是两件事。 所以跨端功能的正确顺序是: 先把验收写成六条,再定接口,再写前后端。 接口定义在验收之后,因为接口要能满足验收,而不是反过来。
四、动手 Demo:从一句话需求到六条可验证的小项
一句十四个字的需求,先标出五个空洞,再拆成六条带验证方式的小项。 最后用三句「看起来像小项」的话,说明可验证的判据。 这一篇没有代码跑——拆解是判断,不是计算。 但每条小项的「怎么验」都能直接变成一条测试,这一点在下一章会真跑起来。
ITEMS = [
("建 tags 表(id、name、唯一索引)", "插入同名两条", "第二条被拒"),
("商品与标签多对多关联表", "给商品挂两个标签", "查出来是两个"),
("单个商品最多挂 5 个", "挂第 6 个", "返回 400"),
("标签名 1~12 字,去首尾空格", "提交「 abc 」", "存成 abc"),
("按标签筛选商品", "筛「新品」", "只返回挂了这个标签的"),
("标签为空时不报错", "筛一个不存在的标签", "返回空列表,200"),
]
for i, (what, how, want) in enumerate(ITEMS, 1):
print(i, what, "|", how, "|", want)
实跑输出:
--------------------------------------------------------------------
从一句话需求到六条可验证的小项
--------------------------------------------------------------------
一句话需求里有五个空洞。不补,Agent 会替你补——而它补的往往不是你想要的。
① 原始需求,和它的五个空洞
--------------------------------------------------------------------
[需求原话]
| 给商品加个标签功能,能筛选。
十四个字,看着清楚,其实五个关键决定都空着。
| 空洞 | 具体在问什么 | 不定会怎样 |
|---|---|---|
| 谁用 | 运营打标签,还是用户自己打? | 权限和数据归属 |
| 什么数据 | 标签是自由文本,还是固定词表? | 决定了要不要建表 |
| 边界 | 一个商品最多几个标签? | 决定了校验和索引 |
| 失败怎么办 | 标签重复、超长、空,分别怎么处理? | 决定了错误码 |
| 怎么算完成 | 什么现象出现算这个需求做完了? | 决定了验收 |
五个空洞里最容易被跳过的是第五个:怎么算完成。
! 没有验收标准的需求,做完了也说不清做没做对——最后变成「先这样吧」,
而「先这样」会在两个月后再改一次。
② 补完之后,拆成六条可验证的小项
--------------------------------------------------------------------
| # | 小项 | 怎么验 | 该看到什么 |
|---|---|---|---|
| 1 | 建 tags 表(id、name、唯一索引) | 插入同名两条 | 第二条被拒 |
| 2 | 商品与标签多对多关联表 | 给商品挂两个标签 | 查出来是两个 |
| 3 | 单个商品最多挂 5 个 | 挂第 6 个 | 返回 400 |
| 4 | 标签名 1~12 字,去首尾空格 | 提交「 abc 」 | 存成 abc |
| 5 | 按标签筛选商品 | 筛「新品」 | 只返回挂了这个标签的 |
| 6 | 标签为空时不报错 | 筛一个不存在的标签 | 返回空列表,200 |
六条,每条都有「怎么验」和「该看到什么」。
这是「可验证」的唯一判据:能不能写成一个输入加一个期望输出。
③ 可验证性的尺子
--------------------------------------------------------------------
| 写着像小项的 | 为什么不算 |
|---|---|
| 标签功能要健壮 | 「健壮」不是一个能判定的状态 |
| 性能要好 | 没给数字,快慢都算好 |
| 体验要顺畅 | 没有可观察的现象 |
三条的共同点:里面都有一个不能判定的形容词——健壮、好、顺畅。
! 把形容词换成一个数,「性能要好」就变成「筛一千个商品两百毫秒内返回」,这时候它才能被验。
④ 六条小项的真实成本
--------------------------------------------------------------------
| 类型 | 条数 | 主要成本在哪 |
|---|---|---|
| 建表与索引 | 2 | 迁移脚本要能回滚 |
| 校验规则 | 2 | 边界值要逐个写测试 |
| 查询与筛选 | 2 | 索引和分页 |
六条里两条是「改结构」,两条是「加约束」,两条是「加查询」。
三类的工作方式完全不同,混在一起估工期一定估错。
六条小项里,第三、四、六条最值得看。 它们全是「边界」和「异常」——而这恰好是需求原话里完全没提的部分。 拆解的价值就在这里:它把「没提的部分」逼出来了。 你没拆之前不知道有这些,拆的时候每写一条「怎么验」,就暴露一个原本空着的地方。 第四行那条「提交『 abc 』存成 abc」,是最典型的例子。 不写这一条,前后端会各按各的理解处理空格—— 前端 trim 了,后端没 trim,然后查重的时候两条记录算两条。 这种 bug 不会报错,只会让数据慢慢脏掉。 最后一段那三句「要健壮」「要好」「要顺畅」是全篇的反面例子。 它们的共同点不是「写得不够细」,是「不能被判定」——你没法写出一条会失败的测试来验它。
30 秒实验
- 找一句你最近接到的需求原话,标出那五个空洞。 标不出来的那一个,就是你后来返工的原因。
- 把「性能要好」改成带数字的版本,看能不能写出来。 写不出来,说明你还不知道这个功能给谁用、用在什么场景。
- 给你的需求写六条验收,每条都要有「怎么验」和「该看到什么」。 写不满六条,说明这个需求还不够大,或者还没想清。
常见误区
- 「需求越模糊,实现越自由。」 自由的是你,遭殃的是联调。 跨端功能里,模糊的部分一定会变成两边理解不一致的地方。
- 「拆得越细越好。」 细不是目标,可判定才是。 拆到「改个变量名」这种粒度,除了增加清单长度没有别的用。
- 「先写完再想怎么验。」 那叫追认,不叫验收。 写验收的时候才会发现需求里的空洞——这一步不能倒过来。
自测三问(点开对答案)
1. 五个空洞里哪个最容易被跳过? —— **第五个:怎么算完成。** **前四个是技术决定,错了能改;这个空着,你连「做完了」都说不出口。** 2. 可验证的判据是什么? —— **能不能写成一个输入加一个期望输出。** **带形容词的都过不了这一关。** 3. 为什么验收要写在实现之前? —— **因为写完实现再定标准,你会按已经写出来的东西定,那是追认。** **而且写验收的过程会逼出需求里的空洞。**下一篇把验收翻译成接口:契约先行,以及怎么判断一次变更是破坏性的。
- ☐ (本篇完,下一篇 11.2 API 契约的演进:从手动同步到自动生成)