it-swarm.cn

如何最有效地调试代码?

爬到代码中的错误可以被最小化,但是在编写时不能完全消除-程序员是,尽管许多人会 不同意 ,但是只有人类。

当我们确实在代码中检测到错误时,该怎么做才能将其清除?我们应该如何处理它,以最有效地利用我们宝贵的时间,使我们花更少的时间去寻找它,而花更多的时间编码呢?另外,调试时应该避免什么?

注意这里我们不是在谈论预防错误;我们正在谈论出现bug时该怎么办。我知道这是一个广阔的领域,可能高度依赖语言,平台和工具。如果是这样,请保持涵盖心态和一般方法之类的答案。

33
gablin

调试的心态和态度可能是最重要的部分,因为它决定了您修复错误的效率以及从错误中学到的知识(如果有的话)。

这样的软件开发经典著作(Pragmatic ProgrammerCode Complete)基本上主张相同的方法:每个错误都是学习的机会,几乎总是关于您自己的(因为只有初学者才将责任归咎于编译器/计算机)。

因此,将其视为一个谜,将很有趣。并通过系统地(对我们自己或对他人)表达我们的假设,然后(如果需要的话)逐一测试我们的假设,使用我们可以使用的所有工具,尤其是调试器和自动测试框架,来系统地破解这个谜团。然后,在解决了这个难题之后,您可以通过遍历所有代码来寻找可能发生的类似错误,从而做得更好。并编写自动化测试以确保不会在不知不觉中再次发生错误。

最后一点-我更喜欢将错误称为“错误”而不是“错误”-Dijkstra指责他的同事使用后一个术语,因为它是不诚实的,支持这样的观点,即有害的和善变的错误精灵会在我们不在的时候在我们的程序中植入错误。 t看,而不是因为我们自己的(马虎)想法而呆在那里: http://www.cs.utexas.edu/users/EWD/transcriptions/EWD10xx/EWD1036.html

例如,我们可以通过不再将bug称为bug而是将其称为error来清理语言。更为诚实的是,因为它把责任归咎于它所属的地方,即。与犯错的程序员。在程序员不看的时候恶意潜入的错误的一种泛泛的比喻在理论上是不诚实的,因为它掩盖了错误是程序员自己创造的。这种简单的词汇更改的好处是,它具有如此深远的影响:而以前,只有一个错误的程序曾经是“几乎正确的”,而后来有错误的程序就是“错误的”(因为错误)。

38
limist
  1. 编写测试。测试不仅可以很好地防止错误(以我的经验,正确执行TDD可以消除几乎所有的琐碎,愚蠢的错误),而且还有助于调试。测试迫使您的设计相当模块化,这使得隔离和复制问题变得更加容易。此外,您可以控制环境,因此不会有太多的惊喜。而且,一旦遇到失败的测试用例,就可以合理地确定自己已经确定了困扰您的行为的real原因。

  2. 了解如何使用调试器。 print语句在某种程度上可能工作得很好,但是大多数时候调试器对很有帮助(并且一旦您知道如何使用它,它比print语句舒适得多)。

  3. 谈论某人关于您的问题,即使只是 橡胶鸭 。强迫自己用语言表达自己正在解决的问题确实是奇迹。

  4. 给自己一个时间限制。例如,如果45分钟后您觉得自己无路可走,只需切换到其他任务一段时间。当您回到自己的错误时,希望您能够看到以前没有考虑过的其他可能的解决方案。

16
Ryszard Szopa

在这方面,我读过一本非常出色的书,叫做《 Why Programs Fail 》,它概述了各种查找错误的策略,从应用科学方法隔离和解决错误到增量调试,不一而足。本书另一个有趣的部分是它取消了术语“ bug”。 Zeller的方法是:

(1)程序员在代码中造成缺陷。 (2)缺陷引起感染(3)感染传播(4)感染引起故障。

如果您想提高调试技巧,我强烈推荐这本书。

