花费太多时间进行调试


24

昨天,我发布了一个Web项目的v1.0版本,该项目花费了大约6周的时间(即打开和关闭)。我没有记录我的时间,但是根据我的经验,我估计在编程的全部时间中,有一半用于调试。我估计大约要花15到20个小时进行调试,这对我来说是宝贵的时间,本来可以更好地花费在编写新代码或更早完成项目上。特别是对我要在5周内上大学一年级的新生也无济于事。

事实是,我花所有的时间进行调试感到很难过。花在调试上的所有时间使我意识到在开发项目时我犯了一些非常愚蠢的错误,这些错误花费了我非常长的时间来修复。

如何防止这种情况将来发生?我不想花费50%的时间进行调试,而宁愿花费10%的调试时间,其余时间编写新代码。我可以尝试哪些技巧来帮助我实现这一目标?


22
大一的时候,我也是一个慢速的程序员。只要给它20年。
工作

27
恩,祝你好运。“如果调试是消除错误的过程。那么编程就必须是将它们放入的过程。” -Edsger Dijkstra
Matt

7
您从这些错误中学到了什么吗?如果这样做,则下次将不再使用它们,这将减少调试时间。
Craig T

5
这就是所谓的“经验”,它将帮助您进行下一个项目。

4
在谈到1940年代后期时,莫里斯·威尔克斯(Maurice Wilkes)写道:“一开始编程,我们就惊讶地发现,正确地编写程序并不像我们想象的那么容易。调试必须被发现。我在EDSAC会议室和打孔设备之间的旅行“在楼梯的角度上犹豫不决”,这使我意识到,余下的大部分时间都将花在寻找自己程序中的错误上。”
Trevor Powell

Answers:


35

您是在寻求软件工程的圣杯,但还没有人对这个问题有“答案”。

重要的是,您要跟踪所犯错误的类型,然后对这些错误进行分析,以确定是否存在共同趋势。根本原因分析是这种自省的正式名称,网络上有很多与此有关的材料。

专业人士使用错误跟踪系统,以便他们(1)知道需要修复的内容,而且(2)事后分析必须修复的内容。您不需要那么正式-仅在笔记本中保存理货对您来说就可以了。

设计阶段缺陷

如果您发现大多数错误是由于对问题陈述的误解引起的,或者您一直发现自己选择了错误的算法或解决问题的路径,那么在设计阶段就会遇到问题。

您应该在项目开始时花更多的时间,并确切指出需要完成的工作以及应该如何执行。请仔细检查此工作,并重新研究原始问题,并确定您是否真的以正确的方式解决了该问题。在开始时多花一三个小时可以为您节省许多时间。

编码错误

如果您的设计是可靠的,但是您一直在与您使用的编码语言作斗争,请给自己一些工具,这些工具可以为您分析代码,并尽早警告您,并且常常会犯错误。

如果您使用C语言进行编程,请打开所有编译器警告,使用像一样的语义检查器lint,并使用像valgrind来捕获与动态内存相关的常见问题的工具。

如果你的Perl编程,打开strictwarnings并留意它说什么。

无论您使用哪种语言,都可能存在许多工具,可以在您进入调试阶段之前帮助您捕获常见错误。

整合阶段的缺陷

在遵循良好的模块化实践开发代码时,必须开始将单独的部分粘合在一起。例如,代码的不同部分可能与用户输入,数据库交互,数据显示,算法/逻辑有关,并且每一个都是相对独立地构建的(也就是说,您倾向于专注于手头的部分)而不用担心与其他所有东西的集成)。

在这里,测试驱动开发(TDD)非常方便。您的代码的每个模块都可以具有测试,以验证它们是否按照设计方式工作。这些测试应该首先编写,也可以在过程的早期编写,以便您可以使用一组“助手”来保持诚实。当您开始使所有东西协同工作,并且发现您必须更改实现方式或与另一个子系统的交互方式时,您可以依靠测试来确保所做的工作所有这些一起工作不会破坏代码的正确性。

等等...

挑选一些有关软件工程和实用编码技术的书,您将学到许多使开发减少混乱并提高可靠性的不同方法。您还会发现,仅凭简单的旧经历-从硬碰硬的学校获得学位-也会使您恢复身材。

几乎所有内容都归结为,在开发/发布过程的后期,一点点时间和前期工作就能获得丰厚的回报。

您在职业生涯的早期就已经注意到这些问题,这对您的未来来说是一个好消息,我祝您好运。


