质量属性与场景:把「要快、要稳」变成可验收的需求
"系统要快、要稳、要好改"不是需求,是愿望。质量属性(quality attribute)必须写成场景(scenario),即"在什么情况下、发生什么事、系统如何响应、用什么数字验收"。写不出数字的场景,验收时一定吵架。
六要素场景模板
一条合格的质量属性场景包含六部分,缺任何一部分都会留下扯皮空间:
来源(source) :谁发起 —— 用户、定时任务、运维、攻击者
刺激(stimulus) :发生什么 —— 提交订单、磁盘写满、并发翻倍
制品(artifact) :作用于谁 —— 下单接口、订单库、网关
环境(environment) :什么状态 —— 正常负载、峰值、降级中
响应(response) :系统做什么 —— 拒绝并提示、切换备用链路、限流
响应度量(measure) :用什么数字判断 —— TP99、恢复时间、丢失条数
把"要快"填进模板:峰值环境下,用户提交下单接口,系统在 1 秒内返回订单号,其中 TP99 ≤ 800ms、TP999 ≤ 1.5s,错误率 < 0.1%。这才叫需求。
常见质量属性与可验收指标
| 质量属性 | 场景化指标示例 | 常被误当成指标的写法 |
|---|---|---|
| 性能(performance) | 峰值 TP99 ≤ 800ms,QPS ≥ 3000 | "响应要快" |
| 可用性(availability) | 单机房故障 5 分钟内自动切流,月度可用性 ≥ 99.95% | "要高可用" |
| 可修改性(modifiability) | 新增一个支付渠道工作量 ≤ 3 人日,改动不超过 2 个模块 | "要好扩展" |
| 可测试性(testability) | 核心聚合的单元测试可脱离数据库运行,单测覆盖分支 ≥ 80% | "要能测" |
| 可部署性(deployability) | 单服务灰度发布 ≤ 10 分钟,回滚 ≤ 3 分钟 | "要能快速上线" |
| 安全性(security) | 越权访问在网关被拦截,审计日志留存 180 天 | "要安全" |
| 可观测性(observability) | 一次线上报错 5 分钟内定位到具体服务与接口 | "要有监控" |
| 成本(cost) | 单万单成本 ≤ 0.8 元,扩容 1 倍容量成本增幅 < 1.2 倍 | "要便宜" |
注意:限流、熔断、降级的具体实现属于弹性设计专题(第 16 章),可观测性体系建设属于第 17 章。本章只解决"怎么把愿望写成可验收的句子"。
场景 → 指标 → 验收 三段式
写完后自检三步,任何一步断裂都说明场景不合格:
| 步骤 | 要回答的问题 | 反例 |
|---|---|---|
| 场景 | 谁在什么情况下触发 | "系统应该很快" |
| 指标 | 用什么数字衡量、在哪个分位 | "平均响应 100ms" |
| 验收 | 谁来测、怎么测、不达标怎么办 | "上线后观察一下" |
验收方式要具体到可执行:
验收方式 A(压测):用 1:9 读写比的脚本打到 3000 QPS 持续 10 分钟,
观察 TP99 与错误率,连续 3 轮达标即通过
验收方式 B(演练):主动 kill 主库所在可用区,记录自动切流耗时
验收方式 C(改造):在隔离分支上实现"新增支付渠道",统计最终 diff 的模块数与工时
平均值的陷阱:延迟分布是长尾的,平均值 100ms 可能对应 TP99 = 3s。任何性能类质量属性都必须带分位(percentile),否则可以被几个慢请求或几个快请求轻易操纵。
质量属性之间会互相拉扯
| 提升 A | 通常牺牲 B | 具体表现 |
|---|---|---|
| 性能(缓存、本地状态) | 一致性、可修改性 | 缓存让读到旧值;本地缓存让改数据结构变难 |
| 可用性(多副本、异步) | 一致性、成本 | 副本滞后;机器数量翻倍 |
| 可修改性(抽象层) | 性能、可理解性 | 每层转发带来开销;调用链变长 |
| 安全性(加密、审计) | 性能、可部署性 | 加解密损耗;密钥管理增加发布步骤 |
| 可测试性(依赖注入) | 上手成本 | 新人需要先理解抽象与装配 |
取舍要有依据:先排出优先级(业务上哪个属性不可让步),再定可牺牲的余量(最多慢多少、最多不一致多久)。这个排序应该写进架构决策记录,而不是留在某个人脑子里。
从质量属性到架构手段
质量属性决定结构,而不是反过来。常见的映射:
可修改性 → 按业务能力划分模块,依赖倒置,接口定义在调用方(第 04 章)
可用性 → 无状态化、多副本、故障隔离与降级(第 16 章)
性能 → 减少跨边界调用、批量与异步化、数据就近(缓存细节见 system-design 第 16 章)
一致性 → 数据所有权单一、事件与事务边界(第 10、14 章)
可观测性 → 统一 trace 上下文、结构化日志、指标埋点作为验收项(第 17 章)
可部署性 → 部署单元粒度、配置外置、向后兼容的接口演进(第 18 章)
反向也成立:如果某个质量属性没有对应到任何结构措施,它就不会被实现。"要高可用"却不做无状态化、不给数据副本,就只是一句口号。
把质量属性写进需求文档
推荐在需求模板里固定留一节,每个关键用例配一张表:
用例:用户提交订单
功能:校验库存、生成订单、发起支付
质量场景:
1) 正常:峰值 3000 QPS,TP99 ≤ 800ms,错误率 < 0.1%
2) 过载:QPS 到 6000 时,保护 80% 的正常用户,多余请求快速失败(≤ 200ms 返回)
3) 故障:支付回调延迟 30 分钟内到达,订单最终一致,不产生重复扣款
4) 变更:新增支付渠道 ≤ 3 人日,不改动订单核心校验逻辑
验收:压测脚本 + 演练剧本 + 隔离分支实测,数据留档
这张表可以直接变成测试用例与监控告警的阈值来源,一举三得。
常见误区
- 只写平均延迟:没有分位的性能指标在长尾场景下毫无约束力。
- 把 SLA 当内部指标:对外承诺 99.9% 不等于内部每个依赖都能用 99.9%,依赖串联会放大不可用。
- 所有属性都要最好:等于没有优先级,最终每个都做不到位。
- 用"高并发"当需求:没有业务动作和读写比的并发数字无法推导结构。
- 验收推给上线后:场景写完就该确定测法,否则上线时只能靠感觉。
小结:把愿望变成需求的唯一路径是六要素场景 + 分位指标 + 明确验收方式。写完后检查质量属性之间是否优先级明确、是否有对应的结构手段;没有结构手段支撑的质量属性等同于没写。