编码时如何减少错误数量?


30

没有人能做到完美,无论我们做什么,我们都将不时产生包含错误的代码。在编写新软件和更改/维护现有代码时,有哪些方法/技术可以减少产生的错误数量?


一个好的方法是进行更多的前期设计(不是太多,但足以使您的代码更结构化和更易于理解)。
乔治

Answers:


58

避免花哨的编码。代码越复杂,出现错误的可能性就越大。通常在现代系统上,编写清晰的代码将足够快且足够小。

使用可用的库。没有编写实用程序例程的错误的最简单方法是不编写实用程序。

学习一些更复杂的东西的正式技巧。如果情况复杂,请用笔和纸钉住它们。理想情况下,了解一些证明技术。如果我能证明代码正确,那么除了容易解决的大而笨拙的明显错误外,几乎总是好的。显然,这只是到目前为止,但是有时您可以正式地推理一些小而复杂的事情。

对于现有代码,学习如何重构:如何经常使用自动化工具在代码中进行小的更改,以使代码在不更改行为的情况下更具可读性。

不要做得太快。花一点时间做正确的事情,检查您所做的事情,并思考您在做什么,这可以在以后获得大量的回报。

编写代码后,请使用所需的功能使其变得更好。单元测试很棒。您通常可以提前编写测试,这可能是一个很好的反馈(如果始终如一,这是测试驱动的开发)。编译警告选项,并注意警告。

让其他人看一下代码。正式的代码审查很好,但是可能不方便。拉取请求(如果您的scm不支持它们,则类似)允许异步查看。好友检查可能不太正式。配对编程可确保两双眼睛观看所有内容。


x2-瑞安说的话。
JBRWilkinson

2
大多数语言也可能会有些挑剔。您希望它尽可能挑剔。

1
“例如,学习一些更复杂的东西的正式技巧。” ...例如?
Dan Rosenstark 2010年

1
@Yar:我希望能像本书中解释的系统一样:amazon.com/Verification-Sequential-Concurrent-Programs-Computer/…;尽管我不得不说特定的书非常干燥和枯燥,所以那里可能有更好的书(但这是我读过的唯一一本书)。
Joeri Sebrechts

30

通过单元测试,您可以减少第二次弹出的错误的数量。如果在代码中发现错误,则编写单元测试将确保以后不会再次出现。(此外,有时很难考虑所有情况并预先编写数千个单元测试)


3
“预先考虑所有情况”会产生干净的,完全指定的界面,这仅仅是一件好事。仅当您将单元测试改编为并非出于测试目的而设计的代码时,才很难编写单元测试。
Mike Seymour 2010年

1
如果可以,应该。不幸的是,我去过的大多数地方都将单元测试视为不仅仅是“快速修复错误”而花费更多的东西。因此,如果可以的话,您应该预先编写测试,但是如果它不被认为是“具有成本效益的”,那么将其与错误修复一起编写可帮助您逐步建立起来,而不必花费太多预算来编写单元测试。
瑞安·海斯

4
“测试表明存在缺陷,而不是没有缺陷。” -E. Dijkstra。话虽如此,自动化测试绝对是维护和提高软件质量的非常有用的方法。
limist 2010年

9

在两个单元测试注释上均为+1。

除此之外,设置编译器提供的最高警告级别,并确保将警告视为错误。错误通常隐藏在那些“错误的”错误中。

同样,请投资在编译时运行的静态分析工具(我将其视为编译器警告的额外级别)。


+1为静态分析注释。免费获得所有这些信息非常宝贵:)
Morten Jensen

9

除了提到的内容:

  • 不要忽略错误代码-例如,不要以为您得到了有效的结果,是否已经成功创建了文件,等等。因为有一天,某些事情会发生。
  • 不要以为您的代码永远不会输入某些条件,因此“忽略该条件是安全的”。
  • 测试您的代码,然后让其他人对其进行测试。我发现我是测试我自己的代码的最糟糕的人。
  • 休息一下,然后重新阅读代码,看看您是否“错过了明显的事情”。经常发生在我身上。

我目前忘记的许多其他事情,但其他人肯定会想到。:)


7
如果你是这样确定的条件X不会发生......用一个断言,以确保条件发生X,你会知道它(通过一个异常,或记录,或其他)。
Frank Shearar 2010年

@MetalMikester:单元测试很好。但是,由于使用了高级语言和良好的库,大多数硬错误都需要进行集成和回归测试。
矢量

9

尽管我的主要语言是C ++和Python,但我已经开发了一种相当实用的编程风格。我发现,如果将所有上下文传递给该函数完成其工作所需的函数(或方法),并返回我要查找的有意义的数据,则我的代码将变得更加健壮。

隐性状态是敌人,根据我的经验,错误是第一大漏洞来源。此状态可以是全局变量或成员变量,但是如果结果取决于未传递给函数的内容,则您会遇到麻烦。显然,消除状态是不可行的,但是将其最小化会对程序可靠性产生巨大的积极影响。

我还想告诉我的同事,每个分支(如果同时是?:)都是一个可能的错误。我不能说这个bug的表现是什么,但是您的代码所具有的条件行为越少,仅由于执行期间的代码覆盖范围将更加一致这一事实,就越有可能实现无bug。

顺便说一句,所有这些都对性能也有积极影响。赢得!


以我的经验,如果方法尽可能小,每次调用都要遍历所有状态,这很快就会变得很乏味。通过使用许多具有较短对象生存期的小型不变类可以解决此问题。这样,您可以将临时状态存储为字段,并在不再需要状态时丢弃该对象。:-)
约尔根·福格

