25 如何定义问题:问题 framing、抽象层次、约束识别
开篇提问
先给你一个真实的故事,你来评判:
19 世纪,伦敦霍乱肆虐。当时的主流理论是”瘴气说”——认为疾病通过”坏空气”传播。医生们致力于”消除臭味”,效果甚微。
John Snow 医生做了一件不同的事:他在地图上标出每一个霍乱死亡病例的位置,发现它们聚集在一口水井周围。他拆掉了那口井的水泵把手,疫情立刻得到控制。
请认真想:Snow 医生做对了什么?他比别人更聪明吗?还是他做了一件”别人没做的事”?
先别急着下结论。我给你三个追问,你顺着想:
- 那些研究”瘴气”的医生,是不是也很聪明、也很努力?他们缺的到底是什么?
- Snow 医生做的”在地图上标点”这件事,技术上难吗?为什么偏偏没人做?
- 如果 Snow 医生也相信”瘴气说”,他会不会也去研究”怎么消除臭味”,而不是”病例分布在哪”?
答案是后者。别人在解决”怎么消除瘴气”,而 Snow 重新定义了问题:“病是从哪来的?“——他把问题从”气味”重新框定为”位置分布”。
这一章的主题,就是这件最值钱、也最少被教的能力:定义问题。
因为一个残酷的事实是:如果问题定义错了,你越努力、解决得越快,错得越离谱。 而在工程领域,绝大多数”解决不了的问题”,其实是”没定义清楚的问题”。
这一章和全书其他章最大的不同:前面 24 章讲的是”别人已经定义好的问题怎么解”,这一章讲的是”你怎么自己定义问题”。 前者是解题能力,后者是出题能力——而真实工作里最难的、最值钱的,恰恰是后者。
子主题一:问题 framing——你问的问题决定了你能找到的答案
先看一个经典的思想实验:
你手里有一把锤子,面前有一堆木板。
- 问”怎么用锤子处理这些木板”→ 你会想把木板砸碎
- 问”怎么用这些木板搭个架子”→ 你会想到连接和支撑
- 问”这堆木板能不能卖钱”→ 你会想到回收
同样一堆木板,你问的问题不同,得到的答案完全不同。
这就是**问题框架(Problem Framing)**的力量:你怎么”框定”一个问题,决定了你能看到什么样的解空间。
在工程里的典型表现
例一:系统慢 → 两种不同的框定
- 框定 A:“怎么让这个查询更快?” → 你会去优化 SQL、加索引
- 框定 B:“系统需要这个查询吗?” → 你会发现它是每分钟跑一次的无效轮询,可以直接去掉
框定 B 的解法,比框定 A 的所有优化都有效。 但只有当你问对了问题,才可能看到它。
例二:需求来了 → 两种不同的框定
- 框定 A:“用户要一个导出按钮” → 你做一个按钮
- 框定 B:“用户为什么需要导出?” → 你会发现他要的是”把数据给财务”,而直接给财务开个只读账号更好
这就是”五个为什么”(5 Whys)的精髓:不断追问”为什么”,直到找到真正的需求,而不是停留在表面的诉求。
一条极其实用的原则
在解决问题之前,先花时间确认”这是不是一个正确的问题”。 在优化之前,先问”这个东西需不需要存在”。
一个帮助你理解 framing 的类比:医生问诊
一个病人说”我头疼”。好医生不会立刻开止痛药,他会问:
- “疼了多久?怎么个疼法?“(定位问题)
- “最近有没有撞到头?有没有熬夜?“(找原因)
- “你希望解决的是’不疼’,还是’找到头疼的根因’?“(澄清目标)
而一个差医生,直接开止痛药——头疼是”症状”,不是”问题”。止痛药只是掩盖了症状,根因还在。
工程师面对需求,和医生面对病人是一模一样的:用户说”我要一个导出按钮”,那是”症状”(或者说,用户自己开的药方),不是”问题”(他真正想解决的是”把数据交给财务”)。 好的工程师像好医生,先诊断,再开药;差的工程师像差医生,用户要什么就做什么。
子主题二:抽象层次——在正确的层次上定义问题
定义问题的第二个关键:你在哪个”抽象层次”上看它。
同一个问题,在不同层次上,是完全不同的问题:
例子:“网站打开慢”
- 硬件层:磁盘 IO 慢、CPU 满了、网络带宽不够
- 系统层:进程调度、内存交换、文件描述符耗尽
- 应用层:算法复杂度高、锁竞争、N+1 查询、频繁 GC
- 架构层:缺少缓存、数据库没索引、服务之间调用链过长
- 产品层:页面元素太多、首屏加载了不必要的资源
- 业务层:用户真的需要打开这个页面吗?能不能换个交互?
看出来了吗?问题的定义,高度依赖你选择的层次。
选错层次的两种典型错误
错误一:层次太低(见木不见林)。
在代码里抠微优化(循环展开、位运算技巧),而真正的问题是”这个接口每分钟被调用一万次但根本不需要”。在错误的层次上优化,收益微乎其微。
错误二:层次太高(空谈无物)。
大谈”架构要重构”、“要用微服务”,而没有定位到具体是哪一行代码慢。高层次的诊断必须能被下钻到具体证据,否则就是空话。
怎么选对层次?一个实用的方法:自顶向下排除法
- 先在最高层次问:“这事儿该不该做?“(能不能直接删掉/绕过)
- 然后到架构层:“路径合理吗?“(有没有明显的多余环节)
- 再到应用层:“实现高效吗?“(算法、查询、锁)
- 最后到系统/硬件层:“资源够吗?”
大多数时候,问题在前两层就被解决了。 只有在前面都排除了,才值得往下深挖。
一个帮助你理解”抽象层次”的类比:找漏水
你家的水费突然暴涨。你可能在几个层次上找原因:
- 最高层次(该不该):那个一直没人用的旧花洒,是不是一直在滴水?直接拧紧/拆掉。
- 中间层次(路径):水管走的路线合理吗?有没有明显漏水的接头?
- 底层(实现):水表坏了吗?水压正常吗?
聪明的做法是先检查”最可能、最便宜”的层次(旧花洒是不是漏水),而不是一上来就撬地板找水管。 但如果你直接去研究”水分子在管道里的流体动力学”(层次太低),那就完全跑偏了——问题的元凶是那个滴水的花洒。
子主题三:约束识别——问题藏在不变量里
定义问题的第三个关键技巧:找出约束(Constraints),尤其是那些”隐藏的假设”。
很多问题之所以”无解”,是因为你默认接受了一些其实可以被打破的约束。
经典案例:快递分拣
- 表面问题:“怎么让分拣员更快地分拣包裹?”
- 隐藏约束(默认假设):“包裹必须经过分拣中心”
- 打破约束:如果直接”把包裹从 A 城送到 B 城,不经过中心”呢?→ 这就是快递网络的”直达线路”设计
另一类经典:约束是可以被”转移”的(呼应第 1 章)
- 问题:“这个计算太慢,用户要等”
- 约束:“必须在用户请求时算完”
- 打破约束:能不能提前算好?(预计算 / 离线批处理)
- 或者:能不能先返回近似结果,再异步精算?
识别约束的具体方法
方法一:列出所有”必须”,然后逐条质疑。
“必须实时吗?必须精确吗?必须持久吗?必须一致吗?必须现在做吗?”
每一个”必须”,都是一个可能的突破口。
方法二:问”如果把这个约束拿掉,会怎样?”
如果拿掉”必须实时”,你能做什么?如果拿掉”必须精确”呢(第 5 章)?如果拿掉”必须保存所有历史数据”呢?
方法三:找出”真正的硬约束” vs “我们自己加的约束”。
- 硬约束:物理定律、光速、热力学、信息论下界(真·不可突破)
- 软约束:历史包袱、组织壁垒、“行业惯例”、我们自己的假设(可以突破)
绝大多数所谓的”不可能”,其实是软约束。 而区分硬软约束的能力,是高手和普通人的分水岭。
一个帮助你理解”约束”的类比:囚犯的栏杆
一个囚犯被关在牢房里,他每天看到的是铁栏杆,于是认定”我被困住了”。
但他从没想过:栏杆之间是有空隙的,隔壁的牢房根本没锁门,甚至——有些栏杆是松的,能掰开。
“被困住”这个判断,隐含了一个假设:“栏杆不可逾越”。 但这个假设,一部分是真的(硬约束:墙壁是混凝土),一部分是假的(软约束:那把没锁的门)。
高手和普通人的区别,就是能不能分清”混凝土墙”和”没锁的门”。 大多数你以为的”不可能”,其实是那扇没锁的门。
子主题四:怎么”敏锐”地发现问题——四个可训练的习惯
你可能会问:“敏锐”是天赋吗?大部分不是,它是可以被训练的习惯。 四个具体习惯:
习惯一:对”反常”保持敏感,而不是习惯化
高手和低手最大的区别之一:低手对异常”习惯了”,高手对异常”不舒服”。
- “这个接口有时候慢一点,正常的” → 高手会问”为什么有时候”
- “这个日志报错但重试就好了” → 高手会问”为什么需要重试”
- “这个定时任务偶尔失败” → 高手会问”失败的规律是什么”
训练方法:当你说”这个……是正常的”时,强制自己追问一句”为什么它正常”。
习惯二:问”为什么”连问五次(5 Whys)
丰田生产方式的经典方法。不断追问”为什么”,直到找到根因:
- “机器停了” → 为什么?“保险丝断了”
- 为什么断?“轴承润滑不足”
- 为什么不足?“油泵工作不正常”
- 为什么不正常?“油泵进了一个金属碎屑”
- 为什么进碎屑?“没有安装过滤网”
真正的根因(没有过滤网)和表面原因(保险丝断了)相隔五层。 只换保险丝,问题会反复发生。
习惯三:量化,而不是感觉
“这个有点慢” → “P99 是 800ms,目标是 200ms” “最近错误变多了” → “错误率从 0.1% 涨到 0.8%,主要集中在 /api/pay”
量化的价值不只是”更精确”,更重要的是:量化会逼你发现”你以为的”和”实际的”之间的差距。 很多”显而易见”的问题,一量化就发现方向错了。
习惯四:主动制造”对比”
敏锐往往来自”反差”。主动创造对比:
- 对比”改之前”和”改之后”(A/B 测试)
- 对比”正常时”和”异常时”(故障时的 profile vs 正常时的 profile)
- 对比”这个服务”和”那个服务”(为什么同样的代码,一个快一个慢)
- 对比”我们的做法”和”别人的做法”
没有对比,就没有洞察。 这是为什么”基准测试”和”可观测性”如此重要——它们本质上都是在制造对比。
一个帮助你理解”敏锐”的类比:老司机听引擎
为什么老司机能听出发动机的异响,而新手听不出来?
- 新手:引擎一直”嗡嗡嗡”,他觉得”很正常”(习惯化了)。
- 老司机:引擎里混进了一丝”咔咔咔”,他立刻警觉”不对劲”。
差别不在于听力,而在于”对’正常’的基准有多清晰”。 老司机听过几千次”正常的嗡嗡嗡”,所以任何偏离这个基准的声音,都像黑夜里的一盏灯一样显眼。
这就是”敏锐”的本质:先建立清晰的”正常”基准,然后对任何偏离保持警觉。 而”量化”和”对比”,就是在帮你建立和校准这个基准。
子主题五:问题定义的几个经典模式
最后,给你几个”经过时间检验”的问题定义模式,它们能帮你快速框定陌生问题:
模式一:这是”权衡”问题还是”缺陷”问题?
- 权衡问题:本质是资源分配的取舍(要不要更多机器?要不要更实时?)→ 解法是”找到合适的平衡点”
- 缺陷问题:本质是做错了(bug、设计错误)→ 解法是”修好它”
- 混淆两者的代价:把缺陷当权衡(“这个慢是正常的,因为要一致性”——其实只是查询写错了),会把 bug 合理化;把权衡当缺陷(拼命优化一个其实由物理限制决定的延迟),会白费力气
模式二:这是”局部”问题还是”系统”问题?
- 局部:某个模块慢 → 优化它
- 系统:架构层面不匹配(比如用 OLTP 数据库做 OLAP 分析)→ 需要架构调整
- 判断方法:如果这个局部问题反复出现,它多半是系统问题的一个症状。
模式三:这是”新问题”还是”老问题换了皮”?
- 大部分”新问题”其实是老问题的变体——认出它属于哪一类,你就能立刻缩小解空间
- 这就是为什么本书按”问题”而不是”技术”组织:训练你认出”这其实是 XX 问题的变体”
模式四:问题的”边界”在哪?
- 明确”什么不算这个问题的一部分”
- 划清边界,往往能让复杂问题突然变简单
这些模式的价值
这几个模式,本质上是一套”问题分类的决策树”:当你面对一个陌生问题时,先问”它是权衡还是缺陷?局部还是系统?新问题还是老问题?边界在哪?“——问完这几个问题,你往往就能把它”归类”,而归类的过程,本身就是”定义问题”的过程。
演进脉络
把”问题定义”这件事的演进串起来,你会看到一条”从经验到方法论”的主线。
- 古代:问题解决主要靠经验和试错。
- 20 世纪初:科学方法论和系统化的问题解决(杜威的”反省思维”)。
- 1940s-60s:系统工程、运筹学;丰田的”五个为什么”;TRIZ(发明问题解决理论)在苏联诞生。
- 1960s-70s:设计方法论运动(Alexander 的《形式综合论》、Simon 的《人工科学》);问题空间与解空间的概念。
- 1980s-2000s:设计思维(Design Thinking)在 IDEO 等公司发展;“问题定义”被明确为创新的核心环节。
- 现代:精益创业(Lean Startup)把”问题定义”变成可验证的假设;数据驱动让”发现问题”从直觉走向量化。
主线:对”问题定义”的重视,是随着系统复杂度增长而增长的。当系统简单时,问题显而易见;当系统复杂到超出个人理解时,“定义清楚问题”反而成了最难、也最关键的环节。
这一章讲的所有技巧,本质上都是在回答一件事:如何在信息不完整、认知有偏差的情况下,尽可能准确地定位”真正的问题是什么”。
一个贯穿全书的”元观察”
这一章是全书第六篇(元能力篇)的第一章,也是统领全书的章节。为什么?
因为前面 24 章讲的每一个”问题”——缓存一致性、共识、并发、排队、压缩——都是”已经被定义好的问题”。你学这些章节,学的是”别人已经把问题定义清楚了,你来解题”。
但真实工作里,你面对的大多数情况是:问题还没被定义。你不知道该优化什么、瓶颈在哪、甚至不知道自己面对的是不是真问题。
这就是为什么”定义问题”比”解决问题”更值钱:解决问题的人,在别人定义好的框架里做增量;定义问题的人,能换掉整个框架。 而本书第 28 章复盘的那些”经典突破”(B 树、MapReduce、TCP 拥塞控制),几乎全都是”重新定义了问题”的产物。
本章思考题
【思考题】
-
John Snow 医生做对了什么?“问题 framing”为什么比”解决问题”更重要?
-
什么叫”在正确的抽象层次上定义问题”?层次太低和太高分别有什么问题?“自顶向下排除法”怎么做?
-
“列出所有必须,然后逐条质疑”——请举一个你见过的、通过质疑”隐藏约束”而找到突破口的例子。硬约束和软约束的区别是什么?
-
“敏锐”能被训练吗?请说出四个可训练的习惯,并用”老司机听引擎”解释”敏锐”的本质。
-
“权衡问题”和”缺陷问题”有什么区别?混淆两者分别有什么代价?
【参考答案】
-
Snow 做对的是重新定义问题:别人在解决”怎么消除瘴气”(接受了错误的框架),他问的是”病从哪来”,把问题从”气味”重新框定为”位置分布”,用地图标出病例聚集在水井周围,拆掉水泵把手即控制疫情。问题 framing 比解决更重要,因为框定决定了你看到的解空间——如果问题定义错了,越努力解决、错得越离谱;而正确框定后,解法可能简单得令人意外(拆个把手 vs 研究除臭技术)。类比医生问诊:用户要”导出按钮”是”症状”(或用户自己开的药方),不是”问题”,好的工程师先诊断再开药。
-
在正确层次意味着:既不在代码层抠微优化而忽略”这功能根本不需要”,也不空谈”架构重构”而无具体证据。层次太低(见木不见林):做循环展开等微优化,而真正问题是”每分钟无谓调用一万次”,收益微乎其微。层次太高(空谈无物):大谈微服务重构却定位不到哪行代码慢,高层次诊断必须能下钻到具体证据否则是空话。自顶向下排除法:先问”该不该做”(能否删掉/绕过),再问”路径合理吗”(有无多余环节),再到”实现高效吗”(算法/查询/锁),最后才看”资源够吗”——多数问题在前两层就解决了(类比找漏水:先看旧花洒是否滴水,而不是一上来撬地板)。
-
(开放题,参考方向)典型例子:认为”必须实时计算”→ 质疑后改为预计算/离线批处理;认为”必须精确统计”→ 改用概率数据结构(Bloom Filter/HyperLogLog)换数量级收益;认为”必须强一致”→ 改为最终一致+幂等+对账;认为”数据必须经过中心节点”→ 改为点对点直达。硬约束 vs 软约束:硬约束是物理定律、光速、热力学、信息论下界(真不可突破);软约束是历史包袱、组织壁垒、行业惯例、自己的假设(可突破)。绝大多数”不可能”其实是软约束(类比囚犯:混凝土墙是硬约束,没锁的门是软约束,多数人把没锁的门也当成墙)。
-
能。四个习惯:一、对”反常”保持敏感而非习惯化(说”这是正常的”时强制追问”为什么正常”);二、连问五个为什么(5 Whys)直到根因(如保险丝断→没装过滤网,相差五层);三、量化而非感觉(“有点慢”→“P99 800ms vs 目标 200ms”,量化会暴露”你以为的”和”实际的”的差距);四、主动制造对比(A/B、正常 vs 异常、我们 vs 别人),没有对比就没有洞察。老司机听引擎:敏锐的本质不是听力,而是”对’正常’基准的清晰度”——老司机听过几千次正常嗡鸣,任何偏离都像黑夜里一盏灯;量化与对比就是在建立和校准这个基准。
-
权衡问题是资源分配的取舍(要不要更多机器、要不要更实时),解法是找平衡点;缺陷问题是做错了(bug、设计错误),解法是修好它。混淆的代价:把缺陷当权衡(“这个慢是正常的,因为要一致性”——其实只是查询写错了)会把 bug 合理化,永远不修;把权衡当缺陷(拼命优化一个由物理限制决定的延迟)会白费力气,在不可能的方向上空耗。判断的关键是追问”这是不是物理/本质限制,还是只是实现没做好”。