以我个人的经验,我在应用程序中发现了很多错误,但是管理层只是简单地迫使我们继续开发新功能。我经常听到“我们自己发现了这个错误,客户还没有注意到它,所以请留到他们发现为止。”我认为对修复漏洞采取积极主动的态度是非常糟糕的主意,因为当需要实际解决问题时,您还需要解决其他问题,并且更多的功能管理人员希望尽快出手,因此您被抓住了在一个恶性循环中,该恶性循环可能导致大量压力并耗尽,最终导致缺陷缠身的系统。

发现错误时,交流也是另一个因素。发送电子邮件或将其记录在错误跟踪器上都很好,但是以我个人的经验,其他开发人员会发现类似的错误,而不是重用您投入的用于修复代码的解决方案(因为他们已经忘记了所有这些) ),它们会添加自己的版本,因此您的代码中有5种不同的解决方案,因此,它看上去更肿且令人困惑。因此,当您确实要修复错误时,请确保有一些人检查此修复程序,并在他们修复了类似问题并找到解决问题的好方法的情况下向您提供反馈。

limist提到了《 The Pragmatic Programmer 》这本书,其中包含一些有关修复错误的有趣材料。使用我在上一段中给出的示例,我将看一下: Software Entrophy ,其中使用了类似于残破的寡妇的类比。如果出现两个破碎的窗口,您的团队可能会无动于衷,除非您采取积极的态度。

3
Desolate Planet

我喜欢其他大多数答案,但是在您执行任何操作之前,这里有一些提示。将节省您的时间。

  1. 确定是否确实存在错误。一个错误总是在系统行为和需求之间有所区别;测试人员应该能够阐明预期的行为和实际行为。如果他无法为预期的行为提供支持,则没有任何要求,也没有错误-只是别人的意见。把它退回。

  2. 考虑预期行为错误的可能性。这可能是由于对该要求的误解。这也可能是由于需求本身的缺陷(详细需求和业务需求之间的差额)造成的。您也可以将这些寄回。

  3. 隔离问题。只有经验会教给您最快的方法-有些人几乎可以凭直觉做到这一点。一种基本方法是在使所有其他事物保持不变的同时改变一件事(问题是否发生在其他环境上?使用其他浏览器吗?在不同的测试区域中?在一天的不同时间?)另一种方法是查看堆栈转储或错误消息-有时您只能通过格式化的方式来判断系统的哪个组件引发了原始错误(例如,如果是德语,则可以责怪与您合作的第三方在柏林)。

  4. 如果将其范围缩小到两个可以协作的系统,请通过流量监控器或日志文件检查两个系统之间的消息,并确定哪个系统符合规范,哪个不符合规范。如果方案中有两个以上的系统,则可以执行成对检查,​​并以“降低”应用程序堆栈的方式进行工作。

  5. 隔离问题之所以如此重要,是因为问题可能不是由于您可以控制的代码缺陷(例如第三方系统或环境)引起的,而是您希望让该方尽快接手。这既可以节省您的工作,又可以立即使它们对准目标,从而可以在尽可能短的时间内实现分辨率。您不想在某问题上工作十天,只是发现它确实与其他人的Web服务有关。

  6. 如果您确定确实存在缺陷,并且确实在您控制的代码中,则可以通过查找最后一个“已知良好”的构建并检查源代码管理日志中是否可能引起此问题的更改来进一步隔离问题。这样可以节省很多时间。

  7. 如果您无法从源代码管理中找出问题,那么现在该是连接调试器并逐步检查代码以解决问题的时候了。无论如何,现在您很有可能对这个问题有个很好的主意。

一旦知道了错误的所在并可以考虑修复程序,以下是修复此错误的好方法:

  1. 编写一个重现问题并失败的单元测试。

  2. 在不修改单元测试的情况下,使其通过(通过修改应用程序代码)。

  3. 将单元测试保存在测试套件中,以防止/检测回归。

