软件开发最佳实践_30种软件开发和测试最佳实践

软件开发最佳实践

加入具有既定文化和编程实践的任何新公司,都是艰巨的经历。 当我加入Ansible团队时,我决定写下我多年来努力学习的软件工程实践和原则。 这是不明确,不详尽的原则清单,应明智而灵活地应用。

我对测试充满热情,因为我相信良好的测试实践既可以确保最低质量标准(在许多软件产品中严重缺乏),又可以指导和塑造开发本身。 这些原则中有许多与测试实践和理想有关。 其中一些原则是特定于Python的,但大多数不是。 (对于Python开发人员, PEP 8应该是您掌握编程风格和准则的第一站。)

总的来说,我们程序员是一个自以为是的人,强烈的意见通常是热情的征兆。 考虑到这一点,请随时不同意这些观点,我们可以在评论中讨论和辩论它们。

开发和测试最佳实践

1. YAGNI:“ 您不会需要它 ”。 不要编写您认为将来可能需要的代码,但是现在还不需要。 这是针对虚构的未来用例进行编码的,并且不可避免地,该代码将变为无效代码或需要重写,因为未来用例的工作原理总是与您的想象有些不同。

如果您将代码放入将来的用例中,我将在代码审查中提出疑问。 (例如,您可以而且必须设计API,以允许将来的用例,但这是一个不同的问题。)

注释掉代码也是如此; 如果有一段注释的代码即将发布,则它不应该存在。 如果是可以恢复的代码,请制作工单并引用提交哈希以删除代码。 YAGNI是敏捷编程的核心元素。 最好的参考是Kent Beck 撰写的《极限编程说明》

2.测试不需要测试。 用于测试的基础架构,框架和库需要测试。 除非确实需要,否则不要测试浏览器或外部库。 测试您编写的代码,而不是其他人的代码。

3. 第三次编写同一段代码是将其提取到通用帮助程序(并为其编写测试)的正确时机。 测试中的辅助功能不需要测试; 当您将它们分解并重复使用时,它们确实需要测试。 到第三次编写类似的代码时,您往往对要解决的通用问题的形状有了清晰的认识。

4.关于API设计(外部接口和对象API): 简单的事情应该很简单; 复杂的事情应该是可能的 。 如果可能的话,请首先针对简单情况进行设计,最好使用零配置或参数化。 为更复杂,更灵活的用例(根据需要)添加选项或其他API方法。

5.快速失败。 请检查输入,并尽早在无意义的输入或无效状态下失败,最好是带有异常或错误响应,以使呼叫者清楚地知道确切的问题。 但是,允许使用代码的“创新”用例(即,除非确实需要,否则不要进行类型检查以进行输入验证)。

6.单元测试是针对行为单位而不是执行单位进行测试。 目标是更改实现,而不更改行为或不必更改任何测试,尽管并非总是可能的。 因此,在可能的情况下,请将您的测试对象视为黑盒,通过公共API进行测试,而无需调用私有方法或修改状态。

对于某些复杂的场景(例如,在特定的复杂状态下测试行为以发现模糊的错误),这可能是不可能的。 首先编写测试确实对此有所帮助,因为它迫使您在编写代码之前考虑代码的行为以及如何进行测试。 测试首先鼓励使用更小,更模块化的代码单元,这通常意味着更好的代码。 肯特·贝克(Kent Beck)的“ 示例驱动测试开发 ”( Test Driven Development by Example)是开始使用“测试优先”方法的一个很好的参考。

7.对于单元测试(包括测试基础结构测试),应测试所有代码路径。 100%的覆盖率是一个不错的起点。 您无法涵盖状态的所有可能排列/组合(组合爆炸),因此需要考虑。 仅在有充分理由的情况下,才应测试代码路径。 缺乏时间不是一个好理由,最终会花费更多时间。 可能的良好原因包括:真正无法测试(以任何有意义的方式),在实践中无法击中或在测试的其他地方涉及。 没有测试的代码是责任。 衡量覆盖率并拒绝降低覆盖率的PR是确保您朝正确方向逐步发展的一种方法。

