随着代码变得越来越复杂,都有哪些好的策略可以修复错误?
Posted
技术标签:
【中文标题】随着代码变得越来越复杂,都有哪些好的策略可以修复错误?【英文标题】:What are some good strategies to fix bugs as code becomes more complex?随着代码变得越来越复杂,有哪些好的策略可以修复错误? 【发布时间】:2009-02-13 04:16:03 【问题描述】:我“只是”一个业余程序员,但我发现随着我的程序变得越来越长,错误变得越来越烦人——而且更难跟踪。就在一切看似顺利的时候,一些新的问题会出现,似乎是自发的。我可能需要很长时间才能弄清楚导致问题的原因。其他时候我会添加一行代码,它会破坏另一个单元中的某些内容。如果我认为一切正常,这可能会令人沮丧。
这对每个人来说都是常见的,还是更像是一种新手?我听说过“单元测试”、“设计框架”和其他各种听起来会减少错误、使我的应用程序“健壮”的概念,并且所有内容一目了然:)
那么,对于受过专业培训的人来说,错误有多大?
谢谢——阿尔·C。
【问题讨论】:
【参考方案1】:“进行修复,在别处引起问题”的问题众所周知,并且确实是单元测试背后的主要动机之一。
这个想法是,如果您为系统的每个小部分独立编写详尽的测试,并在每次您在任何地方进行更改时在整个系统上运行它们,您将立即发现问题。然而,主要的好处是,在构建这些测试的过程中,您还将改进代码以减少依赖关系。
这类问题的典型解决方案是减少耦合;减少不同的部分相互依赖。更有经验的开发人员有时具有以这种方式构建系统的习惯或设计技能。例如,我们使用接口和实现而不是类;我们将模型-视图-控制器用于用户界面等。此外,我们可以使用有助于进一步减少依赖关系的工具,例如“依赖注入”和面向方面的编程。
所有的程序员都会犯错。优秀且经验丰富的程序员构建他们的程序,以便更容易发现错误并限制其影响。
这对每个人来说都是一件大事。大多数公司花在维护上的时间多于编写新代码的时间。
【讨论】:
呵呵,作为一名支持工程师,我将其修改为“大多数公司应该在维护上花费更多时间而不是编写新代码”;)不幸的是,新功能销售产品,所以他们在我的经验中得到优先考虑 就像病毒一样,bugs can cause an infection 到代码的其他部分。【参考方案2】:您是否在自动化测试?如果您不这样做,那么您就是在注册创建错误而没有找到它们。
您是否在修复错误时添加测试?如果您不这样做,您就是在注册一遍又一遍地创建相同的错误。
您是否在编写单元测试?如果没有,您将在测试失败时注册长时间的调试会话。
您是否在编写单元首先进行测试?否则,当您的单元紧密耦合时,您的单元测试将很难编写。
你是在无情地重构吗?否则,每次编辑都会变得更加困难,并且更有可能引入错误。 (但首先要确保你有好的测试。)
当你修复一个错误时,你是在修复整个班级吗?不要只是修复错误;不要只修复整个代码中的类似错误;改变游戏,这样你就不会再制造这种错误了。
【讨论】:
这个答案有点过于(宗教?)专注于单元测试和测试驱动设计。这些做法只是使代码更有可能按照您的意图执行,但并不意味着您的软件最终是正确的。 我同意 OP 和上述两个 cmets。虽然我是 TDD 的忠实拥护者,但它不是灵丹妙药,我认为平衡测试套件的实用性和现实生活的合理性很重要。否则,您最终会得到不经过大量工作就无法更改的代码。 记住这家伙是个业余爱好者。 @Nick:你从他作为业余爱好者中得出什么结论?也许是因为他没有测试人员,所以他必须自己找到所有的错误,所以他需要好的自动化测试。【参考方案3】:错误对每个人来说都很重要。我一直发现,我编程的越多,我对编程的了解就越多。我对几年前写的代码感到畏缩!!我一开始是个业余爱好者,非常喜欢它,所以我去了工程学院攻读计算机科学工程专业(我在最后一个学期)。这些是我学到的东西:
我会花时间实际设计我要编写的内容并记录设计。它确实消除了很多问题。无论设计是简单地写下我将要写的内容还是完整的 UML 建模 (:() 都无关紧要。重要的是思想和目的的清晰性以及当我来的时候有材料可以回顾一段时间后回到最重要的代码。
无论我用什么语言编写代码,保持代码简单易读都很重要。我认为不要过度复杂化代码同时不要过度简化它是非常重要的。 (惨痛的教训!!)
效率优化和花哨的技巧应该在最后应用,只有在必要时才需要。另一件事是,只有当我真的知道我在做什么并且我总是测试我的代码时,我才会应用它们!
了解与语言相关的详细信息有助于我保持代码无错误。例如,我了解到 scanf() 在 C 中是邪恶的!
其他人已经评论了编写测试的禅宗。我想补充一点,您应该始终进行回归测试。 (即编写新代码,测试代码的所有部分以查看是否中断)
有时很难在脑海中记住代码,所以我总是记录我的代码。
我使用方法来确保我的代码的不同部分之间存在最低限度的依赖关系。接口、类层次结构等(解耦设计)
在编写代码之前进行思考并在编写任何内容时保持自律是另一项关键技能。我认识一些不格式化他们的代码的人,所以它的可读性(颤抖!)。
阅读其他人的资源以学习最佳实践是很好的。制作我自己的清单更好!在团队中工作时,必须有一组共同的。
不要被分析所迷惑。编写测试,然后编写代码,然后执行和测试。漂洗重复!
学习通读我自己的代码并梳理错误非常重要。提高我的调试技能是一项巨大的投资。我通过帮助我的同学定期修复错误来保持他们的敏锐度。
当我的代码中有错误时,我认为是我的错误,而不是计算机并从那里开始工作。这种心态对我很有帮助。
一双新的眼睛有助于调试。程序员在精疲力竭时往往会错过自己代码中最明显的错误。有人向您展示您的代码非常棒。
有人提出想法而不被评判很重要。我和我妈妈(不是程序员)交谈,向她提出想法并找到解决方案。她帮助我来回调整我的想法并完善它们。如果她不在,我会和我的宠物猫交谈。
我不再因错误而气馁。我学会了像编程一样热爱消除错误。
使用版本控制确实帮助我管理在编码时获得的不同想法。这有助于减少错误。我建议使用 git 或您可能喜欢的任何其他版本控制系统。
正如 Jay Bazzuzi 所说 - 重构代码。我只是在阅读他的回答后添加了这一点,以保持我的列表完整。所有功劳归他所有。
尝试编写可重用的代码。重用您的代码和库中的代码。使用无错误的库来执行一些常见任务确实可以减少错误(有时)。
我认为下面这句话说得最好——“如果调试是消除错误的艺术,那么编程一定是把它们放进去的艺术。”
不反对任何不同意的人。我希望这个答案有帮助。
注意 正如其他人 Peter 指出的那样,如果您要编写大量代码,请使用面向对象编程。代码长度是有限制的,如果以程序方式编写,代码长度将变得越来越难管理。我喜欢小东西的程序化,比如玩算法。
【讨论】:
【参考方案4】:有两种方法可以编写无错误的程序;只有第三个有效。 ~Alan J. Perlis
在程序中发生错误的唯一方法是被作者放在那里。没有其他机制是已知的。程序无法通过与其他有缺陷的程序坐在一起来获取错误。 ~哈伦米尔斯
【讨论】:
我以前从未听说过玻璃市的那句名言——它在我的收藏夹中。 虽然 Harlan 的引述有点准确,但如今它的软件依赖于如此多的外部组件,它很可能通过与其他有缺陷的程序 IMHO 坐在一起来获得错误。 这并不完全正确。随着时间的推移,由于存储介质上的静默错误,程序也可能被破坏和出错。这就是为什么有一个研究领域专注于corruption resilient data structures 和程序。【参考方案5】:显然,错误对任何程序员来说都是一件大事。只需查看 Stack Overflow 上的问题列表即可查看说明。
业余爱好者和经验丰富的专业人士之间的区别在于,专业人士将能够利用他的经验以更“防御性”的方式编写代码,首先避免许多类型的错误。
【讨论】:
作为一个业余程序员,我不认为我或多或少有“防御性”——即使你只是为了好玩而做一些事情,你仍然可以尝试把它做好。并非所有的爱好者都缺乏经验。 你是对的——更好的区分是“没有经验”和“有经验”。 (哎呀,尽管我几乎获得了计算机科学硕士学位,但我这辈子从来没有为钱写过一行代码——但我不会说自己没有经验。) 我同意 Thomas 的观点,但如前所述,不幸的措辞除外。那里有一些可怕的,可怕的专业人士! :-)【参考方案6】:所有其他答案都很棒。我要补充两点。
-
源代码控制是强制。我假设你在这里的窗户上。 VisualSVN Server 是免费的,点击 4 次即可安装。 TortoiseSVN 也是免费的,它集成到 Windows 资源管理器中,绕过了 VS Express 没有插件的限制。如果您创建了太多错误,您可以还原您的代码并重新开始。如果没有源代码控制,这几乎是不可能的。此外,如果您有笔记本电脑和台式机,还可以同步您的代码。
人们会推荐许多技术,例如单元测试、模拟、控制反转、测试驱动开发等。这些都是很好的做法,但不要试图将它们全部塞进脑海。您必须编写代码才能更好地编写代码,因此请慢慢将这些技术运用到您的代码编写中。你必须先爬,然后才能走,走后才能跑。
祝您在编码冒险中好运!
【讨论】:
【参考方案7】:这是一个常见的新手事情。当然,随着您获得更多经验,您仍然会遇到错误,但它们会更容易找到和修复,因为您将学习如何使您的代码更加模块化(这样更改一件事就不会产生连锁反应)其他任何地方),如何测试它,以及如何构建它以快速失败,靠近问题的根源,而不是在某个任意的地方。不需要复杂的基础设施来实现的一件非常基本但有用的事情是检查所有函数的输入,这些函数具有断言的非平凡前提条件。这在我可能会遇到奇怪的段错误和几乎不可能调试的任意行为的情况下为我节省了好几次。
【讨论】:
【参考方案8】:如果 bug 不是问题,那么我可以在 10 分钟内编写一个 100,000 行的程序!
您的问题是,“作为一名业余医生,我担心患者的健康:有时当我不够小心时,他们会生病。患者的健康对你们专业医生来说也是一个问题吗?”
是的:这是核心问题,甚至是唯一的问题(对于任何足够全面的“错误”定义)。
【讨论】:
【参考方案9】:错误对每个人都很常见——无论专业与否。
项目越大越分散,越要小心。查看任何开源错误数据库(例如:https://bugzilla.mozilla.org/)都会为您确认这一点。
软件行业已经演变出各种programmingstyles 和standards,如果使用得当,可以更容易地发现错误代码或限制其影响。
因此,培训对代码质量有非常积极的影响……但归根结底,错误仍然潜伏着。
【讨论】:
【参考方案10】:如果您只是一个业余程序员,学习完整的 TDD 和 OOP 可能需要比您愿意投入的更多时间。因此,假设您不想在它们上投入时间,一些易于理解的减少错误的建议是:
让每个函数只做一件事。怀疑超过 10 行的函数。如果您认为可以将其分解为两个功能,那么您可能应该这样做。可以帮助您控制这一点的方法是根据函数的确切功能命名您的函数。如果你发现你的名字又长又笨重,那么你的功能可能做的事情太多了。
将魔术字符串变成常量。也就是说,而不是使用:
人[“妈妈”]
改用
var mom = "mom";
people[mom]
-
设计您的函数以执行某事(命令)或获取某事(查询),但不能同时执行。
这里是http://www.holub.com/publications/notes_and_slides/Everything.You.Know.is.Wrong.pdf 对 OOP 的简短易懂的介绍。如果你明白了这一点,那么你就掌握了 OOP 的精髓,并且坦率地说,你领先于许多专业程序员。
【讨论】:
【参考方案11】:普遍的看法似乎是,普通程序员每 1000 行代码会产生 12 个错误 - 取决于您要求的确切数量,但始终是每行代码 - 因此,程序越大,错误越多错误。
低于标准的程序员往往会制造更多的错误。
新手经常被语言的特性所困,缺乏经验也容易出现更多错误。随着你的进步,你会变得更好,但你永远不会创建无错误的代码......好吧,即使在 30 年后,我仍然有错误,但那可能只是我。
【讨论】:
唯一没有错误的程序是一个空的源文件。即使这样编译器也可能崩溃。【参考方案12】:从专业人士到业余爱好者,每个人都会遇到讨厌的错误。真正优秀的程序员会被要求追踪真正令人讨厌的错误。这是工作的一部分。当您盯着一个讨厌的错误两天并且沮丧地大喊“谁写了这个废话!?!?”时,您会知道您已经成为一名软件开发人员。 ......才意识到是你。 :-)
软件开发人员的部分技能是能够将大量相互关联的项目直接记在脑海中。听起来你正在发现当你的系统心理模型崩溃时会发生什么。通过练习,您将学会设计感觉不那么脆弱的软件。有大量关于软件设计主题的书籍、博客等。当然还有 Stack Overflow 的具体问题。
说了这么多,以下是您可以做的几件事:
-
一个好的调试器是无价的。通常,您必须逐行检查代码才能找出问题所在。
如果对您的项目有意义,请使用 Python 或 Java 等垃圾收集语言。 GC 将帮助您专注于使事情正常进行,而不是被令人发狂的内存错误所困扰。
如果您编写 C++,请学会爱上 RAII。
编写大量代码。软件在某种程度上是一种艺术形式。多多练习会让你做得更好。
欢迎来到 Stack Overflow!
【讨论】:
【参考方案13】:真正改变我对代码复杂性和错误的几率是使用编码标准 - 如何放置括号等等。它可能看起来只是无聊和无用的东西,但它确实统一了所有代码并使其更易于阅读和维护。那么您使用编码标准吗?
【讨论】:
【参考方案14】:如果您组织得不好,您的代码库将成为您自己的Zebra Puzzle。添加更多代码就像在你的拼图中添加更多的人/动物/房子,很快你的拼图中就有 150 种不同的动物、人、房子和香烟品牌,你意识到你只花了一周时间就添加了 3 行代码,因为一切都是如此相互关联,以至于需要很长时间才能确保代码仍然按照您希望的方式执行。
最流行的组织范式似乎是Object Oriented Programming,如果你能将你的逻辑分解成可以相互独立构建和使用的小单元,那么你会发现错误发生时的痛苦要小得多。
【讨论】:
以上是关于随着代码变得越来越复杂,都有哪些好的策略可以修复错误?的主要内容,如果未能解决你的问题,请参考以下文章