1
这是一个很好的答案,但是恕我直言,这是一个截然不同的问题。OP表示我花了6周的时间来写东西,并且不得不花很多时间进行调试。我们对其产品的质量,可维护性,可扩展性一无所知。如果我们假设采用TDD,良好的设计,错误跟踪,那么仍然存在问题,即我们如何编写缺陷更少的代码(包括也需要调试的测试代码)。很好的建议是打开警告,使用皮棉等。更多来自学校的人吗?:-)
Guy Sirton

1
@Guy-是的... OP的问题有点含糊,这就是为什么我一直强调根本原因分析的原因。您不知道出了什么问题,直到您知道出了什么问题。我之所以要对问题领域进行调查,是因为我希望他知道许多潜在的陷阱,并且该过程的每个阶段都应该接受自己的检查。据我所知,他可能是下一个托尼·霍尔(Tony Hoare),但他拥有盲人大象的打字技巧-针对不同原因的不同解决方案。
Unpythonic 2011年

37

编写单元测试

为代码编写单元测试将迫使您考虑一下体系结构,并鼓励您将代码编写为细小,经过仔细控制且可测试的部分。这将大大减少您的调试工作,并且您执行的少量调试将仅限于紧凑的,紧凑的代码段。

另外,您编写的测试将“覆盖”您的代码。您将能够知道对代码所做的更改何时破坏了某些内容,因为现有测试中的一项或多项将失败。这降低了调试工作的总体复杂性,并增强了您对代码有效的信心。

当然,要注意的是,您花在调试上的时间现在花在了编写测试上。 但是您只需要编写一次即可,并且在编写它们之后可以根据需要执行多次。


单元测试+1-在开发过程的早期阶段,发现错误的成本更低,而且更容易修复。
Paul R

26

50%的调试(从广义上来说)还不错。与编写实际代码相比,人们通常花费更多的时间来设计,测试,修复错误,重构和编写单元测试。这是工作的一部分。

老实说,在维护编程中情况会更糟-很多时候,我会花一个小时来弄清楚到底出了什么问题,然后花五分钟编写代码来修复它,然后花半个小时来测试整个过程。略高于5%的编码,而几乎是95%的非编码。

不过,您可以采取以下措施来减少调试时间:

  • 编写可调试的代码。这意味着:适当的错误处理(考虑了一些问题),对代码进行结构化以使其易于遵循,使用断言,跟踪以及其他任何可以简化调试器工作的方式。避免复杂的线条;一行以上的内容应该分开,以便您逐一进行。
  • 编写可测试的代码。将您的代码分成简单的函数(或您选择的语言支持的任何其他函数);避免副作用,因为这些副作用很难在单元测试中捕获。设计功能,以便可以独立运行它们。避免使用多功能功能。避免边缘情况。记录您的功能应该执行的操作。
  • 编写测试。进行单元测试意味着您知道您的函数至少可用于其输入的一部分。这也意味着您需要进行健全性检查,以确保所做的更改不会破坏任何内容。确保您了解代码覆盖率和输入覆盖率的概念,以及单元测试的限制。
  • 设置一个“工作台”。具体如何执行取决于所使用的语言。某些语言,例如Python或Haskell,都带有交互式解释器,您可以将现有代码加载到其中以进行使用。这是完美的,因为您可以轻松地在任意上下文中调用函数,这是查找和隔离bug的宝贵工具。其他语言则没有这种奢侈,您必须诉诸编写小的交互式测试程序。
  • 编写可读的代码。养成编写代码以尽可能清晰地表达意图的习惯。记录所有不十分明显的内容。
  • 编写简单的代码。如果您自己的大脑在理解整个代码库时遇到困难,那么这并不简单,而且其他人也很难完全理解它。除非您了解代码的功能,否则无法有效地调试代码。
  • 轻按“删除”按钮。现在,您不需要的任何代码都属于垃圾箱。如果以后需要它,请从源代码管理中恢复它(经验表明,这种情况极为罕见)。处理的代码越多,调试范围就越小。
  • 尽早并经常重构。如果不进行重构,那么在添加新功能时就无法将代码保持在可调试状态。

1
同样,在出现问题的情况下,世界的行为可能与您预期的有所不同。这可能会导致非常细微的错误。

