故障排除:永远不会过时的技能
我在多个领域所做的大部分工作,都可以归结为一项技能:故障排除。
我将故障排除定义为系统地确定系统中不良行为的原因并修复它。 故障排除通常是在明确学习"技能"的过程中潜移默化地学习的。故障排除很少被作为一项技能本身来讨论。但有效进行故障排除的许多方法是领域无关的。 意识到我花在故障排除上的时间比构建或执行上的时间更多,并且故障排除技能可以与应用它的领域分开磨练,我决定尝试找出如何提高我的故障排除技能 - 从而提高我在多个领域的效率。
我做故障排除的方式,主要归结为抓耳挠腮,用谷歌搜索错误信息,以及想出并测试假设以缩小搜索范围。但我经常发现自己犯了以前犯过的错误。所以当我进行故障排除时,我会尝试记住以下几点,以保持在正轨上并避免死胡同。
步骤1:退后一步
故障排除需要一定的心态。据我所知,它需要对系统底层结构的兴趣 - 用皮尔斯的话说,"经典"思维 - 耐心,对细节的关注,以及坚韧不拔。
有时,即使在赶时间的情况下,缓慢、深思熟虑、冥想式地进行故障排除也更有效。
很容易陷入被动的问题处理中,而没有停下来思考:这个问题的真正原因是什么?这里到底发生了什么?
故障排除者是系统的一部分。因此,会有探针效应和传说中的海森堡bug。

确保你在调整正确的弦
任何弹过一段时间吉他的人都知道那种切身的感觉:意识到他们正在转动的是与他们正在拨弄的不同弦的调音器。难怪没有任何帮助!
当试图修复一个系统时,在开始工作之前,我会做一些保证会有效果的事情。
如果我认为我知道要切断哪根电线,我会先拉一拉它,确保它在另一端移动。
当我在排查CSS bug时,我通常会先设置 * {color: red !important;},这样我就知道我写的代码在正确的文件中,并且实际正在运行!
确定流程
很容易花很多时间尝试"修复"问题。但那是简单的部分。困难的部分是理解系统,然后隔离并理解问题。
我通常从"东西" - 电力、水、汽油、空气、力、数据、污水,或其他任何东西 - 如何流经系统,并在此过程中转化为不同的东西开始。
输入、输出和转换是什么?流经系统的不同类型的东西可以被分组为半独立的子系统吗?
在电气系统中,物理追踪布线通常很有帮助。在机械系统中,同样适用于输送液体或气体的管道,或控制电缆、凸轮、齿轮、链条和其他传导机械力的物品。在软件中,追踪数据。在社会动态中 - 祝你好运!

观察症状
应该发生什么,实际发生了什么,以及两者在哪里分歧?
如果可能,我会根据症状缩小哪些子系统受到影响。如果我的汽车刹车灯不工作,问题可能是电气问题;如果汽车下面有油迹,可能不是电气问题。如果发动机无法启动,可能两种情况都有可能,需要更多调查。
我理解系统的信念往往是故障排除的障碍。即使我"对系统了如指掌",我也不太可能完全理解它。即使是我构建的系统也由我没有构建的系统组成;即使看似简单的系统也无限复杂。(正如卡尔·萨根所说:"如果你想从头开始做一个苹果派,你必须首先发明宇宙。")
隔离问题
下一步是找出子系统在哪个步骤失败。
我的基本方法:在系统上"做科学"。
-
对问题形成假设。这可以是来自症状的直觉第一印象,或来自扩展观察的最佳猜测。
- 首先排除最简单和最可能的问题区域。那些旨在被维护、以前失败过或受到机械应力的东西。好的系统设计成让可能坏掉的东西易于维护。例子:电气保险丝和断路器、皮带和链条、过滤器、各种端子和连接、I/O设备。
- 如果没有简单的方法来猜测问题所在的系统区域,执行非正式的二分搜索。
-
找到最简单的方法来证伪我的假设。一般来说,这意味着在我认为问题所在的位置的直接上游/下游"切断"系统,并在切断点测试功能。

断开子系统
当可能时,我会断开我正在调试的子系统。
这有三个好处:
- 它可以防止与系统其他部分的奇怪交互使诊断复杂化(一旦我让子系统自己工作,我可以将其重新连接在一起,看看它是否继续工作)
- 它可以保护系统的其他部分免受我的愚蠢行为的影响
- 它通常会缩短反馈循环
或者不
如果我不能(或不想)完全断开部件,另一种方法是在不同点探测 - 或切割和探测。
如果我知道或可以直觉到系统正常运行时测试点处相关参数的可接受范围,实际值可以指示问题的位置。
找到好的切割点
我可以在多少个点"切割"系统同时保持功能以进行测试?
火花塞是子系统之间切割点的一个例子。
如果发动机无法启动,但我可以获得火花,问题可能不在电气子系统中。
除了内部切割点外,我总是尝试在我负责的系统和世界其他部分之间的接口处进行测试。这可以帮助我确定是"我的东西"坏了,还是只是在尝试与坏了的"其他人的东西"交互时出错(这可能在我的能力范围内或之外)。尝试修复没有问题的东西会浪费很多时间!

