以一个明确标为假设的文件转换任务,把模糊愿望变成输入输出、约束、验收和回退条件,展示工程决策怎样留下依据。
“把它做快一点”还不是完整问题
有人提出:做一个工具,把一批文本文件转换成另一种格式,越快越好。立即写一个循环当然可以开始,但我们还不知道:允许哪些输入,失败后原文件怎样处理,输出如何判定正确,速度是首个结果时间还是整批完成时间。
工程师要把需求放到现实约束里,构造并交付可验证的结果。实现算法是其中一部分,界定目标、识别风险、比较方案、验证和维护也属于工作。NASA系统工程手册把需求、设计、实现、验证与确认组织为相关过程,可作为方法参考。[1]
下面全部是构造案例,不是本人或任何企业的项目经历;数字是为说明决策而设定的假设,不是实测。
先写任务约定的骨架
假设需求是:把UTF-8文本中的某种字段结构转换为新格式。输入为一个明确文件清单,总量上限100MB;输出写入新目录,默认不改原件;转换失败必须报告文件、原因和已完成范围。
“保留信息”也要具体:合法输入的字段值与顺序应按约定保留;未知字段不得静默丢弃;重复键、无效编码和过长记录要有明确处理。空文件应得到空结果还是错误,需要事先决定。
验收不能只写“运行成功”。可以列出普通记录、空输入、边界长度、重复键、非法编码、磁盘写入失败等案例,规定每类预期。需求中的每一项重要承诺,应能对应到一个检查或明确的人工评审。
约束和目标不要放在同一张模糊分数表里
约束是方案必须满足的条件,例如不得覆盖原件、必须能识别不支持的编码;优化目标是在合法方案之间比较的指标,例如处理耗时和内存占用。不能用“快10%”抵消“偶尔悄悄丢数据”。
若提出“1秒内完成”,还要说明输入大小、机器、是否包含读写、冷缓存或热缓存,以及多少次运行满足。否则不同测试条件下的数字没有可比性。对于本案例,我们只拟定评估方法,不声称已经达到某个耗时。
NASA手册区分verification与validation:前者检查产品是否满足规定要求,后者关注产品能否实现预期用途。[2] 若用户真正需要增量处理,而我们把一次性批量转换做得完美,也可能通过了若干测试却没解决需求。
权衡必须放在同一任务与基线上
候选A一次把全部文件读入内存,实现较简单;候选B按记录流式处理,能限制峰值内存,但错误恢复和跨记录状态可能更复杂。先检查两者是否都满足语义,再在同一输入、配置和正确性要求下比较。
若总量与内存预算很小,A可能足够;若最大输入无法装入内存,A就不是合法候选,不能继续拿平均速度争胜。若B让定位错误更困难,就要把诊断与维护成本一并记录。
最强反例是:需求随时会变,何必先写?回应不是冻结一切,而是保留最小可修改约定。没有约定也会发生选择,只是选择被藏在代码和个人记忆里;一旦变化,很难知道哪些行为属于承诺。
一份真正能复用的决策记录
| 字段 | 本例填写方式 |
|---|---|
| 目标与用户 | 谁用转换结果,保留哪些字段 |
| 约束 | 原件只读、输入上限、错误可定位 |
| 候选与基线 | 整批读取、流式读取;同一语义 |
| 证据 | 样例、负例、资源测量;未做则留空 |
| 停止与回退 | 输出校验失败不替换既有结果 |
| 后续责任 | 版本、维护者、已知不支持项 |
这张表不是要求每个小任务写厚报告,而是让关键承诺与证据有位置。对低风险、一次性工具,几行记录可能足够;对长期服务或安全关键系统,证据深度需要提高。
把模糊需求改写成可以否定的承诺
对前面的转换工具,先写三条具体要求。R1:在合法输入上,输出记录数、字段值与顺序符合映射规则。R2:处理过程中不改动输入文件。R3:遇到非法编码或不支持结构时,任务报告失败,不能留下被标为成功的正式结果。
分别设计证据:R1使用独立预期文件或不同实现作对照;R2以只读方式打开输入,检查文件及所在目录的写入权限、全部写入路径和写入尝试;R3注入非法输入与写入失败,检查退出状态和输出目录。运行前后哈希在正确读取同一份完整输入、检查窗口内无并发修改、哈希实现正确且不考虑碰撞的假设下,只能支持两个检查时刻的字节一致,不能单独证明处理中从未改动。只读打开一个句柄也不能阻止经其他句柄或目录替换写入,因此保护承诺还要结合权限与运行条件。单一“进程退出0”不能覆盖三条要求。
NASA的需求编写指南要求语句清晰、可核验,并避免fast、easy等没有操作定义的词。[3] 对本例而言,“快”可以先作为愿望保留,直到明确负载、机器与时延指标;不应随手填一个没有用户依据的秒数,制造虚假的精确性。
还要区分需要与设计限制。用户需要不丢数据,不一定需要某一种数据库或队列;若过早把偏爱的实现写进需求,会排除其他可行方案。只有外部兼容、安全或环境确实要求时,才把具体技术设为硬约束。
比较方案,先检查可行域,再讨论最优
假设内存预算是64MB,最大输入100MB。整批读取方案如果确实需要把完整输入和输出同时驻留,峰值可能超过预算,就应先判为不满足约束;不能用平均输入只有1MB来绕过已经承诺的上限。
流式方案也不自动可行。若转换需要全局排序,单条记录处理后立即写出可能不满足输出顺序;若存在跨记录引用,就可能需要额外索引或多遍处理。可行性来自任务语义与资源条件共同满足,不来自“流式更先进”的标签。
在合法方案中,可以比较耗时、内存、实现复杂度与维护成本。若A比B快但占更多内存,且二者都在预算内,通常不能不交代偏好就宣布A全面更优。应先给出决策原则,例如优先满足交互时延,再在剩余方案中降低维护成本。
如果没有一个方案同时满足全部硬要求,应回到需求方暴露冲突,讨论缩小输入、调整时限或增加资源。不能在实现中偷偷降低要求,交付后再用“工程总要权衡”解释。
一个完整的失败—修订闭环
假设初版采用逐文件写输出,正常样例全部正确,却在第37个文件遇到非法编码,前36个已写入。若需求只承诺每文件独立提交,这可能是合法部分成功;若需求承诺整批要么全有要么全无,就是缺陷。错误性质首先由契约决定。
修订可以选择整批暂存后统一提交,也可以把用户可接受的“部分成功”明确纳入需求。前者增加临时空间与提交复杂度,后者增加用户处理未完成文件的负担。两者不是纯代码风格之争,而是不同使用承诺。
记录应写明:触发输入、受影响输出、原要求、原因、选择、回归用例、恢复办法。若此前已交付错误结果,还要确定哪些使用者或产物受影响;把补丁合入并不自动修复历史数据。
这个闭环的价值在于留下可追溯的判断。后来的维护者能看出为什么选择某种行为,也能在约束变化时重新评估,而不必从零猜测原设计动机。
验收矩阵要填具体结果,不能只填“有测试”
对R1的字段映射,写清输入含哪些字段,预期输出是什么,采用人工审查还是独立实现比较;对R2的原件保护,列出正常、失败和中断三个路径;对R3的错误报告,列出错误码、失败文件标识与是否存在正式输出。
证据表应允许“未验证”这一状态。只有测试文件存在,还没有说明它被执行;只有执行日志,也要确认实际输入、断言与被测版本。覆盖率可以帮助发现空白,却不能证明断言本身正确或需求没有遗漏。
若一条要求无法设计任何检验,先检查它是否含糊、不可达或层级不对。有些用途确认适合用户试用或评审,不必强行写成单元测试;但仍要明确参与者、问题、观察与结论,而不是留一个笼统的“体验通过”。
验收的最终输出应是承诺与证据的对应关系。它让未完成项可见,也使下一次需求变化能定位受影响检查。指标越多不必越可靠,关键是重要承诺有没有独立、适当的支撑。
交付结果,也交付判断的边界
本例尚未实现工具,所以可交付的是需求与验收模板,不能把它登记为已通过测试的产品。真实实施时应保留输入样例、运行命令、原始结果和失败,并由独立检查核对输出。
一个工程结果值得信任,不仅因为它在某次演示中工作,还因为别人知道它满足哪些条件、如何发现失败以及怎样恢复。下一篇会继续讨论:写出局部功能之后,系统责任还包括什么?
来源、证据与阅读路线
- NASA Systems Engineering Handbook,第4章:利益相关方期望、需求与设计过程
- NASA Systems Engineering Handbook,第5章:产品实现、验证、确认与移交
- NASA Systems Engineering Handbook,Appendix C:How to Write a Good Requirement,需求清晰性与可核验性
证据范围:本文以构造案例讨论需求、方案取舍与验收设计;尚未实现所述转换工具,也未执行性能测量或故障注入验证。
写下你的评论
要发表评论,您必须先登录。