2
+1。我要说的是,仅花费50%的精力进行调试是相当低的,尤其是但不仅限于已建立的代码库中。如果我被分配了一个错误,除非它需要对代码的相关部分进行彻底的重写(不太可能),否则我可能花的时间要比总时间的比例大得多,以便找出问题所在,然后进行测试。修复程序本身通常很快,通常只需要更改一行代码即可。
的CVn

@ThorbjørnRavnAndersenHell是的,尤其是对于OP提及的Web项目。这周我们在工作中享受字符编码的美好时光……
Izkata 2012年

5

更多计划

不可避免的是,您将花费大量的时间进行调试(10%)是一个雄心勃勃的目标。尽管减少调试和开发时间的最佳方法之一是在计划阶段花费更多时间。

范围从图表到规划平台上的伪代码。无论哪种方式,您都将有更多的时间来考虑计划的工作,而不是在开发过程中犯下这些错误。


1
+1是因为这样做是为了减少调试时间。当我开始一个新项目时,我用注释写出我要做的所有事情,然后返回并用代码替换注释
CamelBlues 2011年

我对评论也做同样的事情,更多的是防止我忘记我离开的地方。但是我喜欢在纸上绘制类图及其依赖关系。这使我可以很好地了解当时的想法。
布莱恩·哈灵顿

5

更认真地工作

这相当于“两次测量一次”的软件:

  • 如果您感到分心或疲倦,请不要编码。
  • 花足够的时间思考问题,以便您获得干净而优雅的解决方案。简单的解决方案不太可能出现问题。
  • 全神贯注于任务。焦点。
  • 编码后快速阅读代码以尝试查找错误。自我代码审查。
  • 在编码和测试之间不要等待太久。立即反馈对于改进很重要。
  • 避免做通常会导致错误的事情。阅读代码气味
  • 选择适合该工作的工具。

综上所述,没有什么可以完全消除缺陷。您需要接受这是生活中的事实。考虑到这一事实,制定缺陷计划,例如单元测试。也不要将其理解为“永远采取”(又称分析瘫痪)。这是为了找到平衡。


4

其他答案已经涵盖了我想说的大部分内容,但是无论如何,我还是想给我我的(非常诚实的)观点:

基本上,对于不重要的软​​件工作,期望将绝大多数时间花费在维护和调试上。 如果您正在开发成熟的生产软件系统,并且在维护和调试上花费的时间少于80-90%,那么您的工作就做得很好!

现在显然,“维护”和“调试”之间的区别有点主观。您是否仅将“错误”视为代码发布后发现的问题,并且用户对此有所抱怨?还是一旦添加了某些东西(在自己的预发布测试阶段中找到),代码中的每件事都会有问题吗?在非平凡的软件系统中(取决于使用模式),一个可以比另一个大得多。但是无论如何,这比编写玩具“ Hello world”程序所需要的还要大,这需要大量的维护和调试。甚至有人说“ 在代码的第一行之后的所有内容都应该是“维护模式”,

TL; DR:在我看来,这听起来像是您可能对编程无关紧要的软件系统有什么了解有点不切实际。绝大部分的工作是微调,维护,重构,修复错误,以及通常会进行“调试”(维护)工作(至少从广义上来说),而不是进行全新的工作,编写新的代码。


2

如果没有关于您正在做的事情和正在使用的技术的具体细节,很难给出具体的技术。但是,即使是非常优秀的编码人员,也要花费大量时间进行测试和调试。

有很多编写良好代码而没有很多错误的经验。您犯了错误,然后解决了这些错误,然后记住了错误是什么,而必须做些正确的事情,而下次又不会犯同样的错误。而且,如果您甚至还没有上大学,并且已经开始认真考虑减少错误的方法,那么我想您肯定会领先一步。


1
我看到的那些没有从错误中吸取教训的人(或者不愿记住自己所学到的东西)使我感到惊讶。然后,当事情突然大爆发之后,他们转身去做下一个项目中完全相同的事情。
HLGEM'3


1

实际上,要减少调试,您可以通过更深入的计划来提前加载它。还没上过大学吗?我想您会在大学中后期学习到涵盖软件开发生命周期的详细信息,这些细节可能会给您带来很多启发。

当我尝试向雇主解释时,减少代码维护和技术支持的最佳方法是花时间预先全面计划代码。


1

测试驱动的开发可以通过以下方式帮助减少调试时间:

  • 进行大量的小型集中测试意味着如果一个测试失败,则只有少量的代码可能会引起问题。
  • 一步一步地工作(通过编写失败的测试,然后使其通过)意味着您能够一次专注于一项任务。也就是说,使当前测试过去。
  • 通过测试后进行重构会鼓励您保持代码的清晰和易于理解-如果出现问题,则更易于遵循。

