☰
写代码前先问5个为什么:从数据库崩溃到研发流程优化
2026/10/6 10:27:37 网站建设 项目流程

那次事故发生在周四下午两点十七分,我刚从食堂回到工位,手机上的告警群就开始疯狂震动。支付订单表的数据积压、接口响应时间直线飙升、数据库连接数被打满,紧接着就是一连串的宕机提示。等我们手忙脚乱地重启服务、清理会话、恢复数据之后,已经是晚上九点多了。整个下午,用户在下单时要么转圈圈,要么直接提示系统繁忙,客服那边被打爆了电话,运营同事急得直接冲到技术区找人对线。

事后复盘时,我们发现引起崩溃的代码其实很“简单”:某个同事在给订单查询加过滤条件时,直接对一张千万级数据的表做了一个非索引字段的模糊匹配。按照常规思路,开发修复Bug、上线补丁、写个复盘报告,这事就算翻篇了。但那天晚上,团队里的老张提出了一个非常尖锐的问题:”我们为什么要等到线上出问题了,才去翻这段代码?为什么这段代码能通过评审?”

于是就有了后来的那条铁规:写代码前,先问5个“为什么”。

1. 事故复盘:从“数据库打满”到“需求没对齐”

1.1 表面故障与深层根因的距离

复盘会上,我们用了5 Why分析法一层层往下挖。第一层,系统为什么崩溃?因为数据库连接池被打满。第二层,连接池为什么被打满?因为新上线的接口里有个慢查询,平均耗时接近11秒,请求全部卡在数据库上。第三层,为什么会出现这种慢查询?因为对存储过程生成的时间戳字段做了字符串开头的模糊匹配,索引完全失效。第四层,为什么这个SQL能写出来?因为开发同学没有查看执行计划,没有评估数据量级。第五层,为什么他没有评估数据量级?因为需求里只说了“查询用户最近产生的异常订单”,没有明确数量级和性能要求,他按平时几千行的小表思路去写了。

这轮问答走完,我们发现“问题代码”只是冰山一角。深藏在水面下的,是需求评审流于形式、代码评审只看逻辑不看性能、开发没有SQL审查意识、测试环境数据量太小根本没暴露风险。真正引发系统崩溃的,不是某一个开发者的手滑,而是一整条研发链条上的多个环节同时失守。

我把那次复盘的过程整理成了一个表格,贴在团队内部文档库里,每次新人入职都先看一遍。表格长这样:

层数追问问题得到的回答
1 Why系统为什么崩溃?数据库连接池被打满,服务无法响应新请求
2 Why连接池为什么被打满?慢查询把连接长期占住,请求全堵在SQL上
3 Why为什么会有这个慢查询?对无索引字段做前缀模糊匹配,全表扫描
4 Why为什么这SQL能上线?代码评审没人看执行计划,开发也没自查
5 Why为什么没自查?需求只有功能描述,没有任何性能指标

抛开那张表格里冷冰冰的因果链,那次复盘真正让我警觉的,是“人”这一层的状态。那个写问题代码的同事不是新手,他之前负责过好几个核心模块,属于“靠谱”那一类。但那天他说了一句话:“我以为这个接口是内部监控用的,一天没几个请求。”这句话暴露了一个本质问题:他在动手写代码时,脑子里的场景假设和实际生产环境的场景假设完全不是一回事。如果他在写第一行代码之前,先问自己一句”这个接口会被怎么调用、多久调用一次、最多返回多少条数据”,那个慢查询压根不会出生。

1.2 5 Why 不是“连问五句为什么”那么简单

很多文章讲5 Why,举的例子都是“杰斐逊纪念堂墙面为什么风化”那种经典案例。一到实操中,最常见的误区就是把它当成“连问五个为什么”的机械练习,问到最后得到的答案全靠编。我在那次复盘里最大的感受是:5 Why的有效性,取决于每追问一层时能不能拿到真实的数据和证据,而不是坐在一起凭感觉瞎推。

比如复盘时有人说到“连接池被打满”,立刻就有人跳出来说“应该把连接池从100调到200”。如果停在那一层,我们最多算是打了个补丁。但问题是“为什么打到100就被打满了,而不是正常波动?”,于是我们去看监控曲线,发现打满前的15分钟,那个慢查询就已经开始拖垮其他接口了。每往下一层,都要带着“证据”去问,而不是带着“解决方案”去问。

那次之后,我们还给5 Why加了一条配套规则:写代码之前问的为什么,必须有对应的事实支撑,不能用“我猜”“我觉得”来回答。这个要求听着简单,做起来很难,因为人在被追问的时候,本能反应就是快点给个台阶下。如果你不把“凭据”作为硬条件,五个为什么问下来,很可能只得到一个“因为运气不好”式的结论。

