我们从无数的实践经验中认识到这些软技能非常地重要:很多时候项目出错的原因都是因为缺乏测试和合作。下面是在Tower用户数目增长至100,000的过程中我们所学习到的一些经验。
请确保你已经阅读了这个系列的第一章节:结构、设计模式和编程规约
所有的代码都属于团队
代码是团队所有,并不是任何一个开发人员的私有物。这也意味着你所写的任何代码都不属于你,而属于你所在的团队。用这样的方式来看待你所写的代码是非常有益的,有以下几点原因:
1、不一定非要按照你的方式进行编码。在了解代码始终属于团队后,理所当然地我们需要遵守一定的编码标准和规则。既然它不属于你,你不能肆意地按照你喜欢地方式去进行编写。你所写的代码是通用的代码库,所以必须遵守一些通用的规则。这也能够让你在看同事的代码时更加宽容,你可能本能地想到你会以不同的方式完成他们的工作。 但是,请记住所有代码的编写需要遵守的是团队的方式而不是你自己的方式,这可以帮助你更加开放地理解其他解决方案和方法。
2、一切都是你的问题。当你在代码中发现问题时,是不是你的错误一点都不重要。因为项目的所有代码都属于团队,并且你是团队的一员,任何来源的有缺陷的代码都成为你的问题。如果团队中的每个人都秉持着“这不是我的问题”的态度,那么这个团队永远也不会开发出优秀的产品。
3、你,你,你。我从opbeat公司的Ron Cohen在一次会议上的演讲中了解到这一点。这简单地提醒了我们作为软件开发者的责任:你有责任编写代码。同时,你还负责使其运用于生产中。当然,你有责任解决它的任何问题。在我们自己的团队中,我们没有专门的质量保证团队,这提醒我们开发人员不仅要负责编写代码,还要负责编写出的代码的质量。这个责任不是你可以逃脱或交给其他人的。
分享关于代码审查的知识
代码审查有助于提升团队的代码质量。首先,多个人在相同的代码上进行检查,能够提供更多更好的解决方案,将有机会产出更高质量的代码。其次,代码审查有助于团队的人对所有代码进行了解。例如,当只有一名开发人员指导某段代码时,就会存在一定的风险。每当这位开发人员休假、生病甚至离开公司时,团队都将遇到麻烦。即使没有这些情况发生,这也说明你的团队没有真正的实现共享代码库。此外,代码审查有助于促进团队内部的知识交流,提升团队的整体开发水平,因为初级开发人员可以从交流中向高级开发人员学习。高级开发人员也应该意识到在指导初级开发人员时他们也能够学习到新的事物。
并不是所有的都需要改善
你可以认为程序中的所有一切都可以改进,但重点不是什么可以改进而是什么值得改进。我们知道自己的产品存在弱点,如果时间是无限的,我们可能会改进每一点。但随着时间成为任何团队的稀缺资源,我们不得不设定优先级。如果你不这样做,你的产品将不会朝向你想要的方向发展,甚至可能根本就没有任何方向。
能够负责任地思考是否真的去改善某件事情是一门艺术,这需要多年的经验,而且最好的情况是,经过有意识的培训。对许多事情说不,是获得任何有价值的先决条件,这个道理同样适用于软件开发。
进行提问
在阅读其他人的代码时,很容有就在头脑中假设你会做得更好。在第一次阅读时,你可能认为这行或那行代码似乎不需要如此复杂,甚至是多余的。你也许是对的,但很难说,因为你实际上并没有编写代码。
根据我们自己的经验,我们知道即使是最简单的问题也可能包含令人惊讶且无限的复杂性。 只有在实际解决问题的时候(考虑可能的解决方案,副作用等)才能充分地理解它。
因此,当遇到一段让你怀疑的代码时,假设大量隐藏的复杂性是一个很好的基本规则。从这个角度来看,你可以深入了解代码并尝试理解它以及潜在的问题——或者您只需与队友进行对话并询问一些简单的问题。
只有经过测试的软件才完整
单元测试有时被认为是锦上添花。然而,我们的确需要在开发过程中进行单元测试。在较小的项目中,您可能不用编写单元测试。然而,在任何甚至只有少量复杂性的项目中,你都应该进行单元测试。很多工程,起初看似非常简单,但后面将发展的越来越复杂。
单元测试对我们至关重要的确有许多原因:
1、测试使我们能够安全并快速地完成大的代码改变。在每一个大型的项目中,你不得不在一些时候完成一些重构。但是在重构的过程中,如果没有一定数量的单元测试将会带来非常糟糕的体验:在修复一些问题的时候常常不可避免地会破坏另一些地方的代码,如果拥有足够多的单元测试那么你就能够容易地知道哪里出现了问题。
2、我们可以很自信地向外部发布项目。如果你的应用需要提供给付费用户使用,如果你没有足够多的单元测试,那么在每次将代码提供给用户使用时你总是会充满担心。
3、测试使我们可以获得更优秀的代码。进行良好的测试是最终获得优秀项目代码的最好的前置条件,因为测试能够帮助你更好地理解你所解决的问题,并帮助你创造简单的、模块化的代码。
4、测试能帮助我们及时更新文献。在一个理想的世界中,应用中的每一个部分都应该记录在文档中。我们希望拥有一份简洁、格式漂亮、易于阅读的文档,并且最为重要的是当我们每次更改代码的时候我们都希望文档能够及时的更新,当然这是非常理想化的。但是使用单元测试可以到达这个目的,虽然形成的文档不像专门的文档那么专业化,但是至少保证了文档能够反映当前的项目状况。单元测试能够成为一个非常有价值和可靠的文档来源。
对于大型项目来说,对重要部分代码进行充分的测试是非常重要的。它使我们在发布应用程序的时候能够非常自信,并且我们总是能够安全、快速地对代码进行更改。
测试能够加快开发的速度
起初,你可能会认为测试驱动的开发似乎非常消耗时间,会降低你的开发进程,并且编写单元测试的确需要花费一定的时间。但是随着时间的推进,你会发现编写单元测试能够加快你的开发!你可能会花费更少的时间来编写实现代码,因为在你写单元测试的时候你应经完成了大量的工作。另一个好处是:当代码需要发生改变时能够降低破坏现有代码的可能性。你将能够节省无数原本会花在查找和修复bug的时间,然后你就能够用这些时间做更多有用的事情。
通过测试修复问题
修复bug和编写测试应该是密不可分,当然你可以仅跟踪bug并简单地修复代码。然而,我想你应该对“回归”这个词很熟悉:你确定已经修复的bug再次出现在你面前。如果你想要确保bug的确被修复了,那最好的办法就是编写测试代码来确保。
测试低层次的组件
当开发一个每天都有超过100,000人使用的软件产品时,我们总能够从中学到很多经验教训。一方面,我们明白错误是无法避免的,尤其是像Tower这样的产品,需要处理复杂的系统初始化过程,你不能够控制所有的事情。但另一方面,我们知道哪里发生的错误最多。
在Tower(很可能在许多其他较大的应用程序中),大多数错误往往发生在低级别组件中。但比较好的是,这种组件非常适合单元测试。在这个领域,测试驱动的方法提供了一个巨大的机会——通过合理地测试低级组件,以合理的投资来提高应用程序的整体稳定性和质量。
有效测试
通常,尽可能使单元测试覆盖你的代码是非常可取的做法。然而,当你听到一个软件开发人员吹嘘他的“100%测试覆盖率”,你应该小心。听起来我们似乎应该测试程序的每一行代码,但是实际上并不是这样。
一方面,我们需要花费无限的时间来确保100%的测试覆盖率。然而,更重要的是我们不必要编写太多的单元测试,尤其当你进行重构时,你不仅要维护你的代码,还要维护测试代码。当你的代码改变时,你也必须要更改你的测试。
关键的标准是——测试能给你带来价值吗?通常,如果只是测试很简单的逻辑那么测试所能够提供的价值就非常小。在这些情况下,测试所带来的价值就远小于维护它所花费的精力。编写有效的测试也即意味着尽量编写少的但价值高的测试,而不是编写一堆价值较低的测试代码。
仅测试,不要运行!
在你的应用程序中手动地进行测试需要花费大量的时间:你必须启动应用,跳转到特定的页面,然后执行相应的操作来进行测试。然而编写单元测试只在最初的时候需要花费一些时间,之后的多次测试将会非常容易。仅运行单元测试将比启动整个应用进行手工测试要快的多。除了速度快之外,测试也能够提供高质量和稳定的测试结果,在大多数情况下,前期所投入的时间是完全值得的。