编程的艺术就是处理复杂性的艺术
--Edsger Dijkstra (图灵奖得主)

让棘手的问题无处躲藏,如何系统性的定位和解决各种难题

你是否曾陷入进一个困难的问题而束手无策?你是否曾遇到一些诡异的问题而怀疑操作系统,编译器有bug?你在调试程序问题的时候是否只是一通乱试,这里修修那里改改,然后试试结果是否正确?

为什么有些程序员的工作效率很高,而有些则必须经常加班活还是干不完(当然要排除有些就是任务多时间紧的情况)?

大多数情况并不是别人比你有先天优势,很可能仅仅是因为你没有采用系统的解决方法。那么当我们遇到一些棘手的问题到底该怎么办?你可以尝试下面这些套路,这些方法虽不能说包治百病,但至少能帮助你提高你的工作效率。并且在你遇到困难的问题时,有一个清晰的思路可以去一步步走下去,而不是像一只无头苍蝇一样到处乱撞,白白的浪费了时间不说,最后还得拼运气靠人品才能解决。

本文虽然主要面向程序员,但不仅仅只是写给程序员,很多类似需要脑力和手工来处理和解决问题的场合都普遍适用。

 

1.给问题一个清晰明确的定义

define-problem
给问题一个清晰明确的定义

在尝试一头扑下去解决一个问题之前,最好花一点时间搞清楚这个问题到底是什么。只有对一个问题有了清晰明确的认识之后才能更具有针对性和目的性,避免了努力了半天根本方向就不对的尴尬。

比如你在调试一个程序问题时,如果这个问题产生的原因是由于硬件和平台自身的限制引起的。那你一定得换换一个思路和方法去绕过去解决,否则再凭你如何有毅力,如何不屈不饶,硬件和平台的限制始终在那里。你只有修改程序的方法和策略才能绕过它,而不是与之正面交锋。

我们可以通过问自己一些问题来搞清楚问题的定义到底是什么:

  • 这个问题的外在表现是什么?
  • 它是由什么引起的?
  • 和其他部分有什么关联?
  • 可能是什么原因导致的?
  • 如果不去解决这个问题有多大影响?

有时候问题本质定义可能和你一开始想象的并不一样,所以你还可以尝试下改变自己对问题的定义和描述,这样也可以给解决问题带来不一样的思路和方法。

 

2.把问题讲给你的同事听

这其实是针对上述方法的一种实践。之所以我把它独立出来是因为这个实践确实是一个非常行之有效的方法。

实际工作中经常遇到自己把遇到难题完整的描述一遍之后,他们自己就会说:“哦,我已经意识到问题的原因了,大概知道如何解决了”。所以下次你被某些问题纠缠而无法自拔的时候,可以尝试下和你身边的人从头到尾把问题梳理一遍。如果你身边没有合适的人,也可以对着电脑屏幕说说也行,把它们假想成一个能帮助你的人,系统性的描述你的问题,和可能在你描述的过程中你就已经有了解决方案了。

 

3.认真阅读出错信息

error-message
认真阅读出错信息

在计算机软件,手机和其他各种电子设备上,出问题的时候往往会有一段出错提示信息。绝大部分用户都不会仔细阅读就选择直接关掉出错信息显示,接下来往往面临着系统崩溃和设备再也无法正常的工作了。

如果你是要解决这个错误的人,切忌不要随手就关掉这个信息,你应该仔细阅读它。你随手点的一个关闭按钮可能让你多浪费半天的工作时间,因为很大一部分问题往往能在读懂了错误信息之后能被轻松解决。就算不能立刻解决问题,也能给你指示出一个明确的方向。

 

4.善于利用搜索引擎和网络资源

这种方法特别适合解决有上文所述的错误提示信息或者有较明确的关键字的问题。

在你根本不能理解这些提示信息的情况下,你应该稍微整理下这样的信息,提取其中的关键字放到搜索引擎中去搜索。一般来说,你遇到的问题,别人已经遇到过,并且讨论过各种解决方案的概率非常大。就算不一定能找到和你一模一样的问题的答案,也可能找到类似问题的解决方案,或者至少能为自己的问题提供一些不一样的思路和角度。

搜索关键字应该去掉一些和你个人相关的信息,比如只有你电脑上才有的文件路径之类。如果搜索结果不理想可以再次精简和提炼出错信息的关键字再去搜索。如果中文没有太多有价值的内容可以考虑用英文关键字去搜索,毕竟互联网上英文内容是最丰富的。

search
Google的搜索结果是常见搜索引擎中最优秀的,其排在前面的结果都是匹配度比较高的官方权威信息

