第四章 需求像雾像雨又像风

一

入职第四周,王志远终于不再只是修 bug 了。

赵北辰在 Sprint Planning 会上给他分了一个新需求——"用户导出功能优化"。这是他第一次从头开始做一个完整的功能,而不是在别人的代码上修修补补。

需求是产品经理李想提的。李想是个说话很快的年轻人,戴着 AirPods,走路带风,永远在开会或者去开会的路上。他在需求评审会上用五分钟讲完了这个需求:

"现在用户导出功能太慢了,客户投诉了好几次。你们优化一下,让它快起来。另外,客户说导出的格式不太好用,你们也改改。就这样,不复杂,两天应该够了。"

说完他就走了,去赶下一个会。

王志远在笔记本上记了两行字:

  1. 导出功能优化,让它快起来
  2. 导出格式改改

他看着这两行字,觉得挺清楚的。快起来,改格式。没什么难的。

他打开 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 的格式把自己理解的需求写了出来,每条都附了验收标准。他还列了一个"待确认问题清单":

  1. "快起来"的具体指标是什么?客户能接受的最大等待时间?
  2. 新增 PDF 格式是否需要支持自定义模板?
  3. 现有 Excel 格式是否有下游系统依赖?如果有,哪些字段不能改?
  4. 导出数据量的上限是多少?十万条?一百万条?
  5. 异步导出的通知方式——站内通知、邮件、还是两者都要?

李想看到这份文档的时候,明显愣了一下。

"你这个……写得挺详细的。"他翻了翻,语气比昨天缓和了很多。

"赵哥教我的。"王志远老实说。

两个人花了四十分钟,把每一条 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 或法务。

:::