疯狂Java学习笔记(85)-----------用医生的思考方式调试你的代码

“现在的编程工作就像是对你需要解决处理的部分做科学研究。”

——Gerald Sussman

设计和维护好的软件就像是一个抵制复杂度的永无止境的奋斗过程。任何足够大小的应用程序的代码路径和组件都能迅速成长成令人眼花缭乱的组合爆炸。

一点都不简单。

当部署类似于Heroku和AWS的平台时,单服务器的Web应用程序成为了分布式系统。现代浏览器模糊了客户端和服务器之间的界线。当简单程序在多个CPU内核上运行时,它们就会成为复杂的协调问题。虽然像测试驱动开发等实践和SOLID原则等指导,可以帮助我们模拟问题,简化解决方案,但大多数软件应用程序都是一些复杂的系统,每个组件也会以意想不到的方式进行交互和组合。

当软件系统中发生意外情况时,会造成很严重的后果。幸运的是,软件开发人员可以借鉴另一门更古老的学科,来应对对于复杂系统的关注、维护和调试,这门学科就是:医学。

鉴别诊断是医生用来匹配系列症状及其可能病因的系统化方法。一个好的鉴别诊断包括以下4个步骤:

  1. 列出所有观察到的症状。
  2. 列出可能的病因。
  3. 按轻重缓急给这些病因排名。
  4. 按照优先顺序进行测试,以排除病因。

虽然上面这4个步骤是为医生而整理的,但是我们同样可以像一个医生一样思考,用一种强有力的方式来找到并消除软件缺陷。将诊断过程分解为一个一个目的单一的步骤,确保每个步骤都能得到应有的重视。按照优先顺序是为了保证专注检查的重点,并作出务实的干预措施。然后进行测试,排除假设,以确保调试的严谨。

白板是个好东西

当错误发生时,我们大多会想也不想地立马去调查最可能的原因。懂得向后跟踪和少许背景知识,人性就会趋向于投机主义。但是好的诊断始于列出的症状,而不是病因。写下可以观察出来的所有症状,无论是异常处理,还是错误代码,哪怕只是异常的行为,都可以。可以使用文本编辑器或者白板,但是,你最好能对诊断过程中的每一个步骤做笔记,这很重要。从假设出发分开观察,有助于确保你不会排除或忽视潜在原因。并且多数时候,列出更多的症状反而会缩小可能范围,避免你将时间浪费在测试不正确的假设上。

写好了一系列症状,那么接下来就可以开始考虑原因了。

斑马和马

“当你听到马蹄声的时候,找的应该是马,而不是斑马。”

在应用程序中出现代码bug的可能性比在Web框架中出现bug的可能性要大,而在Web框架中发现bug又比在操作系统中发现bug更容易。当然让别人来审查代码是个好主意,但事实是,大多数bug审查起来特别无聊。所以在开始考虑进阶到更复杂的问题之前,先给出最简单的解释。

话又说回来,正如同一个症状却又可能是完全不同的病因引发的,所以我们应该将所有能想到的相关病因都写下来。就像原先我们对症状直接描述为“what”,后来用“how”区分开来,头脑风暴解释法的目的是用“how likely”来区分“how”。捕捉任何看似合理的要点,以便于节约分析。

重中之重,不能有害

鉴别诊断与其他的演绎方法不同,因为医生必须不断地评估风险,并权衡对病人生命的影响。当然如果我们的产品中存在着bug,虽然不会像医生那样负有生命责任那般严重,但是停顿修复会产生既现实又痛苦的成本费用。就像威胁生命的疾病事件一样需要立即进行干预,严重的bug可能需要粗暴的简单修复,例如回滚和重新启动。将假设按优先顺序排列,然后再考虑权衡,并判断决定是否启动测试假设或立即进行干预。

准备图表

正如患者会有医院病历和其他背景信息的图表,你的软件系统可能也需要具备图表。从日志和错误报告系统收集信息,来说明你的分析。至于系统指标和跟踪误差,你不妨将它们当作是明智的预防性药品。

如果你的病人尚未处于严重危险之中,那么可以先进行假设-演绎。从你定义的优先级最高的假设开始,一个一个地证明它们是错误的。虽然支持性证据有时候或许能有助于你找到bug的所在,但是失败的测试驱动了演绎过程。这乍一看上去似乎有悖直觉,但是测试-消除假设策略是追溯bug到它的起因的最快方式。在许多情况下,一些简单的测试就可以一次消除几个假设。当然,也有时候,为了否决假设你就得执行更多的测试。

实验室工作

不同于医疗世界的令人难以接受,只要你愿意,你随时都可以克隆软件应用程序,执行可怕的人体实验。如果你有足够的信息来触发你要诊断的bug,那么可以将它复制到受控环境中,例如一个有着最新数据库备份的临时服务器。当你消灭原因,收集到新的数据,并完善假设之后,你的bug的真正原因线索将变得更加清晰。

清楚地思考复杂系统需要的关心和专注。采用结构化的诊断过程来指导检查可以节省时间和避免挫折感。最重要的是,它 很有用。下次你再陷入bug之中时,那么不妨试试抛开键盘,将步骤一步一步写到白板上,像一个医生诊病一样进行调试。

本文借鉴

http://www.codeceo.com/article/debug-like-doctor.html

时间: 2024-10-25 21:24:58

疯狂Java学习笔记(85)-----------用医生的思考方式调试你的代码的相关文章

用医生的思考方式调试你的代码