非常值得一提的是:请使用Google搜索,请使用Google搜索,请使用Google搜索。虽然目前国内访问Google不太容易,但也没有想象的困难,如果你不知道如何科学上网,可以求助你身边的朋友。Google搜索绝对是世界上最好用的信息搜索引擎,没有之一。我一直坚持认为,不使用Google的程序员不是好程序员

对于利用网络资源,可能会有人说,有些程序员都沦落成 github 和 stack overflow 搬运工了,我并不完全同意。毕竟能解决问题是最关键的。如今大部分的创新形式也都是对现有东西的优化和整合,并不是一个从无到有凭空出现的过程。从 github 和 stack overflow 抄代码和去官方手册抄代码到底又有多少不同?如果能理解并且可以用来快速解决问题,何乐而不为呢?不过提醒下,你应该小心仔细的处理好版权问题。

 

5.首先尝试在自己身上找原因

无数次证明(你可以从现在开始记录下),刚开始很多被断定是编译器和操作系统bug的问题,其实都是自己程序问题。所以解决问题的切入点和重点应该是从自身开始。

如果你写的一段程序无缘无故的就崩溃了,或者表现出了一些奇怪的现象,请不要在没花多少时间排除自己代码故障时,就投入大量的精力去验证是编译器或者操作系统出了问题。确实,再成熟的编译器和操作系统都难免有bug,但被我们遇到的概率较低,如果我们本末倒置花大量时间去验证它们,倒不如先花大量的时间从自己的代码中找原因,从概率角度这也是更加靠谱的策略。

当然,结合上文“善于利用搜索引擎和网络资源”讨论的方法,快速去网上搜索下就可以快速证明其到底是不是外部环境的bug。一般来说,你是第一个遇到这样的编译器和操作系统bug概率也是很低的。如果真是编译器和操作系统bug,网络上相应的讨论早已满天飞了,甚至官方早已经给出了fix的补丁。

对于在一些新的平台和系统上出现的问题,最好能越早去网络上搜索越好,比如iOS 系统各个版本就出现过不少经典的系统API bug,这时候只能使用其他方法绕过去这些bug。

 

6.定位问题的根源和本质

  1. 这个方法特别适合当你遇到一个完全无从下手的问题的时候。用这些技巧去逐步缩小问题的范围,最终揪出到问题的本身所在,让其无处藏身。具体做法如下:

    • 排除法
      一点点排除可能引起问题的关键因素,直到确定是由某个原因引起的。

      此方法适用于以前工作正常,在某个时间或条件之后才出现的问题。这个问题是什么时候开始出现的?我们把这之后添加的新功能(代码)一点点拿掉看看问题是否出现,直到定位到具体某次改动。举个例子,比如你在完成一个组装电脑之后,开机没有任何反应,此时你可以考虑从正常工作的电脑上逐步替换掉问题电脑的内存硬盘CPU等零件,直到问题消失就能定位到底是具体哪个零件出了问题。

    • 最小环境法
      在一个简单且没有过多因素干扰的环境中验证问题根源和本质。

      此方法适用于比较复杂的系统和环境,和适用如果使用排除法需要排除太多东西,并且我们对该问题的起因有一定的认识的情况。比如你在调试一个程序问题的时候,出问题的部分和其他模块或子系统有错综复杂的关系,为了定位问题的本质和消除其他外部因素的影响和干扰,可以考虑新建一个简单项目,在这个没有其他因素影响的环境中重现和解决问题之后再应用到原来的项目中。对于可能由一些驱动或者硬件平台引起的问题,可以考虑在一个纯净的虚拟机环境中重现从而定位你的问题。

    • 使用日志和断点
      这个是调试程序问题的惯用方法,对此不打算详细论述。主要就是在怀疑可能出问题的地方设置断点或者打印出相关变量的值,以便于确定从哪儿开始,程序已经出现了错误了,从而定位到一开始出错的地方加以修正。

     

7.有计划的尝试可能的解决方案

该方法适用于如果使用排除法需要排出因素太多,最小环境下也没有确定出根源,甚至完全没有方向的时候。

具体的做法就把出问题的系统或者模块划分成几个部分,把可能的解决方案列一一列出来,针对每个部分逐步验证。在尝试的时候应该把确定不能解决问题的方案以及和问题无关的部分从列表中删除,不再尝试。对于确定问题所在的部分继续逐步划分,直到定位问题的根源和找到解决方案。

