陪女儿看动画片《米奇妙妙屋》,米奇有一句口头禅:我们是用脑子解决问题的人。从女儿的表情判断,这句话似乎并没有给她留下不一样的印象,三岁半的女儿毕竟还太年轻。但我在想:工程师才最应该是用脑子解决问题的人啊。原始人区别于动物的地方是渐渐地有了思考的能力,但这种思考能力水平很低,缺乏系统性和逻辑性。比如饿了,出去打猎;吃饱的时候,就无所事事,而不是继续工作,储存食物以备不时之需。这样长远的打算远远超出了他的思考能力。就好像小孩子一样,在我们农村老家,冬天很冷,到了这个季节,有的小孩子特别喜欢尿裤子,那是因为尿的温度使孩子感觉很温暖,却全不考虑热尿变凉之后的痛苦,于是,上一次的尿凉了,就只好努力再来上一泡。
到了新石器时代,人类开始耕种和驯养动物时,才真正有了系统性的思考。即使对于现代的人们,系统的逻辑的思考能力依然是宝贵的稀有资源,并非想当然的人人都可以自然地轻易拥有。很多人大多数时候都是情绪的宣泄,不过是所谓的感情用事,这和原始人及小孩子并没有太大的区别。
测试工程师和开发工程师都是软件工程师,请注意在这里工程师才是重点,工程师尤其是和程序打交道的软件工程师的一个重要特质就是知道如何用科学的方法论来解决问题。
所谓科学的方法论,简单地讲,即数据和逻辑的组合,数据表述事实,逻辑用于推理,一言以蔽之,就是基于数据事实的逻辑推理。计算机科学专业的课程中的数据结构和算法从本质上讲也可以说是软件工程师的方法论。数据结构用来无歧义地有组织地抽象出问题域中的实体和对象,而算法描述了计算过程的逻辑的确定性和正确性以及计算的其他性质如时间复杂度和空间复杂度。
软件工程师面对问题时,首先要把问题的事实即数据无歧义地有组织地清清楚楚地表示出来,然后,利用逻辑的分析、归纳和推理找出问题的症结之所在,最后根据软件设计与开发的原则和最佳实践得到问题的解决方案。
四个例子
例子 一:
有测试工程师抱怨开发工程师不停地改变命令行的字符串,导致她的测试脚本不能稳定运行,每次命令行字符串的改变都浪费了她大量的时间去修改测试的脚本。软件开发中随时随地要面对各种各样的变化,不让开发工程师修改这些字符串显然是不可能的和不合理的。问题的事实是在测试的脚本中处理经常改变的命令行字符串需要大量的时间,原因是她把这些字符串硬编码到测试的脚本中。
在程序设计的方法中,一个重要的原则是把需要经常变化的部分隔离开来,这样有利于更容易地管理这些变化。所以,可能的解决方案就是把这些字符串隔离出来,定义为全局变量,在测试脚本中引用这些变量就可以了。这样,当开发工程师修改了这些字符串时,测试工程师只要修改这些变量的定义就好了。
例子二:
常常听到测试团队和开发团队抱怨产品的性能如何糟糕,但抱怨仅仅只是抱怨,问题依然还是问题。解决性能问题,首先要知道怎样量化地定义性能。对程序而言,性能可概括为两个参数,即吞吐量(Throughput)和响应时间(Responding Time)。令人沮丧的并不在于有些工程师不知道性能的定义,而是根本没有想到要知道性能的定义。追本溯源,多半是从来没有使用数据与逻辑的科学方法的意识和习惯,不知道用什么样的方法来解决问题,剩下的当然只有抱怨了。
所以,工程师尤其是测试工程师最不应该抱怨产品的性能,正确的做法应该是找到一个应用案例(Use Case),给出具体的可重现的配置操作步骤,得到相应的吞吐量或响应时间,和期望的结果做比较,指出其中的差距。有了应用案例,开发工程师就可以用一些工具做性能的profiling了,根据profiling的数据,找出性能的瓶颈是什么,帕累托原理告指出往往是局部的区域贡献了大部分的问题,比如一个拙笨的函数,导致响应时间增加80%。以这些数据做基础,开发工程师更容易的做出有针对性的解决方案,我们当然有理由对这样的解决方案对于改进性能抱有更大的信心。
最后,当然还是要用测试的数据来证明解决方案的有效性。这是改善产品性能最专业的做法。我们是做交换机系统软件的公司,利用这种方法,在CPU占用率基本不变的情况下,CPU处理协议包(protocol packet)的吞吐量提高了一倍。
例子三:
做产品级的功能。和例子二一样,我们总是感觉公司的产品的某些地方不太像产品的样子,但到底是哪些地方由于什么问题让我们产生这样的感觉呢?说不清楚。这就如同某人到公司之后给大家留下了很邋遢的印象,可谁会去细想是因为他今天没有刮胡子还是没有洗头发的缘故呢?更有人认为,像我们这样的起步公司,对功能的质量不能过于苛求,最好也就做成这样了。于是,妄自菲薄变成了怠于思考的口实。
解决这个问题,首先还是应该从事实和数据着手,即产品的哪些地方出了什么问题。方法是把问题具体化,以某个具体的功能作为例子,和同类的顶级的产品做对比研究。
结果表明:针对该项功能,在大的方面,比如设计目标、应用场景、部署的网络拓扑、甚至于实现的技术路线,我们的产品和参照的产品并没有显著的差异。真正的差距体现在用户体验(UX, User eXperience)设计的细节上。比如我们在CLi(Command Line)中呈现给用户的信息没有进行精心的过滤和组织,使用了晦涩混淆的术语,并且包含了一些对用户而言毫无意义的数据,使得整个产品看起来粗糙丑陋,不够专业。
结论是:技术总是简单,而设计无限。假设我们做新的功能时,从用户的角度出发,真正花些心思在UX的设计上,产品的质量一定会有大的改善。在这个案例中,如果能够得到用户的反馈信息,做综合的分析,那就更加完美了。最后,由于公司保密政策的考虑,我不能列出做对比的两个产品的名称,也不能给出那个功能的具体的细节。
例子四:
和Regression Bug开战。Regression Bug指的是工程师在修改一个Bug或开发一项功能时将原来好的功能破坏了。如果一个产品开发周期发现了大量的Regression Bug,意味着产品质量的倒退和开发团队的不专业,这真的是一件令人万分沮丧的事儿。如果Regression Bug留到了用户那里,可能会是灾难性的后果。因此,产品新的版本发布前,都要进行严格的Regression测试。
我们没有办法和Regression Bug永远说再见,因为任何人在任何时候和任何环节可能有任何错误,这也是产品开发中最大的不确定的风险,能够做的是尽量避免。还是老的方法,把Regression Bug收集起来,针对其中的每一个,分析产生的原因和可能避免的办法。
我们发现,有趣的是,越是严重的Regression Bug,背后的原因越是简单。比如系统崩溃是因为某个变量使用前没有初始化,忘记for语句的循环变量递加导致系统卡死挂起。所以,小问题引起了大麻烦。这样的信息反馈给开发工程师,就能够激发TA有针对性地提升自己的专业水平。
总结
人类是情感和理性交织的动物,偶尔的情感宣泄难以避免,重要的是,无论什么时候,都不要忘记了理性的思考,这是唯一的可以帮助我们解决问题的力量。因此,一个测试工程师如果不了解不运用科学的方法,即使积累了再多的领域知识,最多也就是一个操作员;一个开发工程师如果不了解不运用科学的方法,即使积累了再多的编程经验,最多也就是一个编码员。