平衡获取信息和尝试修复
应该在尝试修复问题上投入多少努力,在获取有关问题的信息上投入多少努力?
如果我的直觉是对的,直接着手修复问题会快得多。
但如果不是,从长远来看,系统地收集信息会更有效率。
我们进入故障排除情况时,对我们面临的问题的"难度"有一个先验,这会告诉我们何时尝试直接修复问题,何时尝试收集更多关于它的信息。
但我们的"难度先验"往往是错误的。所以我们需要基于个人倾向和领域专业知识,发展某种关于我们的故障排除难度先验可能有多准确的元先验。
了解风险
故障排除问题的风险可以从零(业余软件项目)到改变生活(医疗诊断)到存在性(AGI、核武器),这应该指导问题的处理方式。
所以我尝试问自己:
-
最糟糕的情况是什么?如果我搞砸了,风险是什么?我搞砸的可能性有多大?
- 对故障排除者的危险:系统工作起来有多危险?是否涉及高压、易燃材料、有毒化学品、千斤顶或可能的数据丢失?
- 对旁观者的危险:同上,但考虑爆炸半径的大小,以及旁观者对危险的潜在无知和缺乏安全设备。
- 对系统的危险:这个系统有多脆弱?它可以更换吗?
- 对他人的危险:这个系统行为不可预测的可能后果是什么?谁在这个系统的下游?
-
尝试修复和不尝试修复的相对风险是什么?
- 如果某件事已经危险地坏了,修复它相对更安全,即使修复它有失败的风险。
-
系统属于谁?
- 是我弄坏了这个系统吗?我有责任修复它吗?
- 我是被付钱来修复它的吗?如果是,我成功、失败和灾难性失败的可能性有多大,客户知道风险吗?(我不再为没有备份系统的人提供志愿技术支持。)
-
我如何减少这种干预的下行风险?
- 一般来说,工作更慢更仔细(这样我就不会搞砸);在"暂存"而不是" live"系统上工作;备份系统。
危险在这种情况下至少有四个组成部分:时间框架、强度、范围和物理性。最明显的危险是即时和物理的。但就伤害/益处而言,为主要投资银行处理关键代码可能在长期内同样具有物理风险;风险只是分布、延迟和不可见的。
不要过度思考
不要假设它很复杂。仅仅因为调试起来很复杂并不意味着原因很复杂。
但也不要假设它很简单。
保持耐心
不言而喻。
查找有关系统的信息
根据定义,故障排除者超出了他们的深度。好的故障排除者必须习惯处于无知状态。
需要什么信息以及如何获取信息,取决于系统和问题的细节。
知道使用什么搜索引擎获取什么类型的信息
有时(例如,机械,或钓鱼失败)我需要认识知道如何做的人,并给他们带一箱啤酒。
其他时候,我需要知道如何使用库或手册。(案例研究:我母亲的斯巴鲁的电动车窗停止工作。各种汽车人试图修复它们。我检查了保险丝。但她有一个聪明的想法,不是只是浏览手册找保险丝,而是阅读手册。儿童安全锁打开了。)
在现代世界,我主要需要知道如何使用搜索引擎。不同的搜索引擎用于不同的目的,谷歌不再索引许多利基网站。
我可能需要一个LLM,因为我不知道如何用正确的术语表达我的问题,搜索特定领域的论坛来减少废话,如果是难以用语言描述的物理技能,搜索YouTube,或者直接搜索相关系统的文档/手册。
知道如何使用高级搜索条件缩小搜索空间
虽然它们都不是故障排除特定的,但如果你不熟悉搜索操作符,这里有三篇文章可以开始:
知道如何扩大搜索空间并排除无关信息
大多数系统会发出多种信息,相关性差异很大。
在软件日志中,有一行行重复的 chatter,然后有一行提示我。通常,它会说"error"或"fail",或提到受影响的子系统。
同样,发动机发出很多噪音。大多数噪音是正常的。相关数据是声音的变化。当预期的噪音从总噪音中减去时,剩下的是什么?问题的声音指纹。
错误消息本身只有部分重要。
大多数软件错误消息如果我逐字粘贴到谷歌中,会产生很少有用的信息,因为它们包含特定于我的设备的内容。我通常从完整的错误消息开始,减去明显的设备特定信息。如果没有结果,我会扩大搜索空间。我不再寻找有关特定版本的信息。我不再寻找有关特定硬件的信息。我甚至不再寻找有关特定软件的信息,然后我会在论坛上找到拥有不同笔记本电脑、运行不同应用程序但共享导致错误的依赖项的人。他们的修复可能不起作用,但它可能指向问题所在。然后我可以获取更多数据并重复该过程。
学习钓鱼
如果某件事需要修理,我可以尝试自己做,这可能很慢且效率低下。或者我可以让专家来做,这很快且容易。让别人解决我的问题的问题:我学不到多少。
如果我有一个故障排除问题,并且对该领域一无所知,一个非常有效的方法是:
- 找到该类型系统/问题的领域专家
- 不是让他们为我修复它,而是让他们与我一起修复它。
这在短期内对双方来说都更慢,有时更不有趣,但可以学到很多。下次我可能能够自己做。
这是一个连续体,一端是看着他们修复它,另一端是我自己修复它而他们看着。我越动手,对每个人来说就越慢和沮丧 - 我被迫学习的就越多。
从系统获取信息
我从系统获取的信息越多,最好是在错误发生时,就越好。
我从系统获取所有可能的信息:更多的输出,以及来自系统中更多点的更具体的输出。
在软件中,这意味着日志记录,或在进程运行时附加调试器。
在电子设备中,第一步通常是获取万用表,并检查不同点的系统状态。
在机械方面,我不太擅长,我注意到我的哥哥通常打开引擎盖运行东西,同时密切观察、闻和听。

