
1. 需求与规格之间差的不是文案是可被证明我在项目评审会上最怕听到的一句话不是这个做不了而是这个需求很简单。凡是被评价为简单的需求到了交付验收阶段往往就是扯皮重灾区。就拿那行再常见不过的描述来说——输入电话费余额如果小于10元则提出充值提醒否则直接输出。乍一看逻辑清清楚楚甚至可以直接照着写代码。可真到了开发手里问题会像连珠炮一样冒出来余额是整数还是小数小数点后保留几位小于10元包不包括10元整提醒是在界面上弹窗还是往日志里写一行否则直接输出——输出什么输出到哪里输出了之后还继续提示吗如果用户连着查询十次是不是要弹十个提醒窗口这就是典型的需求和规格之间的鸿沟。需求是一句意图是用自然语言表达我想要什么效果规格则是一份契约是用足够精确的语言表达系统在什么条件下、接收什么输入、执行什么处理、产出什么输出、遵守什么边界并且如何证明它做到了。需求写给人类看规格还要写给机器看、写给测试用例看、写给三个月后接手维护的人看。软件需求规格说明书这个词大家都不陌生。很多团队也写了厚厚一叠封面精美目录齐全。但大多数规格书的问题不是写得不够多而是写得不够可证。需求里写系统应具有良好的用户体验这算规格吗不算。什么叫良好你的良好和我的良好可能相差十万八千里。需求里写系统应能处理大量数据这算规格吗也不算。大量是一万条还是一千万条处理耗时是毫秒级还是分钟级把需求写成规格本质上是在做一件事把主观判断转化为客观标准把模糊描述转化为边界条件把我觉得应该这样转化为输入什么、输出什么、在什么约束下、满足什么标准就算验收通过。这就是我在这篇文章里要展开的四件套输入、输出、约束、验收标准。这四个词看着朴素却是所有需求规格书里最值钱的部分。把这四件事写透了开发不会跑偏测试有据可依项目验收不再靠人情、靠感觉、靠大家都差不多就过了吧。这篇文章不是教科书式的理论讲解而是我这些年写需求规格、评审需求规格、被需求规格坑过的实战经验汇总。我会用一个贯穿全文的真实例子——话费充值提醒——一步步演示如何把一句口头需求拆成一份可直接落地的规格说明书同时穿插硬件、前端、后端、算法各个领域常见的输入输出约束问题。做好了这件事你在团队里说话的分量都会不一样。2. 输入定义先搞清楚数据从哪来、长什么样、什么时候算非法2.1 输入的三个层次漏掉任何一个都会出事很多开发拿到需求就急着写代码第一件事就是设个变量接住输入。但输入这件事往深了说包含三个层次来源、内容、格式。三层都定义清楚了输入才算闭环。来源解决的是数据从哪儿来的问题。是用户在界面手动输入还是上游系统通过接口推送是硬件传感器采集的电信号还是另一个服务从消息队列里发过来的事件这决定了你的代码要在哪一层做防御。如果是用户手动输入你要考虑人为操作的各种误触如果是接口调用你要考虑对方系统宕机、超时、返回格式不兼容的情况如果是传感器信号你要考虑噪声、漂移、温漂和电磁干扰。就拿话费余额这个例子来说余额从哪里来如果是用户在输入框里自己填那输入源只有一个如果是从运营商接口实时查询那就要考虑接口超时、返回空值、余额字段被截断等情况。来源不同处理的优先级完全不同。内容解决的是数据代表什么的问题。这个输入的含义是什么单位是什么编码规则是什么还是拿话费余额举例余额的单位是元还是分运营商接口通常返回的是以分为单位的整数防止浮点精度问题而用户界面展示时需要转换成元。如果规格书里不写明单位前端百分之百会踩坑——直接把12345显示成12345元或者把123.45显示成123元。这种问题在需求评审时根本看不出来只有联调时才会炸出来。更要命的是这类问题往往不是单一模块的问题牵一发动全身改起来涉及接口、存储、展示三层。格式解决的是数据怎么描述的问题。是字符串还是数值定长还是变长编码是UTF-8还是GBK允许不允许小数最大长度是多少别小看格式定义很多线上事故的根源就是格式写得不严。比如输入框限制只能输入数字1-99这个校验是前端做了就行还是后端也要做如果只在前端做用户用爬虫直接调接口就可以绕过限制把1000传进去后端一处理就崩了。我在实际项目里见过太多类似的事了——前端用el-input限制只能输入数字1-99用户老老实实在界面上操作一点问题没有但只要有人绕过界面直接发请求后端就裸奔了。2.2 异常输入不是到时候再说它本身就是规格的一部分我见过太多规格书把正常路径写得天花乱坠一到异常路径就四个字异常处理。这四个字大概是软件工程里最大的废话之一。异常处理到底怎么处理是给用户弹个输入错误的提示还是静默丢弃是重试三次还是直接熔断是记日志还是不记这些不写清楚就等于把设计决策丢给了开发——而且每个开发的理解都不一样。回到话费充值的例子。输入电话费余额——那用户要是输入了abc呢输入了-5呢输入了0呢输入了一个超级大的数99999999999999999999呢输入了空格呢输入了前后有空格、中间有逗号的1000呢这些都不是钻牛角尖而是在真实使用场景中必然会发生的事。规格书里必须明确哪类输入视为合法哪类视为非法对于非法输入系统做什么响应——是拒绝并提示还是自动纠正还是记录后忽略。每条都要有明确的规则。这里有一个我个人的经验法则每定义一个合法的输入域至少要配套定义三个非法的输入样例。合法输入告诉你系统该怎么走非法输入告诉你系统不能怎么崩。测试用例的边界值分析等价类划分、边界值法本质上就是在执行这条法则。比如只允许输入1-99的整数那边界值就是1、99、0、100再加上非数字字符、空字符串、小数、负数、超长字符串。这些全部要写进规格书的异常处理部分测试才能有打钩的依据。2.3 输入校验的分工前端管体验后端管安全很多团队在讨论输入校验时会发生这样的争论这个校验到底前端做还是后端做我的答案向来是前端负责体验后端负责安全。前端的校验是为了让用户操作顺畅输错了当场给提示不用等到提交之后才看到一个冷冰冰的报错后端的校验是为了保证系统的正确性和安全性因为任何外部输入都是不可信的。这个道理适用于所有带输入的系统。你打开任意一个网站几乎都能看到类似的例子——el-input只能输入数字1-99这种热搜词本质上反映的就是前端输入限制这个高频需求。但真正的工程化做法是前端限制用户输入的数字范围1-99、禁止输入小数点和字母这只是为了不让用户在界面上犯错后端必须同样做一遍校验从请求参数里拿到值之后先判断类型、再判断范围、再判断合法性哪怕是同一个字段。我见过一个真实案例一个库存管理系统前端限制数量只能输入1-99但后端没做校验。有一天运营人员用Excel批量导入数据其中一行数量填了1000系统直接把这批货当成1000件入账导致库存虚高、采购计划完全被打乱。这个锅该谁背前端吗导入接口根本不走前端页面。只能说需求规格书里没写清数量字段的校验是前后端双端都要做这件事。所以写规格时建议每个输入字段都单独列一张表字段名、类型、取值范围、是否可空、校验规则、前后端是否同时生效、非法输入时的响应行为。这张表写清楚了开发照着做测试照着测谁都不用临场发挥。认真做这一张表比在需求评审会上吧啦吧啦说半小时都管用。3. 输出定义用户能感知到的变化才算功能完成3.1 输出不只是返回值而是时间、内容、通道的组合输入定义清楚了紧接着就是输出。很多年轻工程师对输出的理解就是函数return一个结果或者说接口返回一段JSON。但在需求规格的语境下输出是用户或下游系统能观察到的、由本功能引起的任何状态变化。这个定义比返回值要宽得多也更接近真实业务。输出至少包含四个维度内容输出了什么、格式以什么形态输出、时机什么时候输出、通道从哪个渠道输出。这四个维度缺一不可。还是讲话费充值提醒这个需求。如果余额小于10元则提出充值提醒这句话里只描述了内容和触发条件完全没有提到格式、时机、通道。是多大的字体什么颜色弹窗还是横幅会不会发出声音提醒展示一次就够了还是要持续展示用户点了知道了之后是否还出现如果用户打开App和短信提醒同时触发两者会不会重复这些问题不定义输出就是一句空话。3.2 成功路径和失败路径每条分支的输出都要单独定义否则直接输出——这句话在需求原文里只有六个字但它其实要求我们定义至少两个分支的输出余额小于10元时走提醒分支余额大于等于10元时走正常显示分支。两个分支的输出内容、输出格式、触发时机都不一样。写规格的时候要把每个分支都当成一个独立的输出项来定义。这里有一个很容易犯的错只定义成功路径的输出不定义失败路径的输出。比如话费余额查询接口超时了用户界面上显示什么是一片空白还是网络异常请稍后重试余额字段返回的值是null界面上怎么处理是显示——还是按0处理再比如说用户输入了非法字符你是要返回一个提示信息还是直接不响应这些都是输出定义的一部分。一个完整的输出规格必须覆盖所有处理路径包括正常分支、异常分支、边界分支。只有一条主路径的系统是不存在的。做硬件和嵌入式系统的人对这一点体会更深。像海康相机使用IO触发模式并输出NG/OK这类场景输出的是电平信号规格书里就要定义清楚触发后的响应延迟不超过多少毫秒、输出电平是高电平有效还是低电平有效、输出脉冲宽度是多少、NG和OK分别对应什么电平组合。这些参数差一点都不行下游的PLC就是靠这些电平信号决定要不要把工件剔除的。输出信号不达标产线上就是一大批废品。规格书里写输出OK/NG信号等于什么都没写必须具体到电平、脉宽、时序才算合格。3.3 格式的坑精度、时区、编码、序列分隔符输出格式是我在评审时最常发现问题的环节而且这些问题往往不是对不对的问题是够不够精确的问题。举个例子一个报表功能需求写着导出Excel文件。听起来很简单对吧但下面这些问题你让开发自己拍板十个人能给出十种答案Excel文件是xls还是xlsx格式如果系统用的第三方组件是试用版导出的文件会不会带水印比如热搜里本页由试用版打印控件Lodop6.2.6输出那个问题金额字段保留几位小数负数用什么颜色标出日期是2025-01-15还是2025年1月15日还是2025/01/15时间用的是北京时间还是UTC如果用户电脑的locale设置是英文日期会不会变成15 Jan 2025所有这些细节测试的时候都能通过唯独到客户现场验收时会出问题。客户用惯了某种日期格式你给的恰好是另一种人家直接一句话打回来这格式不对改一下。改动本身不大但流程要重新走、回归要重新测、版本要重新发成本翻好几倍。规格书里把这些格式定义清楚就是一句话的成本能在后期省掉十几倍的返工成本。这就是为什么我反复强调输出格式不是实现细节是需求规格的一部分。再比如字符串输出空格、换行、逗号分隔符这些看似无关紧要的东西在下游做解析的时候就是天壤之别。假设你的系统输出OK给下位机下位机收到的是OK\r\n还是OK\r\n\r\n回车换行多了少了一个下位机解析就可能失败。搜索词里格式化输出、输出格式无效、C指定顺序输出这类问题之所以长期频繁出现根源就在于很多人在写代码时根本没把输出格式当成需求来定义而是当成自己的个人风格来处理。同一个系统里张三输出带换行李四输出不带下游对接就永远在修Bug。4. 约束条件不是限制开发是框定实施方案的边界4.1 业务约束、技术约束、资源约束缺了哪类都会失控约束是需求规格四件套里最容易被忽略的一件。原因很简单做需求分析的时候人们满脑子都是功能而约束是你不能做的事听起来像是对需求的否定所以大家本能地回避。但恰恰是这些不能做的事决定了项目能不能落地以及能活多久。约束条件大致分三类。业务约束是指业务规则、法规要求、行业标准等施加的限制比如余额低于10元提醒里的10元就是一个业务规则参数再比如金融系统的每笔交易金额不得超过单日限额、医疗器械系统的数据保存年限不得少于5年都属于业务约束。技术约束是指技术选型、平台能力、接口兼容性等方面的限制比如必须兼容IE11数据库必须是MySQL 5.7不能引入新的第三方依赖或者Web.xml里配置的约束必须与Servlet版本匹配这类具体问题。资源约束是指算力、内存、带宽、电量、时间等物理资源的限制比如模型推理延迟不能超过200ms设备电池续航不低于8小时服务器最多只能分配2核4G内存。这三种约束缺了哪类都容易失控。业务约束没写清开发可能用一套不满足合规要求的方案上线前一天才被法务叫停技术约束没写清开发可能引入了一个虽然好用但和现有架构冲突的库后面维护成本暴增资源约束没写清功能倒是做出来了一到高并发就崩一说优化就说重构吧。4.2 用约束的视角重新看热搜里的那些问题如果用一个约束前置的视角去看日常开发中的疑难杂症你会发现很多问题早在需求阶段埋下了种子。举个例子时序约束这个词在数字电路设计里是一个绕不开的概念——芯片里的时钟信号、数据信号什么时候建立、什么时候保持、什么时候有效都必须满足严格的时序关系否则电路就会工作不稳定。对应到软件领域微服务之间的调用同样存在时序约束A服务必须先于B服务完成数据写入B服务才能读到正确数据如果A和B是并行部署的这种时序上的依赖就必须明确写进规格里否则就会出现奇怪的偶发Bug。IO约束也很典型。一个系统的输入输出能力不是无限的磁盘IO、网络IO、数据库连接数都有上限。需求规格里不写本系统需支持每秒1000个并发查询这类IO性能约束开发就不知道要在连接池、缓存、读写分离上做多少工作。做得少了上线压测直接挂做得多了成本超支一样是问题。还有算力约束——热搜里那句算力约束下提升大语言模型能力的资源配置建模翻译成人话就是如果服务器只给你一块GPU、显存只有16G那你要部署一个百亿参数的模型就得在量化、剪枝、蒸馏、批处理这些方案里做组合优化而不是天真地认为模型越大越好。这类约束恰恰是需求规格书里最容易被说得含糊其辞的部分。很多时候业务方只是说我们要AI能力完全不提服务器预算和延迟要求等模型跑起来了才发现根本带不动。硬件领域的例子更能说明问题。电路设计中电流采样电路输入滤波这个热搜词背后就是一个典型的输入性能约束采样电路需要把输入信号里的高频噪声滤掉但滤波时间常数不能太大否则信号变化太快时会失真。具体到规格书里就是输入信号带宽为0-1kHz采样频率不低于10kHz纹波抑制比不小于40dB。没有这些约束硬件工程师只能用经验估算做出来能不能满足应用场景全靠赌。再如3W功放输出转成AUX输入这种电路设计需求表面看只是一根线接一根线的事实际上涉及两个关键约束电平匹配和阻抗匹配。功放输出是功率信号AUX输入是线路电平信号规格书里不写明输出电平不得高于AUX最大输入电平和输出阻抗与输入阻抗需匹配这两个约束直接把输出接到输入上轻则声音失真重则烧毁输入级电路。所以约束不是可有可无的备注它和主功能一样是需要显式定义、显式验收的。4.3 约束之间的取舍把不可能三角写明白约束不是写得越多越好因为约束之间经常互相冲突写的时候就要把这个矛盾暴露出来而不是藏着掖着。最经典的冲突是功能、质量、成本之间的三角关系想做得又快又稳又便宜通常是不可能的。需求方如果既要极致的性能、又要极低的成本、还要最短的开发周期这就是在提一个不可能完成的需求。规格书的价值之一就是把这个矛盾转化为优先级声明当性能、开发周期和成本发生冲突时哪一项是不可妥协的底线算法领域有个没有免费午餐定理说的是没有一种算法能在所有问题上都表现最优。需求规格里也一样面对性能约束和精度约束的冲突时必须明确取谁的优先级。比如目标检测系统帧率要30FPS精度要95%mAP在给定的嵌入式平台上这两个指标同时达到可能不现实就必须写明精度低于90%时是否允许如果允许那验收时按哪个指标为主这些都是约束层面要回答的问题。我自己有一个习惯在规格书的约束章节对每条约束都标注来源和硬/软属性。硬约束是不可协商的不满足就不能验收软约束是目标值尽量达到但不作为验收否决项。这么做有两个好处一是倒逼需求方想清楚自己到底哪些红线不能碰二是防止开发在实现时用约束太多当借口拖延进度。把硬约束和软约束分开写评审时一目了然。5. 验收标准怎么写才能让做完了不再靠感觉5.1 验收标准的三条铁律可测试、可量化、可追溯前三个维度输入、输出、约束定义了系统应该是什么样验收标准则定义了如何证明它确实是这样。做完了这句话在软件工程里是最不严谨的说法。什么叫做完了开发说做完了测试测了三轮也觉得可以了结果产品经理一看说这不是我想要的。原因在于大家心里各自有一套验收标准只是从来没写下来对齐过。写验收标准我建议记住三个词可测试、可量化、可追溯。可测试意味着每一条验收项必须能被客观地执行验证而不是主观感受。可量化意味着必须给出具体的数值、阈值、次数、时间等量化指标。可追溯意味着每条验收项都能追溯到需求规格的前面章节——输入定义、输出定义或约束定义中的某一条不能凭空冒出一个验收项。5.2 从一行需求到一张验收表拿话费充值提醒需求来演示一下。原始需求输入电话费余额如果小于10元则提出充值提醒否则直接输出。如果把验收标准写成系统能正确判断余额是否小于10元并做出提醒那等于没写——什么叫正确判断什么叫做出提醒翻来覆去还是需求原句。完整的验收表应该是这样编号验收项操作步骤期望结果对应规格章节ACC-01正常触发提醒输入余额5.00元界面弹出充值提醒提醒文案为余额不足请及时充值文案中显示当前余额5.00元输出定义-分支AACC-02正常不触发提醒输入余额10.00元界面显示当前余额10.00元无充值提醒输出定义-分支BACC-03边界值正好等于10元输入余额10.00元不触发提醒按小于10元严格小于处理输入定义-取值范围ACC-04边界值9.99元输入余额9.99元触发提醒输入定义-取值范围ACC-05非法输入-非数字输入abc界面提示请输入合法的余额数字不触发提醒输入定义-异常处理ACC-06非法输入-负数输入-1界面提示余额不能为负数不触发提醒输入定义-异常处理ACC-07精度验证输入6.666元三位小数按四舍五入处理为6.67元触发提醒并显示6.67元输出定义-精度规则ACC-08重复触发触发提醒后再次查询余额仍小于10元再次弹窗提醒不因之前提醒过而静默业务约束-提醒频率策略这张表里的每一项开发照着改代码测试照着写用例产品照着点一遍验收三方拿到的是同一份标准。最关键的是这张表的每一项都能追溯到一个具体的规格条目——ACC-03和ACC-04对应的是小于10元这个边界条件的精确语义严格小于还是小于等于如果不写明10.00元不触发、9.99元触发开发写成小于等于10元触发你也没法说它错但就是和期望不一样。验收表和前面章节形成互锁这就是可追溯的价值。5.3 验收标准要和用例设计同步做不是等开发完再补很多团队的流程是需求写完、开发做完、测试启动然后测试经理才开始写测试用例。这个流程里有个巨大的隐患——测试用例往往受限于功能已经做成什么样而不是需求当初要求什么样。功能实现了什么就测什么需求里没实现的东西自然就不会测最后验收就变成了一场确认开发做的东西是对的的循环论证。正确的做法是需求规格评审通过之后测试用例的设计就应该基本成型验收标准表和测试用例表应该是同一件事的两种形态。TDD测试驱动开发理念之所以被很多人推崇底层逻辑就在这里先把应该发生什么写成一个失败用例再去写代码让它通过。需求规格书里的验收标准表就是TDD的测试用例之源。哪怕团队没有正式采用TDD把验收标准前置到开发启动之前也能起到相同的作用——开发每次写代码之前都知道自己要满足什么而不是写完再看能交付什么。我在实际操作中甚至会要求开发在写代码前先跟着验收标准逐条走查一遍设计文档把这条怎么实现写清楚再动手。多数情况下这一遍走查就能发现规格书里没说清楚的地方避免了一轮返工。6. 实战演练把话费余额小于10元提醒写成一份完整规格6.1 原始需求拆解先列问题清单再填规格现在我们把前面讲的四件套完整地应用到那句话上输入电话费余额如果小于10元则提出充值提醒否则直接输出。在动手写规格之前我会先列一份待确认问题清单。这份清单是我在一个又一个项目里总结出来的——每一条背后都是曾经踩过的坑。就这个需求来讲至少要确认以下问题关于输入余额是用户手动输入还是系统查询返回如果是手动输入最大长度是多少支持小数吗小数位最多几位单位是元还是分负数怎么处理非数字字符怎么处理空值怎么处理关于处理和输出小于10元的边界如何定义9.99算触发10.00算不算提醒以什么形式展示——弹窗、横幅、短信、还是声音提醒文案是什么是否需要显示当前余额否则直接输出是输出当前余额吗输出格式是余额xx元还是纯数字输出到对话框、页面某个区域还是返回给API调用方关于约束这个提醒是每次查询都弹还是限制频率如果用户连续查询十次是弹十次还是只弹一次提醒弹出后用户未确认下次进入页面是否还要再次提醒是否有静默期系统的响应时间要求是多少需要考虑高并发场景吗关于验收哪些边界值必须测试非法输入如何定义提醒文案、金额精度、触发频次分别怎么验证这些问题问完之后需求方如果都能给出明确答案规格就有了扎实的原料。如果有些问题需求方答不上来比如连续查询十次弹几次确实没想过那就要当场约定一个默认策略比如每次查询都触发提醒不做频控并写进规格书作为评审项之一而不是留到开发时让程序员自行发挥。6.2 一份可复用的规格模板照着填空就行经过上述问题清单的确认最终写出来的规格大概长这样。我把这个结构做了个模板后面写其他功能的需求规格基本可以照着填空。1. 功能概述本功能接收用户提交的电话费余额或从运营商接口实时获取判断余额是否低于阈值10元并按约定策略向用户展示充值提醒或当前余额信息。2. 输入规格字段类型取值范围必填校验规则非法输入处理balance数值型单位元0 ≤ balance ≤ 999999.99是最多两位小数拒绝字母、负数、超过99位长度的数字非数字提示请输入合法的金额数字负数提示余额不能为负数超范围提示金额超出支持范围3. 处理逻辑系统收到余额后按四舍五入保留两位小数并比较该值与10.00的大小关系。若值小于10.00进入提醒分支若值大于等于10.00进入余额展示分支。4. 输出规格分支A余额 10元在页面顶部渲染黄色横幅文案为余额不足请及时充值。当前余额XX.XX元。横幅每次查询均展示不具备用户关闭功能页面刷新后消失。分支B余额 ≥ 10元在页面余额展示区渲染文本当前余额XX.XX元无额外弹窗或横幅。5. 约束与假设硬约束金额比较使用Decimal类型禁止使用浮点数直接比较避免0.10.2浮点误差问题。硬约束本功能单次查询响应时间不超过500ms含界面渲染。软约束提醒横幅单次展示时长建议不少于5秒具体样式以后续UI评审为准。假设数据来源可靠性由上游接口保证本功能不做余额为空时的兜底业务逻辑但显示余额信息暂不可用。6. 验收标准即上文的ACC-01到ACC-08表按实际情况扩到十到十五条。6.3 评审时最容易被追问的几个问题规格书写完之后要过评审。评审会上大家问的问题往往集中在那几个地方。首先是对边界条件的咬文嚼字小于10元9.99元到底算不算这个例子在真实需求里反复出现金额、温度、重量、时间这些连续量边界值定义不清就会被追问。建议规格书里明确写出边界值按严格小于处理或小于等于处理并附上对应的测试用例编号。第二是异常输入的处理方式为什么是提示而不是自动纠错如果输入10但是以字符串形式传过来是强转还是拒绝第三是输出在不同终端上的一致性手机端、PC端、短信渠道看到的内容是否一致第四是频率控制低频业务可能不涉及高频业务必被问到——连续触发时要不要限制每次都展示同一个提醒横幅用户会不会被烦死这些问题能当场解答评审就能过解答不了说明规格还有漏洞不要强行推进。7. 写了自己和团队都能执行的规格还需要常回头修规格7.1 输出格式无效这类问题为什么反复出现所有系统开发中都会遇到输出格式无效这样的报错。我之前排查过一个打印模块的问题开发用了一个试用版打印控件导出的单据底部出现了本页由试用版打印控件输出的水印字样。客户自然不接受要求去掉。开发查了一圈发现去水印需要买正式授权只好把授权费用加到项目成本里。这个锅技术层面看是选型没考虑授权问题往深了看就是需求规格书里没有写一条约束所有输出文件不得包含任何第三方组件的水印标识。如果这条约束在需求阶段就写进去选型时就会把是否有水印作为硬性筛选条件根本不会等交付时才暴露。我在实践中总结出一个认知格式不对类问题几乎都是格式定义缺失类问题。虽然表面上是编码问题、工具问题、兼容性问题但根子都可以追溯到需求规格阶段没有把输出格式钉死。所以每次收到输出格式无效这种Bug我不急着改代码先把相关的规格书翻出来看看当初有没有定义格式。如果没定义那这个Bug的修复方案就不仅是改一行代码还要补一条规格记录防止其他人再犯。7.2 约束写得过死反而会拖垮开发强调约束的重要性不等于约束越多越好。约束的价值在于划清边界但边界划得太窄也会把合理的实现方案全部堵死。我曾经在一个项目里写过一条硬约束不允许使用任何第三方库理由是担心供应链安全。结果开发为了不再引入依赖自己写了将近两千行工具代码既耗费时间又引入了一堆没人维护的新Bug。后来把这条硬约束改成软约束原则上不引入第三方库如确有必要需提交安全审查并评估维护成本项目才顺畅起来。这个经历让我意识到硬约束的粒度应该控制在红线级别而不是偏好级别。约束太多太细的另一个问题是把开发者的专业判断空间压缩殆尽。工程师的价值在于面对约束找到最优解如果所有约束都规定得死死的那只需要照着写代码的执行者就可以了。需求分析师应该做的是把业务上、物理上不可突破的底线放在硬约束里而把实现方式偏好、外部依赖限制、代码风格这类可商量的放在软约束里。规格书评审时优先检查硬约束里是否混入了本应是软约束的条目。7.3 规格书不是一次性文档是项目的活地图最后想分享一个容易被忽视的经验需求规格书写完之后不是扔进文档库里吃灰而是要和代码、测试一样纳入版本管理。真实工程中需求变更几乎不可避免。客户今天说10元提醒明天可能变成20元提醒加每月一次优惠券推送。每次变更都要像第一次写规格一样回到输入、输出、约束、验收四个维度重新过一遍更新相关条目同步修订验收标准表。我最怕的就是团队把需求变更直接口头沟通开发默默把代码改了测试不知道、文档不更新、验收标准还是旧的。等到项目里程碑验收时大家拿着旧标准测新功能必然扯皮。我有一个强制要求自己对自己也对自己经手的项目任何需求变更必须在规格书上留下修订记录——变更日期、变更内容、变更影响范围、关联验收条目变更。哪怕只是把阈值从10改成20也要记录。因为10元这个数字可能不只在一个地方出现——提醒逻辑里有一处、界面文案里有一处、测试用例里有一处、用户手册里还有一处。只改代码不改文档三个月后没人说得清当初为什么改、改了哪些地方历史包袱就是这么一点点攒起来的。我一直觉得把需求写成规格不是需求分析师一个人的事也不是项目经理用来压进度的手段。它真正的意义是让团队里所有人——业务、产品、开发、测试、运维——在同一个精确的坐标系里工作而不是各自凭想象构建一套系统。输入、输出、约束、验收标准这四列坐标轴是我目前找到的最朴素也最有效的对齐方式。下次你再拿到这个需求很简单的需求时不妨试着先把它拆成这四个维度一份填下来多半就会发现——那个简单的需求其实复杂得很。而把这个复杂搞清楚的过程正是项目成功的开始。