即使您确实使用了TDD,您仍然需要使用调试器。发生这种情况时,应尝试编写单元测试以重现导致调试会话的方案。这将确保如果再次发生该问题,则在测试失败时将迅速发现该问题,并且该测试将充当导致该问题的代码区域的标记-减少调试的需要。


1

调试在编程中是不可避免的,但关键是您的代码是否易于调试?如果您只需要花费几个小时来调试一些简单的东西,那么您的代码体系结构肯定存在某些问题。

您应该习惯于编写干净的代码,并消除不良习惯,例如复制粘贴代码和编写长方法等。

另外,您应该不时重构代码。我建议您阅读Martin Fowler的书:重构:改进现有代码的设计


1

其他人提到了测试和代码审查。它们都非常有用,但是有一个关键的区别-什么时候最好执行它们。最好在接近最初编写代码的位置进行测试,因此,您可以更轻松地记住为什么以某种方式进行操作,并且在测试失败时可以更快地定位问题。另一方面,最好在稍后进行代码审查。您希望在没有完美回忆的情况下查看代码,以免掩饰您曾经想过但没有放入的细节。您想了解代码不清楚的地方。您需要花费一点额外的精力来弄清楚代码在做什么。您希望能够应用所获得的有关问题的任何新知识或与其他代码或新技术的交互。基本上,

不过,所有这些仍然与您的问题相切。为了减少调试时间,您必须首先了解为什么要进行调试。对问题的误解,对工具和技术的了解不完善,以及仅仅碰到“实际数据与样本数据不匹配”的问题类型都将以不同的方式表现出来,并且需要不同的技术和实践类型来避免在将来。

我要讲的最后一点是经验。没有简单的方法可以做到这一点,您只需要花时间。随着经验的积累,您将花费更少的时间进行调试,因为您将开始编写更好的代码,更早地发现问题,并更好地了解问题的根源。坚持下去,您将在整个职业生涯中稳步增长。


0

上面有很好的答案,但没有人直接提及(尽管大多数都在此暗示):

在nauseam上阅读READ READ READ等...

您知道的越多,您所知道的就越少。有点陈词滥调,但仍然是基本事实。

在遵循了上述技巧并分析性地记录了错误之后,请尝试对它们进行分类,然后阅读相关文献。

这是设计决策问题吗?阅读设计模式。

是否缺乏对框架或语言的了解?相信这一点!

等等

一个(实时的)开发人员永远无法逃避的两件事:更改(IT中唯一的常数)和RTFMing ...


0

单元测试和断言

在可能的情况下,将您的代码分解成小段,可以单独进行测试。但是,这并不总是可行的。一些功能依赖于极其复杂的输入。有些操作无法自动进行验证,例如将内容绘制到屏幕上。有时涉及不确定性,等等。

当您不能编写好的单元测试时,下一个最好的事情就是断言。当单元测试检查您是否在某些预定输入上得到正确答案时,断言检查实际输入中的中间步骤是否合理。如果您的代码中有错误,它将迅速失败,靠近问题的根源并带有清晰的错误消息,而不是远离带有模棱两可的错误消息的问题。此外,声明文档假设并使您的代码更具可读性。


0

当您开始一个项目时,您确定几种替代方法?

您是否有两种到四种不同的方法,每种方法各有利弊?然后您从其中进行合理选择吗?

然后,最重要的是,您是否认为简单性非常重要?

根据我的经验,我问的原因是,一种设计方法与另一种设计方法之间的代码量以及错误数量(更不用说性能)的变化可能相差一个数量级以上。我看到经验丰富的人员正在做的事情是,仅用不需要的代码即可完成工作。

他们完全有能力并且了解所有数据结构算法,面向对象语言的功能等,但是他们的代码看起来好像不是,因为如果问题确实存在,他们会很少使用或根本不使用这些东西。不需要他们。


0

每次修复错误时,都希望避免再次犯同样的错误。为此,您可以执行以下操作:

  • 将其记录在缺陷记录日志中,其中包括:

    • 缺陷类型
    • 注入缺陷的阶段
    • 删除阶段
    • 修复时间
    • 问题描述和解决方法
  • 采用样式指南来规范您编写的代码的样式

  • 将安全的编码规则集成到您的代码审查过程中

  • 可视化控制流数据

参考文献

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.