8.代码是敌人:代码可能会出错,并且需要维护。 编写更少的代码。 删除代码。 不要编写不需要的代码。

9.随着时间的推移,代码注释不可避免地成为谎言。 实际上,当事情发生变化时,很少有人会更新评论。 力求通过良好的命名习惯和已知的编程样式来使代码具有可读性和自记录性。

不能使代码变得显而易见的代码(围绕一个模糊的错误或不太可能出现的情况,或必须进行的优化) 确实需要注释。 评论代码的意图 ,以及为什么要做某事而不是做什么。 (顺便说一句,关于注释是谎言的这一特殊点是有争议的。我仍然认为这是正确的, 《编程实践》的作者Kernighan和Pike同意我的看法。)

10.防御性写作。 始终考虑可能出问题的地方,无效输入会发生什么以及可能会失败哪些,这将帮助您在发生许多错误之前就将它们捕获。

11.如果逻辑是无状态且无副作用的,则易于进行单元测试。 将逻辑分解为单独的功能,而不是将逻辑混合为有状态的和充满副作用的代码。 将有状态代码和具有副作用的代码分成较小的函数,可以使它们更容易被模拟和进行单元测试而没有副作用。 (较少的测试开销意味着可以更快地进行测试。) 确实需要进行副作用测试,但是对它们进行一次测试并在其他地方进行模拟通常是一个好的模式。

12. Globals不好。 功能胜于类型。 对象可能比复杂的数据结构更好。

13.使用Python内置类型及其方法将比编写自己的类型更快(除非您使用C编写)。 如果考虑性能,请尝试找出如何使用标准内置类型而不是自定义对象。

14.依赖性注入是一种有用的编码模式,用于明确您的依赖性是什么以及它们来自何处。 (让对象,方法等将它们的依赖关系作为参数接收,而不是实例化新对象本身。)这确实使API签名更加复杂,因此这是一个折衷。 最后得到一个需要10个参数来满足其所有依赖关系的方法,这是一个好兆头,表明您的代码反正做得太多。 关于依赖项注入的权威文章是Martin Fowler的“ 控制容器的倒置和依赖项注入模式 ”。

15.您要模拟出更多代码来测试代码,您的代码就越糟。 为了实例化特定行为,实例化和放置的代码越多,代码的性能就越差。 目标是建立可测试的小型单元,以及更高级别的集成和功能测试,以测试这些单元是否正确协作。

16.面向外部的API是“预先设计”以及对未来用例的考虑的重要方面。 更改API对我们和我们的用户来说都是一个痛苦,并且创建向后不兼容是可怕的(尽管有时无法避免)。 仔细设计面向外部的API,仍然要遵循“简单的事情应该简单”的原则。

17.如果函数或方法超过了30行代码,请考虑将其分解。 一个好的最大模块大小约为500行。 测试文件往往比这更长。

18.不要在对象构造函数中工作,这很难测试并且令人惊讶。 不要将代码放在__init__.py中 (命名空间的导入除外)。 程序员通常不希望 __init__.py找到代码,因此“令人惊讶”。

19.在测试中,DRY(不要重复自己)比在生产代码中的重要性要小得多。 单个测试文件的可读性比可维护性(划分可重用的块)更为重要。 这是因为测试是单独执行和读取的,而不是它们本身是大型系统的一部分。 显然,过多的重复意味着可以为方便起见而创建可重用的组件,但是与生产相比,它所关心的要少得多。

20.只要您看到需要并有机会,就进行重构。 编程是关于抽象的,并且抽象越接近问题域,代码越容易理解和维护。 随着系统的有机增长,他们需要更改结构以扩展其用例。 系统的增长超出了它们的抽象和结构,并且不进行更改就变成了技术上的负担,使工作变得更加痛苦(并且速度越来越慢,而且漏洞更多)。 将清理技术债务(重构)的成本包括在功能工作的估算之内。 您留下的债务越久,累积的利息就越高。 迈克尔·费瑟斯(Michael Feathers) 着有一本关于重构和测试的好书。

