摘要:在本文中,我们将探讨软件开发过程中关于角色、重构和质量的问题。
“每天都会有更多的技术发生,每家公司都在互联网上,每家公司都将成为一家科技公司。”OKTA首席运营官兼联合创始人Frederic Kerrest说道,因为他们必须找出使用该软件的更好方法。软件不仅成为了一个必需品,更成为了一个竞争优势。因为众多公司围绕软件而竞争,软件开发相关的事宜显得越发重要。开发软件的人——软件工程师正显得越发重要。
“对于知识,要求知若渴;对于自己,要虚怀若谷。”优秀的软件工程师一定是在软件开发的道路上前行者。自学是其成长的一个重要手段,在自学的过程中,我们是可以通过考试的方式来收敛思绪,督促自己学习,从而提高自己的基本素质。诚然,原则和模式是软件工程质量的基石。但技术是工具, 是为人服务的,而不是相反的。我们不能为了迎合某种技术而束手束脚,让自己特别难受。与此同时,要让自己的能力发挥到极致,良好的心境是必须要有的,因为软件工程中的一个核心因素是人的因素。
诚然,在软件开发过程中,我们不仅要将自身内功修炼好,更应该 “用产品说话”。那么,在这个过程中,我们该如何保证开发的质量呢?在开发的过程中如何专注于自己擅长的事情呢?在本文中,我们将探讨软件开发过程中关于角色、重构和质量的问题。
角色
我们经常提一句话:革命工作只有分工不同,没有高低贵贱之分。这里的分工其实就是角色的划分。角色划分是为了让个体承担的工作量最小化,从而可以把我们从繁文缛节中解放出来,专注于自己擅长的事情。那么,在软件工程当中,这样的理念应该如何贯彻呢?
软件工作里面的脏活儿、累活儿一般是指技术老旧而不得不维护的一些工作。还有一些重复性强的工作也被称为脏活儿、累活儿。
对于这种活儿,一般工程师都想推脱掉。主要原因是认为做这类活儿技术提高的空间很小,再加上技术陈旧,这些技巧学会了以后也用不上,同时也比较枯燥。
这类工作的工程师一般是指派的。需要对相关的工程师进行一些必要的技术培训或者直接招收懂得相关技术的工程师加入工作。
效率和价值主要体现在帮助客户解决现有软件系统中的问题,或者添加新的功能。客户可能很少愿意购买一套崭新的系统,因为价格相对比较高,所以他们宁愿少花点钱去做些修修补补的工作,能够解决燃眉之急就可以了。
运维工作的价值是把已经开发出来的组件和系统集成起来统一的工作。是推出面向用户的软件系统产品的重要一步。我不认为是边角料的活儿。
运维相关的工作越简洁越清晰越好。这部分相关的文档一般是read me markdown的形式存放在软件系统的repo中。通过查看这些文档,应该可以自行部署整套系统。
系统部署一般会分几种类别,开发模式,qa模式,staging模式和生产模式。
业界对于软件开发过程中的角色有不同的理解和看法。笔者观点如下:
1.项目产品经理负责业务需求的处理,负责跟客户与开发团队打交道。
2.项目开发组长一定是全栈,需要统筹规划,与项目经理一起探讨需求分析,与开发组成员一起探讨开发设计,任务分配与开发实现。
3.前端工程师负责网络页面程序开发,手机端应用开发,桌面端应用开发等等。
4.后端工程师负责API设计与开发, 数据分析处理与消息推送。
5.运维工程师负责部署环境的搭建与看护。
6.针对具体的业务需求,还会有更细分的角色类别,比如说大数据工程师,算法工程师,AI工程师,机器学习工程,深度学习工程师, 中间件工程师。
7.测试工程师负责系统集成后的业务需求案例测试。这一部分的输入跟开发团队的输入是一样的,都是用户的需求。输出则是需求案例对应的测试报告。而开发团队的输出就是整个软件系统。
重构
为什么我们需要对代码和设计进行重构?主要是因为我们发现了更好的做法,如效率更高,更容易维护等等。
简单的代码重构我们都比较熟悉,比如说你通过工具就可以做一些整理。
一般来说,重构是为了解决复杂度的问题。
现在比较头疼的一个话题就是对老产品的重构,一些老产品涉及到上千万行,上亿行的代码。
关于老产品整改的问题。如果只是缝缝补补的话,可能起不到化繁为简的目的。其实做类似这种工作的话,有一个比较可行的方案。就是把现有的产品当做一个成型系统也就是现有运行的产品,不要做大的改动,顶多就是修改bug。
然后以这些成型的系统为基准,去写新的系统。相当于参照一个大的白盒就写一个小的白盒,这样新的小的白盒质量上肯定比大的白盒性能上要有优势。
这样子按部就班去做的话,就会比较靠谱。
有朋友会说上面的做法是重写,字面意义上没错的。
实际上不矛盾。区别就是重构的方式应该从下往上还是从上往下。比如说我们现在大部分的重构都理解为从下往上来做。也就是感觉这个文件里头有坏代码的味道,然后就改这个文件,这样做是没有问题的。前提是这项工作的上下文比较单纯,无技术债务。
很多情况不是如此幸运的,比如现在有些人遇到的问题,就是发现上下文不是很清晰,这个代码为什么要这么写?为什么一个文件有1万行或者3万行,这个来龙去脉不是很清楚。
这个时候可能就需要从整个子模块来进行一个自上而下的分析。梳理出这个子模块的功能需求是怎样的,需要有多少个公共接口?内部公共接口的实现方式是不是应该像目前这样的?
一个文件能够写成1万行或者3万行,肯定是有一定历史原因的,绝大程度是由于全局把握的编程能力不够造成的。
像这种情况,如果从这个文件本身去做重构的话,难度非常之大,但是如果从上往下,从模块的整个设计角度来做重构的话,可能就容易一些。
对于这样的庞然大物,最好的办法就是分而治之。首先要确定系统的功能逻辑点,针对这些逻辑点,要编排好对应的检测点,也就是说等我们完成了重构以后,我们得确保我们的重构是没有问题的,这些检测点就是做这个的,我们可以理解成集成类的测试。
这些集成类的测试一定要确保可以在当前未重构之前的系统上正常运行。
有了这个设施以后,我们就可以开展我们的重构工作。重构的方法有很多,比如采用比较好的工具,函数和变量的命名改变,调用方式的改变等等。这些是在现有代码的基础上进行的重构。这里我们重点说一下重写的方式来实现重构。所谓重写呢,就是另外开辟一套代码底座。甚至可以选用不同的编程语言。
这种情况下重构首先要重用已有的业务逻辑,实现针对业务逻辑集成测试100%的通过率。
具体不管采用哪种方式都要一个模块一个模块的进行推进。验证完成一个是一个,千万不能急于求成,试图一次性的把某些问题搞定。如果出现很多次失败,有可能会消磨掉你的自信心。所以一定要一点一点的往前推进,始终是在进步当中。采用了这种方式以后,不管当前的系统有多么的庞大,你只要坚持做下去,就一定能够把重构工作彻底完成。
这个时候需要做的具体步骤可以参考如下:
1. 根据功能需求定义公共接口。
2. 根据公共接口写出测试案例代码。
3. 这个时候可以按照测试驱动开发的理念去填充代码。
4. 代码可以从现有的代码中抽取出来。
5. 在抽取的过程中进行整理重构。
这样,这个子模块完成以后,就可以尝试去替代现有的子模块,看看能不能在整个系统中安全的运行。
对于整个系统来说,我们又可以分成很多个子模块。然后又可以对各个子模块各个击破,最终完成对整个系统的重构。
如果一开始对整个系统进行重构的话,也是可以从自上而下的角度来看的。
比如说开始的时候先把所有的子模块看成一些占位符,假定他们已经完成他们的接口了。那对于整个系统来说,它本身就是一个子模块,属于提纲挈领的那个模块。
这个过程,从字面意义上可以理解成重写,实际上,它也是一个重构的过程,因为我们肯定会重用这个系统本身的一些现有代码和现有的逻辑。
上面我们是假定系统在已经完成的情况下进行的重构,其实重构可以贯穿于软件开发的始终。软件开发的首要目标是实现业务逻辑,能够解决客户的问题。这个目标实现以后,我们就要追求代码的干净度,复杂度能够降到最小,当前的技术能够用到最先进。
所以只要有机会,我们都应该对代码和设计进行重构。
质量
质量直接关系到客户是否对我们的产品满意。那我们应该如何保证软件开发的质量呢?
要遵循整个开发团队的共识才能保证质量。共识是一个可大可小的术语,大到理想、哲学、人生观;小到软件设计原则,设计模式,代码风格。如果是打造一个团队那就是长期的目标,共识一定要从大的方向上入手。如果仅仅为了开发一个项目,共识可以从具体的细节着手。
软件质量的保证,需要整个团队形成共识,大家都遵循这个共识。这个共识体现在开发原则,设计模式和代码上,具体表现在架构代码和模板代码上,在项目最初的开发阶段,开发速度一定要慢,就是为了经过反复的推敲夯实,把代码的共识部分建立起来。
风格上的目标是,不管这个团队有多少个人,写出来的代码,就像一个人的代码一样,风格是一致的。
代码的质量也体现在复杂度上。复杂度的目标是,在目前的技术条件下,当前的代码的复杂度应该为最低。
另一个软件高质量的重要指标是代码的白盒可测性。测试的框架应该在项目开始阶段搭起来。等部分代码成型的时候,逐步的添加必要的测试案例。测试案例的选取可以按照环形复杂度的计算方法来确定,也可以根据集成测试对应的用户需求来确定。
接下来进一步细说一下软件开发中的测试。与代码相关的测试,一般有单元测试,集成测试和系统级的测试。
单元测试,一般被认为非常繁琐。单元测试的繁琐主要体现在测试案例的选取上, 如果使用全覆盖方式来选取测试案例的话,会产生大量的测试代码,以后维护起来也是一个负担。如果采用环形复杂度来选取测试案例的话,会产生适量的测试代码,但是环形复杂度的计算也是一个很大的时间开销。
集成测试跟客户的实际业务需求相关。在这个过程中需要理清接口的输入与输出,以及运行路径,然后据此来设计测试案例,写出测试案例代码。
开发人员一般不会拒绝写集成测试。因为她带来的好处是实实在在的,会极大的提高你的开发效率和调试效率。尤其是对于无界面的程序接口尤为重要。
系统级测试是大系统中子系统之间的集成测试。这个主要包含两个方面:
一个方面是有界面的自动化测试,通过这样的测试架构来模拟人类用户的使用过程,同时增加一些随机性的行为,试图能够找出系统的一些漏洞。
另一种是无界面的测试,体现在多个服务系统之间的调用上或者类似浏览器自动化框架的使用上。
一套完整的测试系统,可以帮助工程师提高开发效率,减少以后系统维护和重构的成本。
从测试的紧迫性上来说,集成测试最为必要,系统间的测试有时候使用手工测试通过一些测试工具来代替。单元测试可以有很广阔的讨论空间,这部分要具体问题具体分析。
如果只是为了应付检查而写测试代码,是没有意义的。
如果测试代码没有起到应有的价值,写测试代码也是没有意义的。
工程师是软件高质量的主要执行者。项目组长,架构师和开发经理是软件高质量的护航者和守护者。
所以不能放任让工程师从下而上的去保证软件质量,这个要求对工程师来说过高了。
小结
最后提一下工程师文化和主人翁精神。对于工程师文化的内涵,我认为包含如下几点:
- (1)工匠精神,对于所做的事情有着精雕细琢的热忱。
- (2)试错文化,勇于尝试,愿意做第一个吃螃蟹的人。
- (3)自律,这个自律是指“吾日三省吾身”。不断的自我纠错反省提高。
对于主人翁精神,不管做什么工作,只要想充分发挥自己的能力,真正的做些事情,不管级别如何,薪水多寡,简单地说,就是时刻把所做的事情当作自己的事情来做。否则的话,时刻斤斤计较,我们做事情的时候就无法全力以赴。
如果抱有患得患失的心态,我们的工作效率就会下降。久而久之,不仅赚不到想赚的“大钱”,也会阻碍自己能力和心境的提高,可谓是捡了芝麻,丢了西瓜。时间是宝贵的,真的不容浪费。
对于主人翁精神的一些具体表象很多,诸如:从来不说“这不是我的事”;做事情不为了短期利益而牺牲长期利益;等等。
通过本文,笔者梳理了一下从事软件工作二十多年来的心得体会,希望能给大家带来一些有意义的启示。