2. 写代码前的五个“为什么”:一条可直接抄走的开工自检清单

2.1 第一问:这个功能为什么存在

动手写代码之前,第一件事不是打开IDE建文件,而是想清楚这个功能为什么会出现。它来自哪个需求?是用户反馈的痛点、业务方的规划、还是某次数据异常带来的补救措施?这决定了代码的生命力和气质。

有一次我们做一个订单导出功能,需求文档写得特别简单:“支持按时间范围导出订单明细”。开发同学拿到就开干了,做出来一个联调界面,但因为没有考虑“为什么要导出”“导出的数据去做什么”,导致交付后才发现业务方是要给财务做对账用的,需要包含支付流水号、优惠分摊金额、税费明细。这些字段在原始订单表里都有,但导出模板里根本没放。改模板、重新测试、再次发布,一来一回又折腾了两天。

所以第一问的实操方法是:把需求文档里的“做什么”翻译成“为什么做”,然后写出这个功能上线后的预期使用者画像和使用频率。如果写不出这三行字,说明需求还没想清楚,先别碰键盘。

2.2 第二问:这个功能为什么用这种方案实现

需求明确之后,第二个“为什么”是问技术选型的。同一个功能,可以用同步接口、异步消息、定时任务、事件驱动等多种方案实现。为什么要选当前这一种?是基于团队技术栈的延续性、性能要求,还是因为某个约定俗成?

举个例子,用户上传头像后要生成三种尺寸的缩略图。常规做法是同步处理,上传接口直接返回处理结果。但如果用户量上来了,同步处理会导致上传接口响应很慢。这时候改成异步任务加对象存储回调,体验会好很多。如果没问“为什么用同步方案”,上线后被流量一压,马上就露怯。

这个“为什么”还包含另一层含义:为什么不用现成的公共组件,而选择自己写?很多系统崩溃的隐患,就是开发觉得“自己写也不复杂”,于是绕过公司内部的公共库,单独造了一个轮子。轮子的逻辑没测试全、没有负载保护、没有降级方案,平时验不出问题,一到高峰就出事。

2.3 第三问:为什么现在做、现在写是对的时机

第三个“为什么”是关于节奏的。这个需求为什么要在当前迭代做?是因为业务优先级确实高,还是因为开发手上刚好空出来?当前的基础设施、依赖服务、数据准备是否已经就绪?

我见过最典型的反面案例是:新功能依赖的数据表还没来得及做数据清洗,开发已经按“数据可用”的前提把代码写完了。联调时发现数据缺得离谱,代码里加了一堆判空和默认值兜底,导致业务逻辑被各种特殊分支绕晕,最后变成一个谁也动不了的“屎山”。

如果动手前先问一句“现在做这个,前置条件都齐了吗”,很多返工是可以直接避免的。这个为什么的潜台词,是让开发者对“项目节奏”保持敏感,而不是被动地跟着排期走。

2.4 第四问:为什么这个改动只影响A而不影响B

没有局部思维,是很多生产故障的源头。代码从来不是孤岛,你改的每一个字段、每一条SQL、每一个接口签名,都可能被其他模块依赖。第四问要求你在动手前,把改动链路完整地走一遍:它影响哪些上游服务、哪些下游服务、哪些数据表、哪些定时任务。

我统计过团队近半年线上的P2级以上事故,超过60%都跟“改了一处,其他模块没感知”有关。最常见的场景是:有人给某个接口增加了一个必填参数,自己内部调用的地方全部改好了,但没排查其他系统是否也在调用这个接口。等别家系统凌晨跑批时报错,排查到凌晨三点,才发现是接口签名变了。

为了避免这种情况,我们现在的做法是:写代码前必须查调用链,把“谁在调我、我在调谁”梳理出来。如果有跨系统调用,还得走一遍接口文档,确认字段变更的兼容方案。这一步放在写代码之前,成本极低,收益却非常可观。

2.5 第五问:为什么我不能花更少的代价验证这个改动

第五个“为什么”是问验证成本和可测试性的。你打算怎么证明这段代码是对的?靠编译通过、靠本地自测,还是靠上线后等用户反馈?很多开发在写代码前根本没想过这个问题,写完就提测,测出问题再改,改完再看影响面,等于把验证成本全部堆在测试和线上。

更好的做法是:动手前想清楚可测试性的方案。这个函数能不能写单元测试?这个接口要不要输出日志方便联调?这次改动对已有功能的影响,是否可以通过对比测试来验证?如果代码写完后很难验证,那说明设计本身有问题,应该先回到第二问去调整方案,而不是硬着头皮往下写。

上面五个问题如果单独拿出来看,每个都很简单。但把它们串在一起,作为“写代码前的固定仪式”,就会产生完全不同的效果。它逼着开发者在键盘落地前,先把脑子里的思路拎清楚。

