1.5 需求到技术的翻译
一句话
翻译不是 “把需求写成设计”,而是把业务口语换成三样技术可判定的东西——可测量的验收口径、不可谈判的硬边界、显式列出的待定项;翻不出数字,通常不是技术不行,而是场景没交代清楚。
先分清三个动作:翻译 ≠ 设计 ≠ 选型
| 动作 | 输入 | 输出 | 谁做 | 失败表现 |
|---|---|---|---|---|
| 翻译 | 业务原话 | 场景 + 指标 + 硬边界 + 待定项 | 架构师 + 业务方 | 输出里出现 “Redis / K8s / 微服务” |
| 设计 | 翻译结果 | 分层、模块、接口、数据模型 | 架构师 + 开发 | 翻译还在变,设计已经冻结 |
| 选型 | 设计约束 + 候选清单 | 一个具体产品 + 退出路径 | 架构师 + 采购 | 用产品名代替需求(比较维度见 4.3 技术选型的评估维度) |
⚠️ 三个动作混在一起,最典型的症状是需求会上讨论 “要不要上 Kafka”。这不是会议跑偏,是翻译没做完就跳到了选型。
第一步:按三类归口,三类翻法完全不同
按 1.1 功能性需求与非功能性需求 的三分法先归类,因为三类的翻译产物不一样:
| 类型 | 翻译成什么 | 判据 | 例子 |
|---|---|---|---|
| 功能性需求 | 触发 / 输入 / 规则 / 输出 / 异常分支(五要素) | 换个技术栈仍然成立 | 订单 30 分钟未支付自动取消 |
| 非功能性需求 | 六要素质量属性场景 | 有数字、有负载前提、有测量方法 | P99 ≤ 200ms @ 5000 TPS |
| 约束 | 一张 “硬边界清单”,不可谈判 | 没有优化空间 | 数据必须境内存储 |
约束最容易被当成 NFR 讨论,然后在会上被反复问 “能不能优化一下”——它不能。
第二步:把一句话扩成 “质量属性场景” 六要素
一个可验收的指标,必须能回答六个问题(SEI 的 quality attribute scenario 结构):
| 要素 | 要回答 | 缺了会怎样 |
|---|---|---|
| 刺激源 | 谁 / 什么系统发起 | 不知道峰值从哪来,容量估算无依据 |
| 刺激 | 具体动作是什么 | 把 “查询” 与 “下单” 混成一个指标 |
| 环境 | 什么状态下发生(常态 / 峰值 / 降级中 / 故障中) | 常态指标冒充峰值指标 |
| 制品 | 哪个组件 / 接口 / 数据 | 指标无法归属,没人认领 |
| 响应 | 系统做什么 | 只写 “要快”,不知道快在哪一步 |
| 响应度量 | 用什么数、什么分位、什么窗口判定 | 验收退化成主观判断 |
三条填完的完整场景:
| ID | 场景 |
|---|---|
| QA-PERF-01 | 大促峰值(环境)时,约 8000 QPS 的普通买家(刺激源)并发提交订单(刺激),订单服务(制品)在 P99 ≤ 300ms 内返回 201 且订单落库(响应 + 度量) |
| QA-AVAIL-01 | 单可用区故障(刺激 + 环境)时,订单库主节点(制品)在 60 秒内自动切主(响应),RPO = 0、RTO ≤ 60 秒(度量) |
| QA-SEC-01 | 外部攻击者(刺激源)用撞库脚本(刺激)打登录接口(制品)时,同 IP 10 分钟内失败 50 次即封禁 30 分钟(响应),正常用户登录成功率下降 ≤ 0.1%(度量) |
三条的共同点:没有一个词需要再追问。这就是 “翻译完成” 的判据——读完不用开会就能写出测试用例。
模糊词替换表:业务原话 → 必须追问 → 翻译结果
| 业务原话 | 必须追问 | 翻译结果(示例) |
|---|---|---|
| “要快 / 不能卡” | 哪个操作?分位还是平均?多少并发? | 商品详情 P99 ≤ 50ms @ 3000 QPS,缓存命中率 ≥ 95% |
| “高并发” | 峰值多少?读写比?持续多久? | 峰值 8000 QPS,读写 4:1,持续 30 分钟不降级 |
| “实时” | 秒级还是毫秒级?允许最终一致吗? | 订单状态变更后 3 秒内可查(允许最终一致,最长滞后 3 秒) |
| “海量数据” | 现在多少行?3 年后多少?增长率? | 3 年后 50 亿行,日增 800 万,按 8 条件组合查询 P99 ≤ 500ms @ 200 QPS |
| “要稳定 / 高可用” | 几个 9?维护窗口算不算?只读算不算可用? | 99.95%(年停机 ≤ 4.38 小时),月度维护窗口 ≤ 30 分钟且不计入(换算见 35.1 可用性定义与 9 的换算) |
| “要安全” | 防谁?数据什么级别?泄露后果? | 手机号加密存储(AES-256),审计日志留存 ≥ 180 天,越权访问拦截率 100% |
| “要灵活 / 可配置” | 谁在什么场景改?改了要不要审批?多久生效? | 运营可在后台改活动规则,5 分钟内生效,需二级审批并留痕 |
| “和 XX 系统打通” | 谁的数据谁负责?同步还是异步?冲突谁赢? | 订单状态由交易域单向推送 CRM,异步、至少一次,以交易域为准 |
| “要可扩展” | 加一个维度要多久?要不要发版? | 新增一类支付方式 ≤ 2 人日,不发版(走配置) |
追问的通用句式只有三句:“多少”(量化)、“什么时候”(环境与时序)、“如果不行怎么办”(降级与异常)。
🔑 翻译的产出是 判据,不是方案。一段翻译如果没法直接翻成压测脚本、监控告警或测试用例,它就还没翻译完。
第三步:落到哪一层,停在 “层 + 属性”
| 需求里出现 | 落层 | 主语落在 |
|---|---|---|
| 数据能不能出境、异地容灾、就近接入 | L0 运行时底座 | 5.2 部署模型:公有 / 私有 / 混合 |
| 不能丢、不能重、要顺序、跨服务一致 | L1 分布式原理 | 25.4 幂等设计与去重 |
| 独立发布、独立扩缩容、团队边界 | L2 架构风格 | 服务怎么切由设计阶段决定,翻译只给 “要独立发布” 这条约束 |
| 快、稳、安全、省、好改 | L3 质量属性 | 34.1 性能指标 与 35.1 可用性定义与 9 的换算 |
| 用什么存、要不要缓存、消息可靠吗 | L4 组件与技术 | 翻译阶段不出现;出现即越界 |
| 多久能改完、能不能回滚、谁来值班 | L5 工程效能 | 先记成约束,实现方式在治理阶段定 |
这张表的价值是防越层:需求说 “要扛 8000 QPS”,翻译停在 L3 的指标(P99、错误率、降级策略),不上到 L4 的 “上 Redis”。谁先写出产品名,谁就替后面的人做了决策,而且没留退出路径。
翻译单:一张能填完的表 + 一条能跑的自检
# TR-1042.yaml —— 需求翻译单,一条需求一张
id: TR-1042
source: "大促的时候系统要稳住,别像去年那样崩了;下单要快,不能重复扣款" # 原话,不许改写
type: [NFR-性能, NFR-可用性, FR-幂等] # 按三类归口
scenarios: # 每条都要六要素齐全
- id: QA-PERF-01
source: 大促峰值买家
stimulus: 并发提交订单
environment: 峰值 8000 QPS,持续 30 分钟
artifact: 订单创建接口 POST /orders
response: 返回 201 且订单落库
measure: P99 <= 300ms,错误率 <= 0.1%
- id: QA-AVAIL-01
source: 可用区故障
stimulus: 主库不可用
environment: 业务高峰
artifact: 订单库
response: 自动切主,期间下单降级为预约单
measure: RTO <= 60s,RPO = 0,降级错误率 <= 0.5%
hard_boundaries: # 约束:不可谈判
- 订单数据不出境
- 支付回调必须幂等,重复回调不得二次扣款
verification: # 谁能验收:压测 / 演练 / 用例
- k6 压测 8000 QPS × 30min,看 P99 与错误率
- 混沌演练:kill 主库,计时切主
- 用例:同一购物车 5 秒内重复提交,只生成 1 单
open_items: # 待定项:写清谁在什么时候定
- 峰值 8000 QPS 由谁确认(业务方,10-31 前)
- 降级为预约单是否需要法务确认(法务,11-05 前)
- 跨单元下单是否必须强一致(架构组,11-02 前)
owner: 交易域架构师
open_items 最容易在评审时被删,也最该保留:待定项显式化,等于把 “开发自己猜” 换成 “有人负责拍板”。这一节空着,会被默认成 “按最省事的做法实现”。
每条指标都该过一遍机器检查。二十来行的脚本能拦两类问题:形容词没有数字、有分位却没给负载。
#!/usr/bin/env python3
"""req_lint.py —— 检查需求条目是否「可判定」:模糊词必须有数字兜底。
用法:python3 req_lint.py < reqs.txt (每行一条需求,允许 # 注释)
退出码:0 全部可判定;1 存在模糊且无量化的条目
"""
import re, sys
FUZZY = ["快", "稳", "高并发", "实时", "海量", "安全", "灵活", "易用", "友好", "高效"]
NUM = re.compile(r"\d") # 有数字 = 至少给了量化起点
PCT = re.compile(r"P\d{2}|分位|平均") # 延迟必须说清分位还是平均
LOAD = re.compile(r"@\s*\d|并发|QPS|RPS|TPS|峰值") # 指标必须带负载前提
bad, checked = 0, 0
for lineno, line in enumerate(sys.stdin, 1):
s = line.strip()
if not s or s.startswith("#"):
continue
checked += 1
hits = [w for w in FUZZY if w in s]
if hits and not NUM.search(s): # ① 形容词没有数字兜底
print(f"✗ L{lineno} 模糊词 {hits} 无数字:{s}")
bad += 1
elif PCT.search(s) and not LOAD.search(s): # ② 有分位/平均,却没给负载
print(f"⚠ L{lineno} 缺负载前提:{s}")
print(f"检查 {checked} 条,不可判定 {bad} 条")
sys.exit(1 if bad else 0)
跑一次,输入是这次大促需求的原话,加上已经翻译出来的几行:
cat > reqs.txt <<'EOF'
# 大促需求翻译单(节选)
下单接口要快,P99 <= 300ms @ 8000 QPS,持续 30 分钟
系统在大促期间要稳定,不能崩
峰值 8000 QPS 下,订单服务可用性 99.95%,年停机 <= 4.38 小时
库存服务不可用时降级为预约单,错误率 <= 0.5%,需人工兜底
订单查询 P99 <= 200ms
支持海量订单查询
EOF
python3 req_lint.py < reqs.txt; echo "退出码: $?"
本机跑出来的输出:
✗ L3 模糊词 ['稳'] 无数字:系统在大促期间要稳定,不能崩
⚠ L6 缺负载前提:订单查询 P99 <= 200ms
✗ L7 模糊词 ['海量'] 无数字:支持海量订单查询
检查 6 条,不可判定 2 条
退出码: 1
补上数字与负载前提后重跑:
检查 6 条,不可判定 0 条
退出码: 0
脚本只做一件事:把 “还说不清” 的需求挡在评审之前。它不判断指标是否合理(P99 ≤ 1ms 也能通过),合理性归容量估算与压测,见 57.1 压测方法论。
一次完整翻译:从一句抱怨到四条可验收需求
原话:“大促的时候系统要稳住,别像去年那样崩了,而且下单要快,不能重复扣款。”
| 步 | 动作 | 结果 |
|---|---|---|
| ① 拆句 | 一句话里有三个诉求 + 一次历史事故 | 稳定性 / 延迟 / 幂等 / 事故复盘 |
| ② 归口 | 稳定性与延迟是 NFR,幂等是 FR,事故是约束来源 | 3 条需求 + 1 份历史数据 |
| ③ 追数字 | “稳住” 问出峰值与 9;“崩” 追问去年崩在哪 | 峰值 8000 QPS(去年实测 3200 × 2.5 倍余量)、99.95% |
| ④ 写场景 | 六要素填满 | QA-PERF-01、QA-AVAIL-01 |
| ⑤ 标待定 | 谁确认峰值、降级要不要法务过、跨单元是否强一致 | 3 条 open_items,各有 owner 与截止日 |
形成的四条可验收需求:
| ID | 需求 | 验收方式 |
|---|---|---|
| TR-1042-1 | 峰值 8000 QPS 持续 30 分钟,下单接口 P99 ≤ 300ms,错误率 ≤ 0.1% | k6 压测 + 生产 SLO 双验证 |
| TR-1042-2 | 主库故障 60 秒内自动切主,RPO = 0;切换期间下单降级为预约单,降级错误率 ≤ 0.5% | 混沌演练 + 故障注入 |
| TR-1042-3 | 同一用户同一购物车 5 秒内重复提交,只创建 1 个订单 | 自动化用例 + 生产重复率监控(实现见 25.4 幂等设计与去重) |
| TR-1042-4 | 订单数据不出境(约束,不可谈判) | 部署拓扑核查,不允许出现境外 Region |
第 3 条值得单独看:“不能重复扣款” 是结果,不是需求。翻译要做的是把它变成可判定条件(时间窗 5 秒、范围同一购物车、判定只生成 1 单),否则测试只能靠 “手工点两下试试”。
💡 数字的来源要写清:“8000 QPS” 来自 “去年实测 3200 × 2.5 倍余量”,这是可推导的;写成 “预计 8000” 就成了无根据的猜测。数字的来源与数字本身同等重要。
翻译失败的五个信号
| 信号 | 说明 | 修法 |
|---|---|---|
| 输出里出现具体产品名 | 已经跳到选型 | 删掉产品名,只留指标与边界 |
| 输出里还有形容词 | 没量化 | 逐个过模糊词替换表,追问 “多少 / 什么时候 / 不行怎么办” |
| 有 5 个硬指标但没排序 | 指标之间会打架 | 显式写 “当 A 与 B 冲突时优先 A”,排序依据见 1.3 需求优先级 |
| 待定项为 0 | 几乎不可能,多半是没人敢写 | 逐条问 “这条谁拍板、什么时候定” |
| 业务方看不懂输出 | 翻译过头,语言没换回来 | 每条技术指标补一句业务含义(“P99 300ms = 用户点击后基本感觉不到等待”) |
最后一条是很多架构师栽的地方:翻译的结果要给业务方签字,所以技术语言必须能翻回业务语言。写完指标补一句人话,成本一句,收益是对方真的会看。
反模式
| 反模式 | 后果 |
|---|---|
| 把需求会开成选型会 | 方案先于指标确定,后面所有指标都被方案反向裁剪 |
| 用平均延迟验收 | 平均值达标、P99 崩盘,用户体感差(判据见 34.1 性能指标) |
| 指标不带负载前提 | 5 QPS 下的 200ms 与 5000 QPS 下的 200ms 被当成同一件事 |
| “高可用” 直接翻译成 “上多活” | 跳过 9 的定义,成本翻倍却没解决真问题 |
| 待定项留空或只写 “待确认” | 开发按最省事的方案实现,上线后返工 |
| 翻译单只有 NFR,没有异常分支 | 主流程能跑,分支靠临场发挥 |
| 一条需求塞 6 个指标 | 无法定位瓶颈,也不知道该优化哪个 |
| 业务方没签字 | 上线验收时对 “稳不稳” 各说各话 |
三条铁律
🔑 翻译的产物是判据(能直接翻成压测脚本、监控告警、测试用例),不是方案;输出里出现产品名,就是越界。
🔑 每个指标必须带 负载前提 + 分位 + 窗口;缺一个,验收就只能靠吵。
🔑 待定项必须显式列出并指定 owner;留白的部分,开发一定会用最省事的方式填。
自检
Q1 “下单要快” 要追问哪几个问题,才能变成可验收的指标?
▸ 答案:① 哪个操作、哪个接口(下单创建还是订单查询——制品必须唯一);② 多少并发下的快(8000 QPS 与 8 QPS 是两个系统);③ 看分位还是平均、什么窗口(P99 ≤ 300ms @ 5 分钟滑动窗口);④ 什么环境(峰值还是常态)。四问补齐才是完整场景,缺 “环境” 那一项时,峰值指标会被常态数据验收掉。
Q2 为什么 “不能重复扣款” 不是一条需求?
▸ 答案:它是结果,不是可判定的条件。同一个结果有多种实现(幂等键、去重表、状态机),需求该写的是判定口径:范围(同一用户 + 同一购物车)、时间窗(5 秒内)、判定标准(只创建 1 个订单、只扣 1 次款)。写清这三样,测试才能自动化;只写 “不能重复扣款”,验收只能靠手工点两下。
Q3 需求会上有人问 “我们是不是该上 Kafka”,正确的回应是什么?
▸ 答案:先确认翻译是否完成。上 Kafka 对应的是 “异步解耦 + 至少一次投递” 这类判断,属于 L4 的选择;翻译阶段只该出现 “订单创建后 3 秒内库存服务收到扣减事件,允许重复但消费必须幂等” 这样的判据。若 “多久内、允不允许重复、丢了怎么办” 都没答,讨论产品就是替一个尚未定义的需求做决策。
Q4 翻译单的 open_items 写了 3 条,评审时有人说 “先不写,开发先做起来”。风险是什么?
▸ 答案:留白会被最省事的实现填满,而最省事的实现通常最难改:跨单元一致性留白 → 选了本地事务;降级策略留白 → 直接抛错,大促时用户看到 500。显式写 open_items 的成本是三行字加三个 owner,收益是把 “返工” 换成 “开会拍板”。
Q5 为什么翻译要停在 “层 + 属性”,不落到产品?
▸ 答案:因为产品是可替换的战术选择,需求是不可替换的约束。翻译一旦写到 “用 Redis”,后续讨论全被锁在这条路上,退出成本没人算过;写到 “商品详情 P99 ≤ 50ms @ 3000 QPS”,实现可以是本地缓存、多级缓存或读副本,等设计阶段再按退出成本比较。越早写产品名,越早失去选择权。
关联
- 前置:1.1 功能性需求与非功能性需求、5.2 部署模型:公有 / 私有 / 混合
- 并列:1.3 需求优先级、4.3 技术选型的评估维度
- 下游:25.4 幂等设计与去重、34.1 性能指标、35.1 可用性定义与 9 的换算、57.1 压测方法论
- 待补:79.1 从需求到架构的推导
- 下游:2.4 从业务模型到系统边界