专业程序员必知的技巧:敲打代码
编写生产质量级别的代码似乎是一个明摆着的目标,但计算机行业却费了不少时日才弄明白正确的实现之道。例如,Windows 95曾经有个Bug会让操作系统在连续运行49.7天之后挂起—但是该Bug花了4年时间才暴露,有Bug这件事本身并不特别让人觉得惊讶,时间之所以这么长是因为其他Bug在不到49.7天的时候就让Windows 95崩溃了。
通往高质量代码的道路有两条,你可以二选一:一开始就内置质量,或者事后再敲打它。前者需要你在日复一日的编码中遵循众多戒律;后者则要求大量测试,到头来,在自以为完工之后,你会发现还有很多工作要做。
事后敲打(beat-it-in-afterward)是常见的工作方式,行业占统治地位的瀑布开发方法就是这样:规格说明、设计、构建、测试。测试是最后的步骤。产品来到测试部门,很快就崩溃了。于是,又回到工程部门,修复Bug。接着,把另一版提交给测试部门,又由于其他原因崩溃。就这样,来来回回,许多月(甚至是数年)流逝。
在程序员们深入具体实践之前,我们会从技巧1——敲打代码开始,帮你建立正确的思维方式。
你可能认为编写可靠代码是再明显不过的工作要求了。招工广告上不可能写:“急聘:具备良好工作态度、团队合作精神和桌上足球技巧的程序员。有则更佳:会编写可靠的代码。”可有问题的程序还是有这么多,怎么回事?
在深入探讨保证代码质量的日常实践之前,让我们先讨论“编写可靠代码”的含义。它不仅仅是一份实践清单,它还是一种思维方式。在把产品交到客户手中之前,你必须敲打自己的代码和整个产品。
客户终究敲打你的产品,以一种你不曾预料到的方式使用它。他们用它的时间会很长,而且会在你没有测试过的环境里用它。你必须考虑的问题是:打算让客户发现多少Bug?
你现在对代码敲打的次数越多,在交到客户手中之前,能清除掉的Bug就越多,留给客户的Bug就越少。
质量保证的形式
1.代码评审
保证代码质量最简单的方法就是让另一个程序员去读它。别出心裁的评审过程并没有必要,而且就连结对编程也算是一种形式的实时代码评审。团队将利用代码评审捕获Bug,贯彻编程风格和标准,同时在团队成员间传播知识。
2.单元测试
在你一个类接着一个类、一个方法接着一个方法地构建应用的业务逻辑时,验证代码的最佳方式就是单元测试。这种内部零件级的测试被设计用来对逻辑的各部分单独验证。
3.接受测试
单元测试立足于由内而外地审视产品,接受测试则被设计成模拟真实世界的用户,代表他们与系统交互。理想状况下,它们是自动执行的,而且以某种叙述式的风格书写出来。例如,某银行自动柜员机应用会有类似这样的接受故事:若我的活期存款为0,当我在ATM的“活期存款”中选择“取款”时,那么我应该看到“对不起,今天的晚餐吃泡面吧。”
它不像莎翁著作那样文采飞扬,但这些测试操练了整个系统:从用户界面一直到业务逻辑。无论它们是自动执行的,还是人工执行的,你的公司需要知道—在任何客户使用它之前—所有系统组件正在像预期的那样协调工作。
4.负载测试
负载测试将产品置于真实的压力条件下,然后度量它的响应。例如,某网站可能需要在数据库有100万条记录的条件下在100毫秒内展示指定页面。这些测试将揭示正确但不恰当的行为,如需要线性伸缩但却以指数级别伸缩的代码。
5.定向探索测试
接受测试覆盖了产品的所有指定行为,它可能来自于产品需求文档或会议。但程序员通常还是有办法使之崩溃—总有些黑暗角落被规格说明疏忽掉。定向探索测试就是要将这些边界情况挖出来。
这种测试通常是人工执行的,可能是程序员自己,用于探索和发现问题。但最初探索之后,任何有用的测试就会被加到接受测试套件之中。
该测试有一个专业化的变种,如安全审计。在这些情况下,专业测试人员会利用他们的领域知识(可能也包括代码评审)来指导他们的测试。
6.机构测试
硬件产品需要不同的机构认证:FCC度量电磁辐射,确保产品不会导致无线电干扰;美国保险商实验室(UL)检查当你将产品置于火上或电池电极时会发生什么。这些测试都在新产品发布之前进行,每次硬件变化都会影响认证。
7.环境测试
硬件产品的运行温度和湿度也需要在推至极限时测试。这些测试是用环境室来完成的,它可以同时控制这两个因素;当产品在其间运行时,它会经历所有四种极限条件。
8.兼容性测试
一旦产品需要跟其他产品进行互操作(如某字处理程序需要跟其他字处理程序交换文档),这些兼容性的论断就需要定期验证。它们可能会访问一组已保存的文档,也可能会实时地将你的产品连接到其他产品上。
9.耐久性测试
你会注意到这里提到的大多数测试都是尽量频繁且快速地运行。可有些Bug只会在一段时间的使用之后现身。前面提到的49.7天的Bug很好说明了这一点—它源于每毫秒递增的32位计数器,在49.7天之后,它会从最大值反转成0。测试若不持续运行上一会儿,你就无法发现类似Bug。
10.Beta测试
产品在这一阶段被送到了真实客户手中—他们知道自己要参加测试,并同意发现问题时提交报告。Beta测试的目的就在于我们在本技巧一开始讨论的:Beta测试者将以你意想不到的方式使用产品,试用它一段时间,并在你没有测试过的环境中测试产品。
11.运行中测试
公司可能会在产品上市之后继续测试。尤其是硬件产品,如偶尔从制造线上拔掉一个单元并证明制造线能工作正常是一种很有用的方法。这些运行中测试的设计目的就是为了捕获因零件或装配过程中的变化而导致的问题。
实践 VS 思维方式
你的团队可能采用类似“所有代码都必须有单元测试”或“所有代码必须先评审后检入(check in)”的实践。但这些实践没有一个能保证代码坚若磐石。想想若公司根本就没有采用一个质量实践,这种状况下该怎么做,即你将如何敲打代码以保证它的可靠性?
这是在继续深入之前你需要建立的思维方式。提交可靠的代码。质量实践只是达到目的的一种手段—最终的裁判是客户手中产品的可靠性。你想让你的名字跟市面上满是Bug的垃圾产品挂钩吗?不,当然不想。
行动指南
在上述所有形式的测试中,你的公司采用了哪些?在源代码中寻找单元测试,向测试部门询问接受测试计划,问问Beta测试是如何进行的以及向哪个部门提交反馈。再问下资深工程师:这是否足以保证客户有一个平滑的体验?
在定向探索测试上多花些时间,哪怕你的“方向”有点儿模糊。实际用一下产品,看看你是否可以让它崩溃。如果可以,那就相应地记下Bug报告。