3. 5 Why 融入研发流程的落地方式

3.1 需求评审加入“根因预判”环节

先说需求侧。过去我们开需求评审会,前端、后端、测试、产品坐在一起,主要听产品经理讲完了“要做什么”,然后各自领任务就散会了。系统崩溃之后,我们给评审会加了一个固定环节:每个需求,开发要用5 Why的方式做一次“根因预判”。

产品讲完需求后,开发轮流追问:“这个需求为什么存在?”“为什么不在上个迭代做?”“用户为什么需要这个功能?”“如果这个功能不做,最坏情况是什么?”听起来像是在找茬,但实际上是从各个角度把需求的边界摸清楚。很多不成熟的需求,在这种追问下会自己暴露问题。比如有次评审会上,产品提出“用户签到页要展示广告”,我们连问了几个为什么,最后发现这个需求其实是为了提升广告位点击率,而不是为了用户体验。基于这个根因,我们把方案改成了“在签到结果页展示相关度更高的推荐内容”,点击率目标反而实现了。

这个过程非常有意思:如果不做5 Why,团队就停留在“接需求、排期、开发”的流水线上,没人对需求的“合理性”负责。一旦开始追问根因,很多表面的伪需求就会自动坍塌,团队的研发精力自然就聚焦到真正重要的事情上了。

3.2 代码评审变成“答辩会”,而不是“走过场”

代码评审是质量门禁,但大多数团队的评审是“看有没有低级错误”,而不是“看设计是否经得起推敲”。我们把5 Why引入评审后,评审被改成了答辩形式:提交代码的同学要面对评审人提出的追问,比如“你这个循环为什么要放这里?”、“这个缓存为什么要设置2分钟的过期时间?”、“为什么不用布隆过滤器而是直接用Set?”

刚开始,开发们很不适应,觉得这是在“刁难人”。但执行了几周后,效果很明显:提交上来的代码质量提升了一大截,因为每个人都会在提测之前,自己先模拟一遍“如果评审问这个,我该怎么说”。很多隐蔽的问题,在“预答辩”的阶段就被自己发现并修掉了。

为了让评审有效,我们还定了一条硬性约定:如果代码作者答不上来“为什么这样写”,默认代码不合格,打回重新补充设计说明。这条规则看似强硬,但一次运行下来,团队里没一个人提出反对。因为大家心里都清楚,写代码时敷衍一个“为什么”,上线后就要花十倍的代价去填坑。

3.3 复盘会从“追责”转向“系统性根因”

系统崩溃后的那次复盘,是我们团队复盘文化的一个分水岭。以前复盘,语气是“谁的锅”,最后的结论往往是“某某需要注意”。这样的复盘不光让人抵触,而且没有任何建设性。引入5 Why之后,复盘会变成了“共同挖矿”:每个与会者都可以提问,每个回答都要提供证据,大家的目标不是找一个人来背锅,而是把链条上的每一个断点都找出来。

从那以后,我们复盘时有一个不成立的规定:拿到“人粗心大意”这一层必须继续往下挖,因为“粗心”不是根因,只是表象。继续问下去,往往会发现问题出在流程、工具、沟通机制或者环境约束上。把系统性根因挖出来之后,对应的改进措施才能落地。比如我们在一次复盘中发现,某个模块频繁出Bug是因为这个模块的前任维护者离职后,没有留下任何设计文档。于是我们立了一条规矩:核心模块必须补文档,没有文档的情况下,修改代码前必须花时间“考古”,先搞懂旧逻辑为什么存在,再谈改动。

4. 常见问题与避坑心得

4.1 五个问不下去怎么办

实操中,5 Why最常见的卡住场景是“问了两层就没人能回答了”。技术团队的知识分布是不均匀的,负责需求的同学并不知道数据库索引的原理,后端的同学也不一定清楚前端的渲染机制。如果你问的人刚好回答不了,场面就会一下子冷下来。

我的处理方法是:把“5 Why”改成“5 Why+协同问答”——问不下去的那一层,不要逼当事人在会上硬想,而是把这个“为什么”分摊给所有与会者,谁了解这块就由谁来回答。如果所有人都答不上来,那就把它标记为一个“知识盲区”,后续安排专项调研。这样做不会卡节奏,还能顺便把团队的隐性知识盲区暴露出来。

4.2 问出“甩锅链条”或“虚假归因”怎么办

5 Why用起来还有一个常见的坑:问到最后变成了“甩锅链条”。比如“为什么没发现问题?因为测试没测出来;为什么测试没测出来?因为测试用例没覆盖;为什么测试用例没覆盖?因为测试资源不够”。看起来很合理,但每一层都在指认别人,没有一层的答案是落在自己能改的动作上。

