58.5 技术选型流程
一句话
选型流程不是“比参数”,而是在预算与合规约束下,用可验证的证据把不确定性降到可接受,并留下退出路径。
先划边界:维度在 4.3,流程在本节
4.3 技术选型的评估维度 讲的是怎么打分:七个维度、一票否决、权重、POC 验证最贵的假设、可替换性。
本节讲的是怎么把一次选型从想法推进到合同与落地:谁发起、经过哪些门、每步产出什么、谁拍板、怎么退出。前者是“尺子”,后者是“流程”。
先破除四个误区
| 误区 | 现实 |
|---|---|
| “Demo 好用就选它” | Demo 是供应商挑的最好场景;你的场景是长尾、脏数据、异常流量 |
| “只有一家候选” | 单一供应商没有议价权,也没有对照;至少 3 个长名单,哪怕最后只剩 1 个 |
| “POC 跑通就行” | 没有假设与退出标准的 POC,只是“再演示一遍”;跑通不等于在你的规模下可用 |
| “先决定,采购/法务后面补” | 合同、数据处理协议、出口条款谈不下来,技术选型就是空中楼阁 |
七个阶段,每步有产出
| 阶段 | 做什么 | 负责人 | 时长(示例) | 产出 |
|---|---|---|---|---|
| ① 问题与约束 | 要解决什么问题、NFR、预算、合规 | 发起人 + 技术 lead | 1 周 | 一页问题说明 |
| ② 长名单 | 列 3–6 个候选(含“自建/不选”) | 技术 lead | 3–5 天 | 候选清单 |
| ③ 否决项 | 合规、许可证、维护性、数据可导出 | 安全/法务/架构 | 1–3 天 | 否决结论 |
| ④ 短名单 + 评分 | 按 4.3 的维度与权重打分 | 技术 lead + 评审组 | 1 周 | 打分表 |
| ⑤ POC | 只验证最贵的 3 个假设 | 技术 lead | 2–4 周 | POC 报告 |
| ⑥ 决策会 + ADR | 拍板、记录背景/备选/后果 | 决策者 | 1 天 | ADR |
| ⑦ 采购与落地 | 合同、SLA、迁移、回看 | 采购/法务/团队 | 2–6 周 | 合同 + 上线 |
总时长通常 6–12 周;超过 12 周还没决策,通常是“问题没定义清”或“没人敢拍板”,不是技术问题。
角色:谁负责什么
| 角色 | 职责 | 不该做 |
|---|---|---|
| 发起人 | 说清业务问题、预算、时间窗 | 指定技术方案 |
| 技术 lead | 候选、评分、POC、迁移方案 | 绕过安全/法务 |
| 架构 | 与目标架构/路线图对齐(见 58.4 演进路线图) | 替业务定优先级 |
| 安全/合规 | 威胁建模、数据流、合规结论 | 最后一天才出现 |
| 采购 | 商务谈判、合同、SLA | 替技术做技术判断 |
| 法务 | 许可证、数据处理协议、退出条款 | 事后补签 |
| 财务 | 预算、三年 TCO 口径 | 只看首年报价 |
| 决策者 | 在信息充分时拍板并承担结果 | 既不决策也不授权 |
POC:先写“怎么算失败”
| 项 | 要求 | 反例 |
|---|---|---|
| 假设 | ≤3 个,可证伪 | “验证它好不好用” |
| 指标 | 基线 + 目标 + 测量方法 | “性能提升明显” |
| 退出标准 | 达标/未达标分别怎么决策 | 没有预先定义 |
| 时间盒 | 2–4 周,超时即停 | 无限期“再试试” |
| 成本上限 | 人力/资源/云费用上限 | 先花再说 |
| 数据 | 脱敏数据;禁止直接拿生产数据 | 直接连生产库 |
POC 报告只写三件事:哪些假设被验证、哪些被推翻、还有什么没验证。不要写成产品评测。
一个能跑的“选型门禁”
决策会之前先过门:材料不齐,不进会。这样能避免“会上才发现没做安全评审”。
package main
import (
"fmt"
"os"
)
type selection struct {
name string
problem bool // 问题与约束写清楚
candidates int // 进入短名单的候选数
vetoDone bool // 合规/许可证/维护性等否决项已过
pocDone bool
pocChecks int // POC 验证通过的假设数
security bool
legal bool
exitPlan bool // 数据导出/替换路径/合同退出条款
adr bool
budget bool
}
type gate struct {
name, status, detail string
}
func check(s selection) ([]gate, bool) {
var gs []gate
add := func(name string, pass bool, detail string) {
status := "PASS"
if !pass {
status = "BLOCK"
}
gs = append(gs, gate{name, status, detail})
}
add("问题定义", s.problem, "要解决什么问题、约束是什么")
add("候选长名单", s.candidates >= 3, fmt.Sprintf("候选 %d 个(至少 3 个,避免单一供应商)", s.candidates))
add("否决项", s.vetoDone, "合规、许可证、维护性、数据可导出")
add("POC", s.pocDone && s.pocChecks >= 3, fmt.Sprintf("POC 完成=%v,验证假设 %d 个(至少 3)", s.pocDone, s.pocChecks))
add("安全评审", s.security, "威胁建模/数据流/权限")
add("法务与采购", s.legal, "合同、SLA、数据处理协议")
add("退出计划", s.exitPlan, "数据导出、抽象层、替换成本")
add("决策记录", s.adr, "ADR 写清背景、备选、决策、后果")
add("预算", s.budget, "三年 TCO 与采购预算已批")
ok := true
for _, g := range gs {
if g.status == "BLOCK" {
ok = false
}
}
return gs, ok
}
func run(s selection) bool {
gs, ok := check(s)
fmt.Printf("== 选型门禁:%s ==\n", s.name)
for _, g := range gs {
fmt.Printf(" %-6s %-10s %s\n", g.status, g.name, g.detail)
}
if ok {
fmt.Println(" 结论:可以进决策会\n")
} else {
fmt.Println(" 结论:有 BLOCK 项,不能进决策会;补齐或明确接受风险(需书面记录)\n")
}
return ok
}
func main() {
good := selection{
name: "消息队列选型", problem: true, candidates: 4, vetoDone: true,
pocDone: true, pocChecks: 4, security: true, legal: true,
exitPlan: true, adr: true, budget: true,
}
bad := selection{
name: "缓存中间件选型", problem: true, candidates: 1, vetoDone: false,
pocDone: false, pocChecks: 0, security: false, legal: false,
exitPlan: false, adr: false, budget: true,
}
okA := run(good)
okB := run(bad)
fmt.Printf("可进决策会:A=%v,B=%v\n", okA, okB)
if !okA || !okB {
os.Exit(1)
}
}
本机运行结果(退出码 1):
== 选型门禁:消息队列选型 ==
PASS 问题定义 要解决什么问题、约束是什么
PASS 候选长名单 候选 4 个(至少 3 个,避免单一供应商)
PASS 否决项 合规、许可证、维护性、数据可导出
PASS POC POC 完成=true,验证假设 4 个(至少 3)
PASS 安全评审 威胁建模/数据流/权限
PASS 法务与采购 合同、SLA、数据处理协议
PASS 退出计划 数据导出、抽象层、替换成本
PASS 决策记录 ADR 写清背景、备选、决策、后果
PASS 预算 三年 TCO 与采购预算已批
结论:可以进决策会
== 选型门禁:缓存中间件选型 ==
PASS 问题定义 要解决什么问题、约束是什么
BLOCK 候选长名单 候选 1 个(至少 3 个,避免单一供应商)
BLOCK 否决项 合规、许可证、维护性、数据可导出
BLOCK POC POC 完成=false,验证假设 0 个(至少 3)
BLOCK 安全评审 威胁建模/数据流/权限
BLOCK 法务与采购 合同、SLA、数据处理协议
BLOCK 退出计划 数据导出、抽象层、替换成本
BLOCK 决策记录 ADR 写清背景、备选、决策、后果
PASS 预算 三年 TCO 与采购预算已批
结论:有 BLOCK 项,不能进决策会;补齐或明确接受风险(需书面记录)
可进决策会:A=true,B=false
门禁不是官僚:九项里任何一项缺失,决策会都会退化成“听供应商讲 PPT”。特别注意“退出计划”和“法务与采购”这两项——它们最容易被技术团队忽略,也最容易在两年后变成巨额成本。
锁定与退出:选型时就要想好怎么分手
| 维度 | 要确认的事 | 合同/技术上的保障 |
|---|---|---|
| 数据 | 能否全量导出?格式是否标准? | 导出工具 + 定期演练 |
| 协议 | 是否标准协议(SQL/HTTP/S3/Kafka) | 避免私有协议 |
| 抽象 | 业务代码是否依赖厂商 SDK | 适配层/接口隔离 |
| 合同 | 退出条款、数据删除、迁移协助 | 写进合同,不靠口头 |
| 源码 | 是否托管/escrow | 关键系统要求源码托管 |
| 价格 | 涨价机制、最低消费 | 锁定涨幅与期限 |
云厂商锁定与迁移的完整分析见 5.7 云厂商锁定与迁移;成本口径见 4.2 成本-收益分析。
选型落地后还要写清 “什么信号出现就换方案”(止损条件、执行人与复核日),见 4.7 技术风险与止损——没有止损条件的选型,等于把决定权交给了下一次事故。
决策与回看
决策会产出两样东西:
- ADR:背景、候选、评分、决策、后果、退出计划(见 58.1 ADR 实践)。
- 落地计划:迁移步骤、验收指标、回滚方案(重大变更走 58.3 架构评审)。
12 个月回看:当初的假设哪些成立?成本是否超支?故障是否在预期内?如果重来还会选它吗?把结论补进 ADR,作为下次选型的输入。
生产落地:指名 → 可抄 → 可验 → 可退
指名:选型报告用 Markdown/Confluence;评分表按 4.3 的维度;决策落 ADR;采购用公司既有流程(不要绕过)。Make/Buy/Reuse 的判断见 4.4 Make / Buy / Reuse 决策。
可抄(选型报告骨架):
# 消息队列选型报告
## 问题与约束
- 峰值 8k QPS;保留 7 天;P99 < 50ms;预算 30 万/年
## 候选与否决
| 候选 | 否决项 | 结论 |
|---|---|---|
| Kafka | 无 | 进入短名单 |
| RocketMQ | 团队无生产经验 | 保留,风险项 |
| 自建 | 人力不足 | 否决 |
## POC 结论
- 假设 1(8k QPS 下 P99 < 50ms):成立,实测 38ms
- 假设 2(积压 1 亿条可恢复):成立,耗时 22 分钟
- 假设 3(运维人力 < 0.5 人):未验证
## 决策
选 Kafka;代价:需要补监控与值班 runbook;退出计划:标准协议 + 双写迁移。
可验:本节门禁真跑过,输出如上;把它放在决策会邀请前,BLOCK 未清就不排会。人工验证:随机抽一次选型,检查是否有 ≥3 个候选、POC 是否有预设退出标准、合同是否有退出条款。
可退:选错了要能退。技术上留适配层与数据导出;合同上留退出条款与迁移协助;组织上留“12 个月回看”的机制。没有退出路径的选型,本质是一次不可逆赌博。
反模式
| 反模式 | 后果 |
|---|---|
| Demo 驱动选型 | 选的是演示效果,不是生产可用性 |
| 单一候选/内定 | 没有对照与议价,风险无人兜底 |
| POC 没有退出标准 | 跑通了也不知道该不该选 |
| 采购/法务后置 | 合同谈不下来,技术方案作废 |
| 只看首年报价 | 三年 TCO 与人力成本被低估 |
| 没有退出计划 | 被锁定后迁移成本极高 |
| 决策会没有 ADR | 半年后没人记得为什么选它 |
| 选完不复盘 | 同样的坑下次再踩 |
自检
Q1 4.3 已经讲了评估维度,为什么还需要选型流程?
▸ 答案:维度解决“怎么比”,流程解决“谁来比、按什么顺序、每一步产出什么、谁拍板、怎么退出”。没有流程,打分表会被跳过、否决项会被忽略、采购与法务会拖到最后一刻。流程把“技术判断”变成“组织可执行的决策”。
Q2 为什么要求至少 3 个候选,哪怕最后只认真评估 1 个?
▸ 答案:候选的作用是提供对照与议价,并逼团队说清“为什么不选另外两个”。只有一个候选时,评审无法判断是“最优”还是“没得选”,采购也没有议价空间。3 个是下限,不是形式。
Q3 POC 的退出标准应该怎么定?
▸ 答案:在 POC 开始前写死:验证哪 3 个假设、每个假设的指标与测量方法、达标/未达标分别怎么决策、时间盒与成本上限。没有退出标准的 POC 会无限延长,最后变成“再试试”或“感觉还行”。
Q4 为什么“退出计划”要在选型时就定,而不是等以后再说?
▸ 答案:锁定成本随时间上升:数据越多、依赖越深、合同越长,迁移越贵。选型时还有议价权,可以要求标准协议、数据导出工具、退出条款;上线两年后再谈,供应商没有动力配合。退出计划是选型的一部分,不是运维的补救。
Q5 12 个月回看应该看什么?
▸ 答案:看四件事:① 当初的容量/性能假设是否成立;② 三年 TCO 是否超支、超在哪;③ 故障是否在预期内、恢复是否达标;④ 如果重来是否还选它。结论补进 ADR,作为下一次选型的输入,而不是停留在口头。