可以将爬入代码中的错误减至最少,但在编写时不能完全消除-程序员是程序员,尽管许多人会不同意,但这只是人类。
当我们确实在代码中检测到错误时,该怎么做才能将其清除?我们应该如何处理它,以最有效地利用我们的宝贵时间,使我们花更少的时间去寻找它,而花费更多的时间进行编码?另外,调试时应该避免什么?
请注意,此处我们并不是在谈论防止错误;我们正在谈论出现错误时该怎么办。我知道这是一个广阔的领域,可能高度依赖语言,平台和工具。如果是这样,请保持涵盖心态和一般方法之类的答案。
可以将爬入代码中的错误减至最少,但在编写时不能完全消除-程序员是程序员,尽管许多人会不同意,但这只是人类。
当我们确实在代码中检测到错误时,该怎么做才能将其清除?我们应该如何处理它,以最有效地利用我们的宝贵时间,使我们花更少的时间去寻找它,而花费更多的时间进行编码?另外,调试时应该避免什么?
请注意,此处我们并不是在谈论防止错误;我们正在谈论出现错误时该怎么办。我知道这是一个广阔的领域,可能高度依赖语言,平台和工具。如果是这样,请保持涵盖心态和一般方法之类的答案。
Answers:
调试的心态和态度也许是最重要的部分,因为它决定了您修复错误的效率以及从错误中学习的内容(如果有的话)。
诸如The Pragmatic Programmer和Code Complete之类的软件开发经典著作基本上都主张采用相同的方法:每个错误都是学习的机会,几乎总是关于您自己的(因为只有初学者首先将责任归咎于编译器/计算机)。
因此,将其视为一个谜,将很有趣。并通过系统地(对我们自己或对他人)表达我们的假设,然后(如果需要的话)逐一测试我们的假设,使用我们可以使用的所有工具,尤其是调试器和自动测试框架,来系统地破解这个谜团。然后,在解开谜团之后,您可以通过查看所有代码中是否存在类似的错误来做得更好。并编写一个自动化测试,以确保不会在不知不觉中再次发生该错误。
最后一点-我更喜欢将错误称为“错误”而不是“错误”-Dijkstra指责他的同事使用后一个术语,因为这是不诚实的,支持这样的观点,即有害和善变的错误精灵会在我们不使用时在我们的程序中植入错误。 t看,而不是因为我们自己的(草率的)想法而呆在那里:http : //www.cs.utexas.edu/users/EWD/transcriptions/EWD10xx/EWD1036.html
例如,我们可以通过不再将bug称为bug而是将其称为error来清理语言。坦率地说,因为它把责任归咎于它所属的地方,即。错误的程序员。在程序员不看的时候恶意潜入的bug的一种泛泛的比喻在理论上是不诚实的,因为它掩盖了错误是程序员自己创造的。这种简单的词汇变化的好处是,它具有如此深远的影响:以前,只有一个错误的程序曾经是“几乎正确的”,而后来有错误的程序只是“错误的”(因为错误)。
编写测试。测试不仅可以很好地防止错误(以我的经验,正确执行TDD可以消除几乎所有的琐碎,愚蠢的错误),而且还有助于调试。测试迫使您的设计相当模块化,这使得隔离和复制问题变得更加容易。此外,您可以控制环境,因此不会有太多的惊喜。而且,一旦遇到失败的测试用例,就可以合理地确定自己已经弄清了困扰您的行为的真正原因。
了解如何使用调试器。print
语句可能在某种程度上可以很好地工作,但是大多数时候调试器都非常有用(而且一旦知道如何使用它,它比print
语句要舒适得多)。
与某人谈论您的问题,即使这只是橡皮鸭。强迫自己用语言表达自己正在解决的问题确实是奇迹。
给自己一个时间限制。例如,如果45分钟后您觉得自己无路可走,只需切换到其他任务一段时间。当您回到自己的错误时,希望您能够看到其他您以前从未考虑过的解决方案。
关于这一主题,我读了一本非常出色的书,名为《程序为什么失败》,其中概述了各种查找错误的策略,从应用科学方法隔离和解决错误到增量调试,不一而足。本书另一个有趣的部分是它消除了术语“ bug”。Zeller的方法是:
(1)程序员在代码中造成缺陷。(2)缺陷引起感染(3)感染传播(4)感染引起故障。
如果您想提高调试技巧,我强烈推荐这本书。
以我个人的经验,我在应用程序中发现了很多错误,但是管理层只是简单地迫使我们继续开发新功能。我经常听到“我们自己发现了这个错误,客户还没有注意到它,所以请留到他们发现为止。” 我认为对修复漏洞采取积极主动的态度是非常糟糕的主意,因为当需要实际解决问题时,您还需要解决其他问题,并且更多的功能管理人员希望尽快出手,因此您被抓住了在一个恶性循环中,该恶性循环可能导致大量压力并消耗out尽,最终导致缺陷缠身的系统。
发现错误时,交流也是另一个因素。发送电子邮件或将其记录在错误跟踪器上都很好,但是以我个人的经验,其他开发人员会发现类似的错误,而不是重用您投入的用于修复代码的解决方案(因为他们已经忘记了所有这些) ),它们会添加自己的版本,因此您的代码中有5种不同的解决方案,因此,它看上去更肿且令人困惑。因此,当您确实要修复错误时,请确保有一些人检查此修复程序,并在他们修复了类似问题并找到解决问题的好方法的情况下向您提供反馈。
limist提到了《实用程序员》这本书,其中有一些有趣的错误修复材料。使用我在上一段中给出的示例,我将看一下:Software Entrophy,其中使用了一个残破寡妇的类比。如果出现两个破碎的窗口,您的团队可能会无动于衷,除非您采取积极的态度。
错误,错误,问题,缺陷-不管您想称它什么,它都没有太大的区别。我会坚持下去,因为那是我过去的习惯。
如果您对代码非常熟悉,或者问题或解决方法很明显,则可以跳过其中一些步骤。
我们应该如何利用它来最有效地利用我们宝贵的时间,使我们花费更少的时间去寻找它,而花费更多的时间进行编码?
我对此表示怀疑,因为这意味着编写新代码比拥有高质量的工作程序有价值。尽可能有效地解决问题并没有错,但是程序不一定会通过添加更多代码而变得更好。
我喜欢其他大多数答案,但是在您执行任何操作之前,这里有一些提示。将节省您的时间。
确定是否确实存在错误。一个bug总是在系统行为和需求之间有所不同。测试人员应该能够阐明预期的行为和实际行为。如果他无法为预期的行为提供支持,则没有任何要求,也没有错误-只是别人的意见。把它退回。
考虑预期行为错误的可能性。这可能是由于对该要求的误解。这也可能是由于需求本身的缺陷(详细需求和业务需求之间的差额)造成的。您也可以将这些寄回。
隔离问题。只有经验会教给您最快的方法-有些人几乎可以凭直觉做到这一点。一种基本方法是在使所有其他事物保持不变的同时改变一件事(问题是否发生在其他环境上?使用其他浏览器吗?在不同的测试区域中?在一天的不同时间?)另一种方法是查看堆栈转储或错误消息-有时您只能通过格式化的方式来判断系统的哪个组件引发了原始错误(例如,如果是德语,则可以责怪与您合作的第三方在柏林)。
如果将其范围缩小到两个可以协作的系统,请通过流量监控器或日志文件检查两个系统之间的消息,并确定哪个系统符合规范,哪个不符合规范。如果方案中有两个以上的系统,则可以执行成对检查并以“降低”应用程序堆栈的方式进行工作。
隔离问题之所以如此重要,是因为问题可能不是由于您可以控制的代码缺陷(例如第三方系统或环境)引起的,而是您希望让该方尽快接手。这既可以节省您的工作,又可以立即使它们对准目标,从而可以在尽可能短的时间内实现分辨率。您不想在某问题上工作十天,只是发现它确实与其他人的Web服务有关。
如果您确定确实存在缺陷,并且确实在您控制的代码中,则可以通过查找最后一个“已知良好”的构建并检查源代码管理日志中是否可能引起此问题的更改来进一步隔离问题。这样可以节省很多时间。
如果您无法从源代码管理中找出问题,那么现在该是连接调试器并逐步检查代码以解决问题的时候了。无论如何,现在您很有可能对这个问题有了一个很好的主意。
一旦知道了错误的所在并可以考虑修复程序,以下是修复此错误的好方法:
编写一个重现问题并失败的单元测试。
在不修改单元测试的情况下,使其通过(通过修改应用程序代码)。
将单元测试保存在测试套件中,以防止/检测回归。
当我们确实在代码中检测到错误时,该怎么做才能将其清除?我们应该如何处理它,以最有效地利用我们的宝贵时间,使我们花更少的时间去寻找它,而花费更多的时间进行编码?另外,调试时应该避免什么?
假设您处于生产环境中,则需要执行以下操作:
正确描述“错误”,并确定导致错误发生的事件。
确定“错误”是代码错误还是规范错误。例如,对于某些系统,输入1个字母名称可能被认为是错误,但对于其他系统则可以接受。有时,用户会报告他/她认为是问题的错误,但是用户对系统行为的期望并不属于要求的一部分。
如果您已证明存在错误并且该错误是由于代码引起的,则可以确定需要修复哪些代码段以防止错误。还检查行为对当前数据和将来的系统操作的影响(对代码和数据的影响分析)。
在这一点上,您可能会估计将花费多少资源来修复该错误。您可以立即对其进行修复,也可以计划在即将发布的软件版本中进行修复。这还取决于最终用户是否愿意为修复程序付费。您还应该评估其他可用选项以修复该错误。可能有不止一种方法。您需要选择最适合这种情况的方法。
分析导致此错误出现的原因(需求,编码,测试等)。强制执行可防止情况再次发生的过程。
充分记录该情节。
发布修复程序(或新版本)