3
John Wu

错误,错误,问题,缺陷-不管您想称它什么,它都没有太大的区别。我会坚持下去,因为那是我惯常的做法。

  1. 弄清楚问题的根源:将客户的“鲍勃仍不在系统中”转换为“当我尝试为鲍勃创建用户记录时,它会失败,并出现重复的关键异常,尽管鲍勃还没有在那里'
  2. 找出是真正的问题还是误解(事实上,鲍勃不在那儿,没有人叫鲍勃,插入应该可以工作)。
  3. 尝试获得可重现该问题的最低限度的可靠步骤-类似于“给出具有用户记录'Bruce'的系统,当插入用户记录'Bob'时,会发生异常”
  4. 这是您的测试-如有可能,将其放入可以一次又一次运行的自动测试工具中,这在调试时将非常宝贵。您还可以使其成为测试套件的一部分,以确保以后不再出现该特定问题。
  5. 释放调试器并开始放置断点-在运行测试时找出代码路径,并找出问题所在。在执行此操作时,还可以通过使其尽可能窄来优化测试-理想情况下是单元测试。
  6. 修复它-验证您的测试通过。
  7. 验证客户描述的原始问题是否也已解决(非常重要-您可能只解决了部分问题)。验证您没有在程序的其他方面引入回归。

如果您对代码非常熟悉,或者问题或解决方法很明显,则可以跳过其中一些步骤。

我们应该如何处理它,以最有效地利用我们的宝贵时间,使我们花更少的时间去寻找它,而花更多的时间编码?

我对此表示怀疑,因为这意味着编写新代码比拥有高质量的工作程序有价值。尽可能有效地解决问题并没有错,但是程序不一定会通过添加更多代码而变得更好。

3
ptyx

我认为错误的复制也很重要。可以列出所有重现该错误的案例,然后可以确保您的错误修复程序涵盖了所有这些案例。

3
aslisabanci

当我们确实在代码中检测到错误时,该怎么做才能将其清除?我们应该如何处理它,以最有效地利用我们的宝贵时间,使我们花更少的时间去寻找它,而花费更多的时间进行编码?另外,调试时应该避免什么?

假设您处于生产环境中,则需要执行以下操作:

  1. 正确描述“错误”,并确定导致错误发生的事件。

  2. 确定“错误”是代码错误还是规范错误。例如,对于某些系统,输入1个字母名称可能被认为是错误,但对于其他系统则可以接受。有时,用户会报告他/她认为是问题的错误,但是用户对系统行为的期望并不是要求的一部分。

  3. 如果您已证明存在错误并且该错误是由于代码引起的,则可以确定需要修复哪些代码段以防止错误。还检查行为对当前数据和将来的系统操作的影响(对代码和数据的影响分析)。

  4. 在这一点上,您可能会估计将花费多少资源来修复该错误。您可以立即对其进行修复,也可以计划在即将发布的软件版本中进行修复。这还取决于最终用户是否愿意为修复程序付费。您还应该评估其他可用选项以修复该错误。可能有不止一种方法。您需要选择最适合这种情况的方法。

  5. 分析导致此错误出现的原因(需求,编码,测试等)。强制执行可防止情况再次发生的过程。

  6. 充分记录该情节。

  7. 发布修复程序(或新版本)

1
NoChance

这是我的方法:

  1. 每次使用相同的方法查找问题。这将改善您对错误的反应时间。
  2. 最好的方法可能是阅读代码。这是因为所有信息在代码中都可用。您只需要有效的方法来找到正确的位置和理解所有细节的能力。
  3. 调试是非常慢的方式,只有在您的程序员还不了解计算机如何执行asm指令/不了解调用堆栈和基本内容的情况下才需要进行调试
  4. 尝试开发证明技术,例如使用函数原型来推理程序的行为。这将有助于更快地找到正确的位置
1
tp1