遇到这种情况,我习惯加一个约束:每一层的答案,必须落在“可执行的改进动作”上。比如“测试用例没覆盖”对应的可执行动作是“新增接口性能测试的门禁卡点”,那这个答案就是有效的。如果答案是“因为测试不认真”,那就不能停在这一层,还是要继续往下挖,直到挖到一个“明天就可以做”的具体事项为止。

虚假归因也很常见。比如“为什么数据库响应慢?因为磁盘IO瓶颈”,但事实上响应慢是因为SQL扫描行数过大。如果第一层归因就错了,后面的四个Why只能越走越偏。所以每层回答都必须有监控数据、日志或者代码来佐证,这就回到了复盘会上“没有证据不推进”的原则。

4.3 5 Why 是否适合所有“写代码之前”的场景

有人会问,如果每个功能都要问5个为什么,那效率从哪里来?我的回答是:5 Why不是让你每次写代码都花一小时做头脑风暴,而是要形成一种快速的“思维条件反射”。

熟练之后,面对简单需求,你可能几十秒就把五个问题在脑子里过了一遍:“这个功能为什么存在?因为用户要导出报表。为什么用同步导出?因为数据量小、操作简单。为什么现在做?因为本周迭代刚好有空档。为什么只影响报表模块?因为查过调用链了。为什么用现在的验证方案?因为导出文件可以用自动化脚本比对。”这五个问题已经保底信息足够,就不需要写任何纸质文档了。如果是复杂需求,再把这些问法落实到文档里,逐条展开。

所以5 Why其实是一把“标尺”:它帮你快速判断一个需求是大是小、是安全还是危险。越依赖“问过这个为什么”的团队,对复杂任务的判断越敏锐;反之,团队写代码就像蒙眼走路,走到哪倒在哪。

4.4 与AI辅助写代码的关系:为什么变成了新常态下的必修课

这几年团队里越来越多同学在用AI辅助写代码,热搜里也常见“AI写代码最强”“AI写代码规则设定”之类的讨论。很多人以为AI来了,写代码的门槛变低了,但我的实际感受恰好相反:AI写得越快,写代码前问“为什么”就越重要。

AI可以帮你很快生成一段CRUD代码,也能帮你补一个函数、写一条SQL,但它不会主动替你想清楚“这个接口为什么会在这张表上做模糊查询”、“这个字段的分布情况会不会导致慢查询”,更不会告诉你这个功能为什么存在、为什么用这种方案落地。如果你没有在指令里把“为什么”表达清楚,AI生成的东西大概率是“代码正确但上下文缺失”的半成品。

我团队里的用法是:让AI写代码之前,先让它基于需求生成一份“问题清单”,把我们技术方案涉及的关键决策点全部列出来。比如“这张表的数据量预期是多少?查询条件是否需要避免全表扫描?同步还是异步?是否需要幂等?”然后把这些问题在提示词里说清楚,AI才能产出真正有质量的东西。这也是为什么现在大家聊“提示词工程”聊得特别多:提示词的本质,其实就是把你心里的那五个“为什么”翻译给AI听。

5. 后续的扩展思考:从“写代码前”扩展到“整个研发链路”

铁规如果只停留在“写代码前”,作用仍然有限。我们团队在执行了一个季度之后,把5 Why的思路进一步扩展到了整个研发链路。

在需求阶段,用5 Why判断真伪需求和优先级;在技术设计阶段,用5 Why审视方案取舍和潜在风险;在编码阶段,用5 Why做开工自检;在Code Review阶段,用5 Why做答辩;在测试阶段,用5 Why评估测试用例设计是否覆盖到了使用场景;在上线阶段,用5 Why制定回滚和监控方案。

最明显的变化是:团队每天的“突发救火”变少了。以前几乎每周都要为线上问题焦头烂额,后来变成了一个月可能才一两次。这个数字变化的背后,不是因为某一个人的技术能力突飞猛进,而是“想清楚再做”变成了一种集体习惯。

落到个人层面,我现在无论是看代码还是写代码,都已经离不开那几个“为什么”了。被领导质问的时候能用上它,评审别人的代码时能用上它,指导新人时能用上它,就连自己在午休前快速浏览一段老代码时,也会下意识地在心里问一句:这段代码当初为什么要这样写?如果答不上来,我就知道自己该去翻历史记录了。

最后再分享一张我们贴在工位旁边的小卡片。“先问5个为什么,再写第一行代码”——这个习惯不一定保证你永远不犯错,但能保证你犯的每一个错,都是“有的放矢”的错,事后都有复盘的价值。我还记得那次系统崩溃复盘结束时,老张说了一句话:“今天我们花四个小时问为什么,是为了以后不再花一整个晚上填坑。”这句话我一直记到现在,也一直在用。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询