21.首先使代码正确,然后使快速正确。 处理性能问题时,请务必先进行概要分析,然后再进行修复。 通常,瓶颈并不像您想象的那样。 因为速度更快而编写晦涩的代码,只有在您已概要分析并证明它确实值得时,才值得这样做。 编写一个可以按时执行您要分析的代码的测试可以让您更轻松地知道何时完成操作,并且可以将其留在测试套件中以防止性能下降。 (通常需要注意的是,添加计时代码总是会改变代码的性能特征,使性能工作成为更令人沮丧的任务之一。)

22.范围更小,范围更紧密的单元测试在失败时会提供更多有价值的信息-它们会特别告诉您哪里出了问题。 占据整个系统一半以测试行为的测试需要进行更多调查才能确定出什么问题了。 通常,耗时超过0.1秒的测试不是单元测试。 没有慢的单元测试之类的东西。 通过严格限定单元测试的测试行为,您的测试将成为代码的实际规范。 理想情况下,如果有人想了解您的代码,则他们应该能够将测试套件作为行为的“文档”。 Gary Bernhardt的快速测试,慢速测试是有关单元测试实践的出色演讲:

23. “不是在这里发明”并不像人们所说的那样糟糕。 如果我们编写代码,那么我们就会知道它的作用,我们知道如何维护它,并且我们可以随意扩展和修改它。 这遵循YAGNI原则:我们为需要的用例提供了特定的代码,而不是对不需要的事情具有复杂性的通用代码。 另一方面,代码是敌人,拥有多余的代码是不好的。 引入新的依赖项时请考虑权衡。

24.共享代码所有权是目标; 孤立的知识是不好的。 至少,这意味着讨论或记录设计决策和重要的实现决策。 代码审查是开始讨论设计决策的最糟糕时间,因为很难克服编写代码后进行大范围更改的惯性。 (当然,最好在评审时指出并更改设计错误总比没有好。)

25.发电机摇滚! 与有状态对象相比,它们比迭代或重复执行的对象更短,更容易理解。 大卫·比兹利(David Beazley)撰写的“ 用于系统程序员的发电机技巧 ”是对发电机的很好介绍。

26.让我们成为工程师! 让我们考虑设计和构建功能强大且实施良好的系统,而不是让有机怪物成长。 但是,编程是一种平衡行为。 我们并不总是在建造火箭飞船。 过度设计(洋葱架构)与设计欠佳的代码一样痛苦。 罗伯特·马丁(Robert Martin)的几乎所有文章都值得一读,《 清洁体系结构:软件结构和设计的工匠指南》是有关此主题的好资源。 设计模式是每位工程师都应阅读的经典编程书籍。

27.间歇性失败的测试会侵蚀您的测试套件的价值,以至于最终每个人都忽略了测试运行结果,因为总有一些失败。 修复或删除间歇性失败的测试很痛苦,但值得付出努力。

28.通常,特别是在测试中,请等待特定的更改,而不要等待任意时间。 教的睡眠很难理解,并且会降低测试套件的速度。

29.总是看到您的测试至少失败一次。 放入故意的错误并确保其失败,或者在完成被测行为之前运行测试。 否则,您可能不知道自己在测试什么。 偶然编写实际上不测试任何东西或永不失败的测试很容易。

30.最后,管理要点:恒定特征研磨是开发软件的糟糕方法。 不要让开发人员为他们的工作感到自豪,以确保您不会从他们身上获得最大的收益。 不解决技术债务会减慢开发速度,并导致产品变得更糟,更容易出问题。

感谢Ansible团队,尤其是Wayne Witzel ,为改进此列表中建议的原则提出了意见和建议。


想要摆脱IT流程和复杂性的束缚,让您从最佳性能中退缩吗? 下载此免费电子书: 教大象跳舞

翻译自: https://opensource.com/article/17/5/30-best-practices-software-development-and-testing

软件开发最佳实践

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值