第四章 需求像雾像雨又像风
一
入职第四周,王志远终于不再只是修 bug 了。
赵北辰在 Sprint Planning 会上给他分了一个新需求——"用户导出功能优化"。这是他第一次从头开始做一个完整的功能,而不是在别人的代码上修修补补。
需求是产品经理李想提的。李想是个说话很快的年轻人,戴着 AirPods,走路带风,永远在开会或者去开会的路上。他在需求评审会上用五分钟讲完了这个需求:
"现在用户导出功能太慢了,客户投诉了好几次。你们优化一下,让它快起来。另外,客户说导出的格式不太好用,你们也改改。就这样,不复杂,两天应该够了。"
说完他就走了,去赶下一个会。
王志远在笔记本上记了两行字:
- 导出功能优化,让它快起来
- 导出格式改改
他看着这两行字,觉得挺清楚的。快起来,改格式。没什么难的。
他打开 IDE,开始干活。
二
"快起来"——怎么算快?
王志远想了想,现在导出一万条数据大概要三十秒。如果能优化到十秒以内,应该算快了吧?
他花了一天时间做了性能优化:把数据库查询从逐条查改成了批量查,用了 EasyExcel 的流式写入(上次那个 bug 的经验派上了用场),还加了一个异步导出的机制——用户点击导出后不用等着,后台生成好了发一条站内通知,用户再去下载。
"导出格式改改"——改成什么样?
李想没说。王志远翻了翻客户的投诉记录,发现有人说"列太多了看不过来",有人说"日期格式不统一",还有人说"能不能加个汇总行"。
他综合了一下这些反馈,做了几个改动:把不常用的列默认隐藏,统一了日期格式为 yyyy-MM-dd HH:mm:ss,在最后一行加了金额的汇总。
两天后,他提交了代码,在 Jira 上把任务状态改成了"已完成"。
他觉得自己这次做得不错——又快又好,还提前完成了。
三
第二天下午,李想风风火火地冲进了研发区。
"谁做的导出优化?"他的声音比平时高了半个八度。
王志远举了一下手。
李想走到他工位旁边,把笔记本电脑往桌上一放,屏幕上是一封客户的邮件:"你看看客户怎么说的。"
王志远凑过去看:
你们的导出功能改了之后,我们的财务系统对接不上了。之前导出的 Excel 第三列是"交易时间",格式是"2024/7/15",我们的财务系统是按这个格式解析的。你们改成了"2024-07-15 14:30:00",我们的解析脚本全挂了。另外,你们把一些列隐藏了,但我们的报表模板是按列号取数的,列号变了,报表全乱了。请立即回退。
王志远的脸一下子白了。
李想的语气不太好:"我说的'格式改改',是让你加一个 PDF 导出选项,客户要打印用的。谁让你改 Excel 的列格式了?"
"可是你没说要加 PDF 啊……"王志远小声说。
"我说了'格式不太好用',客户要的是多一种格式选择,不是把现有格式改了!"
"但是'格式不太好用'这句话,我理解的是现有格式有问题……"
"你理解错了!"
两个人大眼瞪小眼。
这时候周旋经理从办公室里走出来了。他大概听到了动静,端着保温杯,不紧不慢地走过来。
"怎么了?"
李想把情况说了一遍。周旋听完,看了王志远一眼,又看了李想一眼,然后说了一句让王志远记了很久的话:
"志远,做之前为什么不跟产品确认清楚?"
王志远张了张嘴,想说"产品说得太模糊了",但话到嘴边又咽了回去。因为他知道,不管产品说得多模糊,自己没去确认就是自己的问题。
周旋又转向李想:"李想,需求描述要写清楚。'让它快起来'不是需求,'格式改改'也不是需求。你是产品经理,需求文档是你的基本功。"
李想也不说话了。
周旋喝了一口保温杯里的水,总结道:"这个事情,你们俩都有责任。志远先把 Excel 格式回退,然后你们坐下来,把需求重新对一遍。这次写清楚,我要看到验收标准。"
说完他转身回了办公室。
四
王志远花了一个小时把 Excel 的格式回退了。回退的过程中他心里很不是滋味——自己辛辛苦苦做的优化,异步导出、流式写入、汇总行,全白费了吗?
不,性能优化的部分没问题,那是实打实的改进。出问题的是格式改动——他在没有确认的情况下,擅自改了客户依赖的数据格式。
他想起赵北辰说过的一句话:"改代码之前,先搞清楚影响范围。"
上次是改公共工具类没查调用方,这次是改导出格式没查下游依赖。本质上是同一个错误——没搞清楚谁在用,就动手改了。
只不过上次影响的是内部模块,这次影响的是外部客户。后果严重得多。
他坐在工位上发了一会儿呆,然后站起来,去找赵北辰。
五
赵北辰正在看代码,听完王志远的讲述后,没有马上说话。他把茶杯里的茶喝完,又倒了一杯,然后说:
"你觉得这次的问题出在哪?"
"我没有跟产品确认需求就动手了。"
"对。但更深层的问题是——你拿到的根本不是一个'需求',只是一个'想法'。"
赵北辰在白板上写了三个词:
想法(Idea)→ 需求(Requirement)→ 任务(Task)
"李想说的'让它快起来'、'格式改改',这是想法,不是需求。想法是模糊的、主观的、可能自相矛盾的。你不能拿着一个想法就开始写代码——你得先把想法变成需求。"
"怎么变?"
"问啊。"赵北辰说。"产品说'让它快起来',你就问:快到什么程度?现在多慢?客户能忍多久?产品说'格式改改',你就问:改哪儿?现在的格式哪里不好使?客户到底想要啥?下游有没有系统在用现在的格式?"
他在白板上画了一个表格:
模糊的想法 → 你应该问的问题
─────────────────────────────────────────
"让它快起来" → 快到什么程度?现在多慢?
客户能接受的等待时间?
数据量级是多少?
"格式改改" → 改什么格式?加新格式还是改旧格式?
现有格式有哪些下游依赖?
客户的具体诉求是什么?
"不复杂,两天应该够了" → 需求范围确认了吗?
验收标准是什么?
有没有隐藏的技术风险?
"你看,李想说的每一句话,背后都藏着至少三个问题。你不问,就只能靠猜。猜对了是运气,猜错了就是事故。"
王志远看着白板,觉得赵北辰说的每一个字都在戳他的痛处。他确实一个问题都没问——拿到需求就开始干,觉得自己理解了,其实只是自以为理解了。
六
"那怎么才能把想法变成需求呢?"王志远问。
赵北辰擦掉白板上的内容,重新写了一行字:
User Story(用户故事)
"敏捷开发里有一个工具叫 User Story,专门用来描述需求。格式很简单:"
他在白板上写下:
作为一个 [角色],
我想要 [功能],
以便 [价值/目的]。
"比如这次的导出优化,如果用 User Story 来写,应该是这样的:"
User Story 1:
作为一个运营人员,
我想要导出用户数据时等待时间不超过 10 秒(当前约 30 秒),
以便我能更高效地完成日常数据分析工作。
User Story 2:
作为一个运营人员,
我想要在导出 Excel 的同时可以选择导出 PDF 格式,
以便我能直接打印报表给领导审阅。
"你看出区别了吗?"赵北辰问。
王志远看了一会儿:"User Story 里说清楚了谁要用、要什么、为什么要。"
"对。三个要素:角色(谁)、功能(要什么)、价值(为什么)。缺了任何一个,这个需求都是不完整的。"
"但光有 User Story 还不够。"赵北辰又在白板上写了一个词:
Acceptance Criteria(验收标准)
"User Story 说的是'要什么',验收标准说的是'怎么算做完了'。没有验收标准的需求,就像没有终点线的赛跑——你永远不知道自己到没到。"
他写了一个例子:
User Story 1 的验收标准:
AC1:导出 1 万条数据,耗时不超过 10 秒
AC2:导出 5 万条数据,耗时不超过 30 秒
AC3:导出过程中,用户可以继续使用系统的其他功能(异步导出)
AC4:导出完成后,用户收到站内通知,点击可下载
AC5:现有 Excel 格式(列顺序、列名、日期格式)保持不变,不影响下游系统
User Story 2 的验收标准:
AC1:导出页面新增"PDF"格式选项
AC2:PDF 格式包含与 Excel 相同的数据内容
AC3:PDF 格式适合 A4 纸打印,有页眉页脚和页码
AC4:PDF 文件大小不超过 Excel 文件的 3 倍
王志远盯着白板看了很久。
"如果一开始就有这些验收标准,"他慢慢地说,"我就不会去改 Excel 的日期格式了。因为 AC5 明确写了'现有格式保持不变'。"
"对。"赵北辰点头。"验收标准就是你的护身符。需求做完了,按照验收标准一条一条核对,全部通过才算完成。如果产品验收的时候说'这不是我要的',你可以拿出验收标准说'这是我们当初确认的'。"
"但如果产品经理不写验收标准呢?"
赵北辰笑了:"那你自己写,写完发给他确认。需求确认是双向的——产品有责任写清楚,你也有责任问清楚。别等着别人把一切都嚼碎了喂你嘴里,你得自己主动去推。"
七
第二天,王志远约了李想重新对需求。
这次他提前准备了一份文档,按照 User Story 的格式把自己理解的需求写了出来,每条都附了验收标准。他还列了一个"待确认问题清单":
- "快起来"的具体指标是什么?客户能接受的最大等待时间?
- 新增 PDF 格式是否需要支持自定义模板?
- 现有 Excel 格式是否有下游系统依赖?如果有,哪些字段不能改?
- 导出数据量的上限是多少?十万条?一百万条?
- 异步导出的通知方式——站内通知、邮件、还是两者都要?
李想看到这份文档的时候,明显愣了一下。
"你这个……写得挺详细的。"他翻了翻,语气比昨天缓和了很多。
"赵哥教我的。"王志远老实说。
两个人花了四十分钟,把每一条 User Story 和验收标准都过了一遍。李想在这个过程中也补充了很多之前没说的细节——比如 PDF 导出不需要自定义模板,用固定模板就行;比如数据量上限是十万条,超过十万的场景暂时不考虑;比如通知方式先做站内通知,邮件通知放到下一个 Sprint。
对完之后,王志远把文档更新了一版,发邮件给李想确认。邮件标题写的是:
【需求确认】用户导出功能优化 - User Story & 验收标准 v2.0
李想回复了一个字:"确。"
王志远把这封邮件截图保存了。赵北辰说过:"邮件确认是最基本的自我保护。口头说的话,风一吹就散了。"
八
有了清晰的需求和验收标准,这次开发顺利多了。
性能优化的部分,他之前做的异步导出和流式写入都保留了——这些是对的,只是之前没有明确的性能指标,现在有了:一万条十秒内,五万条三十秒内。他跑了压测,都达标了。
PDF 导出是新功能。他调研了几个 Java 的 PDF 生成库,最终选了 iText(开源版本,AGPL 协议)。选型的时候他特意查了一下许可证——赵北辰之前提醒过他,开源库的许可证很重要,AGPL 要求使用它的项目也必须开源,如果公司的产品是商业闭源的,就不能用 AGPL 的库。
他去问了赵北辰。赵北辰说:"我们的产品是商业的,AGPL 确实不行。你看看 Apache PDFBox 或者 OpenPDF,这两个是 Apache License 和 LGPL,商业项目可以用。"
王志远最终选了 OpenPDF(LGPL 协议),写了一个简单的 PDF 导出模块。他在技术选型文档里记录了为什么选 OpenPDF 而不是 iText——不是因为技术不行,而是因为许可证不兼容。
这是他第一次在选型时考虑"技术之外"的因素。
九
功能做完后,他按照验收标准一条一条地自测:
- ✅ AC1:导出 1 万条数据,耗时 7.2 秒(< 10 秒)
- ✅ AC2:导出 5 万条数据,耗时 24.8 秒(< 30 秒)
- ✅ AC3:异步导出,用户可以继续操作
- ✅ AC4:导出完成后站内通知,点击可下载
- ✅ AC5:现有 Excel 格式完全不变
- ✅ PDF AC1-AC4:全部通过
他提交了 PR,苏薇 Review 的时候只提了一条:PDF 生成的字体要用系统自带的中文字体,不要打包到 jar 里,否则部署包会变大。
改完之后,PR 合入。
李想验收的时候,对着验收标准一条一条地核对,全部通过。他拍了拍王志远的肩膀:"这次不错。以后需求都这么对,省得扯皮。"
王志远笑了笑,心想:上次扯皮的时候你可不是这个态度。
但他没说出来。他开始学会一件事——有些话不用说,做到就行了。
十
那天晚上,王志远在出租屋里翻开笔记本,把这次的经历写了下来。
写着写着,他想到了一个问题:为什么产品经理的需求总是模糊的?
他想了想,觉得原因可能有几个:
第一,产品经理面对的是客户,客户说的话比产品经理还模糊。客户说"不好用",产品经理翻译成"格式改改",信息在传递过程中不断失真。
第二,产品经理不懂技术,他不知道"改格式"这三个字对开发来说意味着什么。他不知道有下游系统依赖,不知道改一个日期格式可能引发连锁反应。
第三,产品经理很忙,他同时跟进十几个需求,没有时间把每一个都写得很详细。
所以,需求模糊不完全是产品经理的错,但澄清需求是开发的责任。
你不能指望别人把一切都想清楚了再告诉你——你得主动去问、去确认、去挖掘。这不是多管闲事,这是专业素养。
他在笔记本上写下:
Day 28
今天被产品和客户夹击了一顿。说实话,挺委屈的——我加班加点做的优化,结果因为一个日期格式把客户的系统搞挂了。
但冷静下来想想,确实是我的问题。拿到需求就开干,一个确认的问题都没问。赵哥说得对——模糊的需求不是需求,是想法。我不能拿着一个想法就写代码。
以后第一件事不是打开 IDE,而是打开脑子。
还有一个教训:口头说的话,风一吹就散了。邮件确认,白纸黑字。这次吃的亏,下次不能再吃。
他合上笔记本,又打开了电脑。
他想起赵北辰说的 User Story 格式,觉得这个东西不只是用来写需求的——它其实是一种思维方式。
做任何事情之前,先想清楚三个问题:谁要用?要什么?为什么?
写代码是这样,做产品是这样,甚至做人也是这样。
他打开 Jira,看了看下一个 Sprint 的待办列表。有一个新需求,描述只有一行字:
"用户管理页面加个搜索功能。"
他笑了笑,打开一个新文档,开始写:
待确认问题清单: 1. 搜索哪些字段?用户名?手机号?邮箱?还是全部? 2. 搜索方式是精确匹配还是模糊匹配? 3. 搜索结果需要分页吗?每页多少条? 4. 搜索需要支持组合条件吗?比如"用户名 + 注册时间"? 5. 搜索性能要求是什么?数据量级是多少? 6. 搜索结果的排序规则是什么?
六个问题,两分钟写完。
他保存了文档,关上电脑,躺在床上。
窗外的路灯透过窗帘,在天花板上投下一片昏黄的光。隔壁外卖骑手今天没设闹钟,安静得有点不习惯。
他闭上眼睛,脑子里浮现出赵北辰在白板上写的那三个词:
想法 → 需求 → 任务。
从想法到需求,中间隔着一堆问题。从需求到任务,中间隔着验收标准。
这条路看起来很长,但走对了方向,每一步都不白费。
:::{admonition} 小胖的笔记 :class: tip
本章知识卡片:需求分析与 User Story
1. 想法 ≠ 需求
想法(Idea)──→ 需求(Requirement)──→ 任务(Task)
↑ ↑ ↑
模糊的 清晰的 可执行的
主观的 可验证的 有估算的
"让它快起来" "1万条数据10秒内" "用EasyExcel流式写入+异步导出"
拿到一个模糊的想法,不要直接动手。先通过提问把它变成清晰的需求,再把需求拆成可执行的任务。
2. User Story(用户故事)
格式:
作为一个 [角色],我想要 [功能],以便 [价值/目的]。
三要素:
| 要素 | 含义 | 示例 |
|---|---|---|
| 角色(Who) | 谁来用这个功能? | 运营人员 |
| 功能(What) | 要什么? | 导出数据时等待不超过 10 秒 |
| 价值(Why) | 为什么要? | 提高日常数据分析效率 |
好的 User Story 遵循 INVEST 原则: - Independent(独立的):不依赖其他 Story - Negotiable(可协商的):细节可以讨论 - Valuable(有价值的):对用户有明确价值 - Estimable(可估算的):开发能评估工作量 - Small(小的):一个 Sprint 内能完成 - Testable(可测试的):有明确的验收标准
3. Acceptance Criteria(验收标准)
验收标准回答的问题是:怎么算做完了?
常用格式(Given-When-Then):
Given(前提条件):系统中有 1 万条用户数据 When(操作):运营人员点击"导出 Excel"按钮 Then(预期结果):10 秒内生成 Excel 文件并弹出下载提示
验收标准的作用: - 开发知道做到什么程度算完成 - 测试知道怎么验证功能是否正确 - 产品验收时有据可依,减少扯皮 - 出了分歧,以书面确认的验收标准为准
4. 需求确认检查清单
拿到一个需求后,问自己这些问题:
| # | 问题 | 确认了吗? |
|---|---|---|
| 1 | 谁是这个功能的用户? | ☐ |
| 2 | 用户的核心诉求是什么? | ☐ |
| 3 | 有没有量化的指标(性能、容量等)? | ☐ |
| 4 | 有没有明确的验收标准? | ☐ |
| 5 | 有没有下游依赖(其他系统、客户脚本等)? | ☐ |
| 6 | 有没有非功能性要求(安全、兼容性、可访问性)? | ☐ |
| 7 | 需求范围的边界在哪里?哪些不做? | ☐ |
| 8 | 以上所有确认是否有书面记录(邮件/文档)? | ☐ |
5. 开源许可证速查
在技术选型时,别忘了检查开源库的许可证:
| 许可证 | 商业项目可用? | 要求 |
|---|---|---|
| MIT | ✅ | 保留版权声明即可 |
| Apache 2.0 | ✅ | 保留版权声明 + NOTICE 文件 |
| BSD | ✅ | 保留版权声明 |
| LGPL | ✅(有条件) | 动态链接可以,静态链接需开源 |
| GPL | ⚠️ | 使用了 GPL 库的项目必须也用 GPL 开源 |
| AGPL | ❌(通常) | 即使是网络服务也必须开源 |
核心原则:不确定的时候,问你的 Tech Lead 或法务。
:::