这种对系统和功能进行划分并且把可能的解决方案列一一列出来的方法,可以避免胡乱反复尝试碰运气,避免了在不可能出问题的部分上反复尝试,从而不必要的浪费时间。否则你一会儿试试A,一会儿试试B,一会儿再试试A,如此反复,也不清楚到底具体尝试了什么。其中可能还要使用排列组合的方法应用这项方案,因为问题的根源可能是多个因素造成的。

 

8.休息一会儿,暂时彻底抛开问题

当你长时间在研究一个问题而无法解决的时候,此时最好的策略是停下来休息。长时间的聚焦在一个问题让容易让自己的大脑陷入泥沼中而无法自拔。如果坚持继续在这个问题上纠缠,越是挣扎陷入越深。

pit
《人月神话》第一章 焦油坑(The Tar Pit)第一幅图

此时你应该完全放下问题,出去转一圈,或者干脆抛开这个问题,第二天再去解决。很多人都有遇到过当天无法解决的问题,第二天一来就轻松的被解决了。出现这种现象的原因不仅仅因为之前你已经排除了各种坑,更可能是隔一段时间之后你会对这个问题有新的思考角度和新的认知,有了不一样的角度就能轻松跳出之前陷入的泥沼。

 

9.求助领域专家和找人讨论

面对超出自己知识范围的问题,或者遇到需要花太长时间来熟悉相关知识才能解决的问题,而此时你身边恰好可以找到相关专家的时候,你应该果断的去求助他们。

在求助别人之前,请确保你已经做好相关功课和准备了一些显而易见的知识,不能简单的把问题扔给别人而浪费别人的时间。特别要避免请教别人那些只要简单的在网络上搜索就能找到答案的问题。这里有份提问别人问题之前的准备资料,找别人帮你解决问题之前,做完这些功课以后,至少让人觉得你其实已经充分研究了这个问题确实自己没有办法解决才去找他帮忙的。

 

10.跳出问题本身去思考

有些时候,别人描述的问题并不一定就是问题的真正所在。有一副很有名的漫画 How IT Projects Really Work,虽然它描述的是IT项目中需求和管理的各种坑,但从中也可以看出有时候我们认为的问题真正原因并不一定就是问题的本质。写到这里忍不住要推荐一本很老的书《你的灯亮着吗?:发现问题的真正所在》。这本书对问题的定义超过了很对人对问题的认知。一般来说一个问题就一个问题,它有明确的定义,它能被解决就结束了。但事实上有些问题没有一个明确的定义,也没有一个最优的解。你可以从正面去解决它,也可以从它相反的方面去适应它。即使被你解决了的问题也不一定就是表面上看上去那样,你也未必就找到了问题的真正所在。

有一个关于慈溪太后的趣闻,话说慈溪太后第一次坐汽车时问道:“这车跑得这么快,要吃许多草吧?”[1]这是一个典型的没有跳出问题本身去思考的例子。在汽车没有被发明之前,如果有人问你,你的出行需求是什么,你的回答很可能是:我要一辆速度更快,更加舒适豪华的马车。如果你没有跳出这个问题本身,不去寻找这个问题的本质是什么,你只能做出一个更快更舒适的马车,虽然很大程度上你也是结解决了这个问题。如果所有人都这么想,如今的汽车就不会被发明出来了。正是发明汽车的人跳出了问题的本身而挖掘到了这个问题的本质其实是:用户需要更快更舒适的出行方案,而并不是更快更舒适的马车。这也是文章开头第一点说明的,要给问题一个清晰明确的定义作为解决问题的出发点的原因所在,也是为什么我们要像第六点中提到的定位问题的根源和本质的原因所在。

如果你不能跳出问题本身,不去探索问题的本质,虽然能在某种程度上也能解决问题,但很难做出创新和完美的解决方案。

 

我们再来来回顾下这个十条策略分别是:

  • 给问题一个清晰明确的定义
  • 把问题讲给你的同事听
  • 认真阅读出错信息
  • 善于利用搜索引擎和网络资源
  • 首先尝试在自己身上找原因
  • 定位问题的根源和本质
  • 有计划的尝试可能的解决方案
  • 休息一会儿,暂时彻底抛开问题
  • 求助领域专家和找人讨论
  • 跳出问题本身去思考

相信只要灵活应用这些策略,系统性的去分析和解决出现的问题,任何棘手的问题都能被有条不紊的应对。


[1]:来源无法考证 ↩︎

 

版权声明
本博客所有文章皆为原创,作者保留所有版权。转载必须保证全文完整和包含本声明,并以超链接形式注明出处 http://www.macode.net/resolve-problem-in-systematic-way/

发表评论

电子邮件地址不会被公开。 必填项已用*标注