这种情况变得乏味的另一个考虑因素是,也许您正试图传递过多的状态。:)
dash-tom-bang

在许多情况下是正确的,但事实并非如此。在某些域中,即使您没有很多可变状态,您也需要访问很多状态。我目前正在使用代码生成器,需要访问几个符号表。我不想将它们传递给每种方法。
约根·福

8
  • 编写更少的代码,做更多的事情。
  • 考虑一下低层次的含义和高层次的后果
  • 考虑在代码中创建的抽象。
  • 如果可能,仅写出基本的复杂性。

我打算写一篇大文章,总结为“少写多做”(IOW知道并使用可用的工具)。我只会对此+1。
卡兹巨龙

1
但是,在尝试减少代码量时,请注意不要获得多余的代码。
gablin

1
@gablin:在许多方面,情人眼中的情人。我今天可以编写和阅读本可以在4年前感到震惊的代码。今天的Haskell对我来说很奇特。:)
Paul Nathan

8

技术上的回答要少一些:在您累了(每天9小时/天),醉酒或“烤”时不要编程。当我很累的时候,我没有足够的耐心来编写干净的代码。


2
大多数程序员通常要花几年的时间才能意识到这一点。这很重要。
杰夫·戴维斯


5

这里有一些关于单元测试和工具的很好的答案。我唯一可以添加给他们的东西是:

尽早让测试人员参与

如果您有测试团队,请不要陷入将他们视为代码质量的守门人并为您发现缺陷的陷阱。相反,与他们合作并尽早让他们参与进来(在敏捷项目中,这将从项目开始就开始,但是如果我们真的尝试的话,我们总是可以找到更早让他们参与的方法)。

  • 找出他们的测试计划是什么。与他们一起检查他们的测试用例-您是否用代码覆盖了它们的全部?
  • 要求他们了解要求。和你一样吗
  • 给他们早期的工作版本进行探索性测试-您会对他们建议的改进感到惊讶。

与测试人员保持良好的工作关系意味着您可以尽早发现错误的假设和缺陷,然后再对他们造成任何损害。这也意味着测试人员有足够的能力来协助产品设计并在有时间修复它们时发现可用性问题。


4

静态分析工具

诸如FindBugs之类的插件和应用程序会抓取您的代码,并查找可能存在错误的地方。没有初始化和使用变量的地方,或者只是疯狂的事情(十分之九)的地方,这使得更容易出现错误。像这样的工具可以帮助我阻止骨头走下坡路,即使这还不是bug。

PS:请记住要始终研究为什么工具会告诉您一些不好的东西。永远不会伤害学习(并非在所有情况下都适用)。



2

除了此处的所有其他建议外,还应将所有可能的警告设置为最高灵敏度,并将其视为错误。还可以使用该语言具有的所有整理工具。

您会惊讶于警告会捕获多少简单错误,以及其中有多少简单错误转化为代码中的实际错误。


2

这里有很多好的答案,但我想补充一些内容。确保您真正了解该要求。当用户认为需求指的是X而程序员认为需求指的是Y时,我已经看到了很多错误。请后退以澄清较差或模棱两可的需求。我知道我们所有人都喜欢加入代码,但是花在确保理解上的时间越多,返工和错误修复就越少。

了解您所支持的业务后,您将经常看到需求中遗漏的东西,然后需要进一步的解释。知道如果您按照说明执行任务Y,它将破坏现有功能Z。

了解您的数据库结构。许多错误是由于语法正确而返回错误结果的查询的结果。了解如何识别结果有趣的时候。如果我要编写一个复杂的报表查询,我总是会找一个技术专家来审查我的结果,然后再将其标记为可以使用,他们将不可避免地看到我遗漏的数据中的某些内容。然后给自己做一个记录,让他们记下他们没有发现的东西,并记住下次您做类似​​的事情。


1

我认为最重要的技术是花时间。如果您觉得需要两天的时间来编写一个新模块,但是老板强迫您只在一天之内进行编码,那么您的代码可能会出现更多错误。

我前一段时间读过的一本书中说过,您不应该在窗户破损的情况下生活,因为人们不会在乎别人是否破损……编码是一样的,每个人都会在意做坏事的第一人但是速度很快,但是没有人会关心一个地狱代码,有很多错误,而且设计和样式也很差。


1

我遵循“测试代码测试”而不是“代码测试代码测试”的做法。这可以帮助我考虑用例并适当地构建逻辑


1

使用诸如ReSharper之类的代码检查工具或诸如IntelliJ IDEA之类的IDE ,它们通过指出“已写入但从未读取”的变量来警告许多复制和粘贴错误以及其他错误。节省了我很多时间。


1

令人惊讶的是,尚未提及以下三个非常重要的点:

  • 自由地使用断言。您应该一直问自己的问题不是“我应该坚持吗?” 但是“我有什么要断言的吗?”

  • 选择不变性。(自由地使用final / readonly。)您所拥有的可变性越少,出错的事件就越少。

  • 不要过早优化。许多程序员都关注性能问题,这导致他们不必要地使代码复杂化并混搭了设计,甚至没有事先知道性能是否会成为问题。首先,不管性能如何,以学术方式构建您的软件产品;然后,看看它是否表现不佳;(可能不会。)如果存在任何性能问题,请转到一两个地方,您可以在这些地方提供漂亮而正式的算法优化,以使您的产品满足其性能要求,而不必对整个代码库进行调整和修改。在这里和那里挤压时钟周期。

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.