埃里克·巴克在《与他人相处融洽》中写到了这与人们的关系。我们不擅长阅读人(即使我们认为我们擅长),少数有帮助的事情之一是与该人互动,以产生更多信息。"从系统获取更多信息"是少数科学验证的测谎方法之一(说谎者最终会自相矛盾)。相关地:许多人可以权威地谈论复杂主题,但两三个精心选择的深入细节的问题可以将真正的专家与那些只是重复他人表面意见以听起来令人印象深刻的人分开。
当我们调试自己时,仅仅从系统获取信息的行为(记日记)有时就足以修复它。
直觉系统的容忍度
有时,在修复事物的过程中,我们会破坏东西,无论是意外还是因为无法避免。
不同的材料有不同的容忍度。某些部件可以承受某些类型的损坏而不改变系统的功能。其他区域如果以同样的方式处理可能会导致整个系统失败。
对于物理事物,需要机械直觉来施加正确的力,并理解哪些部件可以和不能承受损坏。孔或密封比外部外壳更敏感。
与系统保持良好关系
我注意到:不喜欢计算机的人在计算机方面往往效率低下;不喜欢人的人往往无法从他们那里得到他们想要的东西,除非他们擅长隐藏自己的感受。这听起来有些模糊,但我认为欣赏行为不当的系统的美丽和复杂性会使一个人成为更有效的故障排除者。将系统视为敌人会使它成为敌人。
在我看来,敌人与对手不同。与系统的友好竞争,试图"在游戏中击败它",似乎不会损害故障排除。对手必须受到尊重和理解才能被击败。要避免的是"坏狗"式的愚蠢计算机不肯按我想要的方式做事的心态。敌人,当被憎恨时,会被剥夺他们的个性,成为自己的漫画;你不能故障排除一个漫画,因为它与实际系统不匹配。
利用可用资源
拥有正确的工具来测试、拆卸、修理和重新组装系统会有所帮助。
在实践中,更重要的是能够即兴发挥,并基于底层形式而不是标签或先入之见找到工具和替换零件。
缩短反馈循环
为了修复系统,我需要能够重现问题。为了获得足够的关于问题原因的数据以可靠地重现它,我经常需要在不同条件下多次运行系统。然后,一旦我可以重现它,我通常需要再运行系统多次,每次调整参数,试图找出条件的哪个方面实际上触发了问题。
有时,系统会有一些内置的延迟或 latency,使得没有复杂的步骤就难以重现错误。
当这种情况发生时,我会问:"有什么方法可以缩短反馈循环吗?"
在代码中,这可能是减少硬编码的超时,只处理子组件(带有虚拟输入和输出),在本地测试以便我不会访问网络,启用热重载,或自动化部署过程。
对于电子设备,可能就像将万用表粘在端子上一样简单。
一个有点可怜的例子:当故障排除时,我经常发现自己在连接缓慢的小手机上查找东西,或者在我正在修复的东西和关于我正在修复的东西的信息源之间来回奔波。将手册或计算机直接放在我工作的地方可以节省令人惊讶的时间。
减少噪音
在进行干预之前,我尝试减少系统中的混淆变量和" sloppiness"。
断开子系统有助于此,因为与其他子系统的交互会混淆测试结果。
缩短反馈循环也有帮助,因为时间延迟给其他变量在输入进入和输出出来之间影响系统状态的机会。
写下来
作家喜欢说"写作就是思考"。以下是我将写作用作故障排除工具的两种方式:
-
像专业人士一样进行橡胶鸭调试:我通常可以通过起草论坛帖子而不发布它来解决我的问题。在我决定需要帮助的点上,阐明系统和问题的 salient 细节而不显得愚蠢所需的努力,高于我通常投入的努力。推论:在听起来我没有做过功课的情况下发表论坛帖子,也往往会让我超出解决看似微不足道的问题的时间/精力预算。
-
** behold the trail of crumbs**:我发现写作和图表,虽然对许多故障排除项目有帮助,但对多会话故障排除项目至关重要。我高估了我对上下文的记忆,以及我多快能继续该项目。故障排除笔记文件,无论我写它时其中的信息看起来多么明显或不完整,都会留下面包屑 trail,我下次可以跟随。(我经常重复,逐字逐句,整个故障排除过程,找到问题 - 然后记得我几年前就故障排除了完全相同的系统,并得出了相同的结论;但有一些小问题,我未能订购或安装新零件。)
通过向黑盒中放入特定内容并观察输出来查看内部
尽管"断开子系统",有时我必须故障排除黑盒系统。一种方法是向它们提供非常具体的输入,并观察会发生什么。如果系统在某些输入上失败,采取以下两种方法之一可能会有所帮助:
-
采取导致它失败的条件/输入,移除条件/输入的一个组件,并将其馈入系统。如果它仍然导致问题,移除另一个因素(可选地,恢复前一个因素)。重复直到剩下的只有真正导致问题的东西,或尽可能接近。
-
采取一些已知良好的条件/输入。添加一个存在于导致失败的输入/条件中的因素。重复(一个接一个,或累加)直到系统失败。
理解问题
也许其中一个小部件烧坏了。但为什么它会烧坏?小部件在周二下午就烧坏是正常的吗?或者,是否有短路,或水损坏,或散热问题,或(如最近的情况)容易被附近的雷达击倒?
如果某件事不应该失败,但它失败了,故事还有更多。
在放入新零件之前,我想确信它不会遭受同样的命运。要么前一个零件规格不足,要么系统中的其他东西使它承受了过度的压力。
解决问题
一个被理解的问题已经大部分被解决,除非零件难以获得,或难以安装。
"修复"某物通常是"更换损坏的组件"的同义词。
我对问题和系统的理解程度决定了需要更换的系统部分有多大。"零件更换者"是对坏机械师的贬义词。一般来说,故障排除者越好,更换的组件越小。
这些陈述可能都是真的:
- 这个音响系统坏了。
- 这个MP3播放器坏了。
- 这个MP3播放器的耳机插孔坏了。
- 这个MP3播放器的耳机插孔上的焊点坏了。
我可以更换MP3播放器。我可以更换耳机插孔。或者,我可以重流一个焊点。结果将是相同的。
虽然更换超过必要的部分是浪费和不优雅的,但一个好的故障排除者也需要知道何时放弃深入研究一个并不真正重要的问题的根源,并接受一个更资源高效的权宜之计。
故障排除可以教吗?
我从2024年5月开始间歇性地写这篇文章。也许我在自欺欺人,但我似乎在故障排除方面变得更好了。最大的变化是现在我费心。我接受否则无吸引力的故障排除问题以测试我的理论,无论我是否认为它们值得我的时间。并且因为我花了很多时间思考和谈论故障排除,我觉得我应该是某种当地专家,即使这个声誉只存在于我的脑海中,我也想不辜负它。
如果我有预算,我会科学地测试这一点。我会招募一群人。一半会读这篇文章,另一半会读同样长度和风格但不是关于故障排除的东西。然后,两组都会尝试在像sadservers.com这样的网站上解决Linux服务器故障排除难题。哪一组会更有效?如果读这篇文章的人做得更好,实验可以在其他领域重复,看看故障排除技能如何推广。
结论
一旦我进入故障排除心态,大多数事情开始看起来像故障排除问题。这种系统-问题-解决方案的世界观在某些情况下是有效的,但它不是唯一的世界观,也不是对所有事情的最佳方法。
当我意识到我正在处理一个故障排除问题并有意识地故障排除它时,它节省了很多时间。但同样重要的是要意识到并非所有事情都是需要解决方案的问题。
一些故障排除故事
祝你故障排除愉快!