“现在的编程工作就像是对你需要解决处理的部分做科学研究.”——Gerald Sussman 设计和维护好的软件就像是一个抵制复杂度的永无止境的奋斗过程.任何足够大小的应用程序的代码路径和组件都能迅速成长成令人眼花缭乱的组合爆炸. 一点都不简单. 当部署类似于Heroku和AWS的平台时,单服务器的Web应用程序成为了分布式系统.现代浏览器模糊了客户端和服务器之间的界线.当简单程序在多个CPU内核上运行时,它们就会成为复杂的协调问题.虽然像测试驱动开发等实践和SOLID原则等指导,可以帮助我们模拟

【疯狂Java学习笔记】【理解面向对象】

[学习笔记]1.Java语言是纯粹的面向对象语言,这体现在Java完全支持面向对象的三大基本特征:封装.继承.多态.抽象也是面向对象的重要组成部分,不过它不是面向对象的特征之一,因为所有的编程语言都需要抽象. 2.面向对象开发方法比较结构化开发方法的优势在于可以提供更好的可重用性.可扩展性.可维护性. 3.基于对象和面向对象的区别:基于对象也使用了对象,但是无法通过现有的对象作为模板来生成新的对象类型,继而产生新的对象,也就是说,基于对象没有继承的特点.而面向对象有继承,而多态则是建立在继承的基

疯狂Java学习笔记(89)-----------Java习惯用法总结

在Java编程中,有些知识 并不能仅通过语言规范或者标准API文档就能学到的.在本文中,我会尽量收集一些最常用的习惯用法,特别是很难猜到的用法.(Joshua Bloch的<Effective Java>对这个话题给出了更详尽的论述,可以从这本书里学习更多的用法.) 我把本文的所有代码都放在公共场所里.你可以根据自己的喜好去复制和修改任意的代码片段,不需要任何的凭证. 目录 实现: equals() hashCode() compareTo() clone() 应用: StringBuilde

疯狂java学习笔记之面向对象(八) - static和final

一.static: 1.static是一个标识符: - 有static修饰的成员表明该成员是属于类的; - 没有static修饰的成员表明该成员是属于实例/对象的. 2.static修饰的成员(Field.方法.初始化块),与类共存亡:static修饰的成员建议总是通过类名来访问,虽然它也可以通过实例来访问(实质也是通过类来访问的),所以平时若在其他程序中见到通过实例/对象来访问static成员时,可以直接将实例/对象 替换成类名: 3.程序都是先有类再有对象的,有可能出现有类但没有实例/对象的

[core java学习笔记][第十一章异常断言日志调试]

第11章 异常,断言,日志,调试 处理错误 捕获异常 使用异常机制的技巧 使用断言 日志 測试技巧 GUI程序排错技巧 使用调试器 11.1 处理错误 11.1.1异常分类 都继承自Throwable类 分成Error和Exception Error类 描写叙述了Java运行时系统的内部错误和资源耗尽错误. 应用程序不应该抛出此种类型的错误.假设出现了这样的内部错误.除了通告给用户,并尽力使程序安全地终止外,再也无能为力 Exception层次结构:最需关注的 RuntimeException

疯狂Java学习笔记(34)----------Iterator、Collection接口以及foreach

Iterator.Collection接口: 如下图:Iterator.Collection同在一个包中: 红字部分使我们经常遇到的,但是遇到又不知道怎么去理解,去应用它! Collection是最基本集合接口,它定义了一组允许重复的对象.Collection接口派生了两个子接口Set和List,分别定义了两种不同的存储方式,如下: 2. Set接口 Set接口继承于Collection接口,它没有提供额外的方法,但实现了Set接口的集合类中的元素是无序且不可重复. 特征:无序且不可重复. 3.

疯狂Java学习笔记(51)-----------面试题

自己做了一点面试题,感觉很经典,分享给大家,发现还有很多东西需要学! 一.String,StringBuffer, StringBuilder 的区别是什么?String为什么是不可变的? 答:   1.String是字符串常量,StringBuffer和StringBuilder都是字符串变量.后两者的字符内容可变,而前者创建后内容不可变. 2.String不可变是因为在JDK中String类被声明为一个final类. 3.StringBuffer是线程安全的,而StringBuilder是非

疯狂Java学习笔记(87)-----------十篇必读的Java文章

1.Brian Goetz:"管理工作:发人深省的部分" 这其实不是一篇博文,而是Brian Goetz对于甲骨文公司Java的管理的一个非常有趣的讨论的记录.在 以前我们将Java语言与Scala或者Ceylon相比较的时候,对其1-2个特性一直稍微有些意见. 对于为什么Java尽快变得和其他语言一样"时髦"不是一个好主意,Brian提出了很好的观点.每一个Java开发者都应有所了解(大约一个小时). Youtube链接. 2.Aleksey Shipilёv:(

疯狂Java学习笔记(88)-----------值得拥有的10本书

Java是时下最流行的编程语言之一.市面上也出现了适合初学者的大量书籍.但是对于那些在Java编程上淫浸多时的开发人员而言,这些书的内容未免显得过于简单和冗余了.那些适合初学者的书籍看着真想打瞌睡,有木有.想找高级点的Java书籍吧,又不知道哪些适合自己. 别急,雪中送炭的来了:下面我将分享的书单绝对值得拥有.ps,我也尽力避免列出为特定软件或框架或认证的Java书,因为我觉得那不是纯Java书. 1.<Java in a Nutshell>(Java技术手册) 与其说是必读书籍,还不说是参考