关于软件工程的思考
关于软件工程的思考
说起来,自己的学位其实是「软件工程学士」,现在又在从事移动软件开发的工作。但是关于「软件工程」这门学科,在学校里所学到的内容似乎并没有很好的解决我在实际工作中面临的挑战,而我自认为自己的专业和工作还算比较对口,所以从教学到生产的环节,一定欠缺了某种东西。
这个问题我认为既有教学时客观条件的限制,也有软件工程自身的独特性,我想从这两个角度出发,谈谈自己对于这门学科的思考。
软件工程的 A-Side
软件工程顾名思义就是和软件开发相关的工程学,工程学是一个很庞大的概念,它聚焦于解决现实问题。
学院中的软件工程
前面说到过我们学校对软件工程的授课存在一定的不足,其实据我了解,这个现象广泛的出现在国内的高校中。在很多语境和情况下,计算机科学技术(Computer Science)和软件工程(Software Engineering)是可以互换的,这点体现在授课内容和求职上,计科和软工这两个学科的授课内容是高度重合的,在求职的过程中也不会有某个工作指名道姓只要计科或者软工的毕业生。在国内软件工程一般都授予工学学位,计算机科学技术大部分授予工学学位,也有少数院校会授予理学学位。
这当然不是说这两门学科天然就应该存在巨大的缝隙,恰恰相反,任何一个优秀的计算机从业者,都需要在掌握这两门学科交集部分的前提下,充分的了解两门学科各自的特色部分。事实上,「计算机」这个笼统的概念下,有大量的分支内容,很难用简单的二元论将某一个方向的知识归类到属于计算机科学或属于软件工程,但这不代表软件工程就应该是计算机科学技术的影子,这门学科也有其独特性,否则何必广泛开设这门专业呢,毕竟“若无必要,勿增实体”。
我们学校同时开设了软件工程和计算机科学技术这两门专业,并且实行了大类招生,大一入学后统一以「计算机类」的名义进行培养,大二进行专业分流。当初在分流的时候,其实我对这两门学科还没有清晰的认知,固然学校开设了专业导论课帮助同学们了解不同专业的培养计划和特点,但纸上得来终觉浅,绝知此事要躬行,这门课只让我模糊的知道了一些理论上的区别。我真正体悟到软件工程这门学科的一些门道,已经是在我第二段工作后了。
我们学校分流的原则我已记不大清,总之是以成绩为主,成绩好的同学可以优先选择。可供选择的专业有 3 个,分别是计算机科学技术、软件工程和网络工程,计算机科学技术因为专业的 Title 最多,所以最受成绩优秀的同学追捧;软件工程居中;网络工程几乎是没有选择的选择。而大二专业分流的时候,我的成绩可以满足我 3 个专业任选,选择软件工程,其实是一个相对来说比较另类的决定。
这个决定并没有多神秘,当时我听说软件工程可以多写代码,而计算机科学技术会涉及到硬件相关的课程,我并不喜欢硬件,并且我熟知的几位我心目中优秀的学长,都来自于软件工程专业,所以我选择了它(这个是倒因为果了,其实计算机科学专业也有很多优秀的学长)。
其实从功利主义的角度出发,选软件工程保研的难度比计算机科学技术低,因为绩点高的同学在专业分流的时候大多去了计算机科学技术,像我这样成绩靠前还选择了软件工程的是很小众的一种选择,只不过当时自己根本没想到这一层,也没有争取保研或考研的念头。
不软件工程的软件工程
The application of a systematic, disciplined, quantifiable approach to the development, operation, and maintenance of software.
——软件工程定义,电气电子工程师学会(IEEE)
使用系统化的方法,在保证质量的前提下,更高效率的为客户/用户持续交付有价值的软件或服务的能力。
——软件工程能力定义,百度
软件工程有众多的定义,我选取了 IEEE 对软件工程的定义以及我目前供职的百度公司对软件工程能力的定义作为参考。
IEEE 提到了系统化、有规范和可量化,百度提到了系统化、保证质量和高效率。从中提取共同点,特点就是系统化,进一步结合引申,可以这么说,软件工程是研究如何使用系统化的,可量化的方法,高质高效交付软件产品的一门学科。其中系统化和可量化的方法是手段,高质高效是目的。所有软件工程研究的东西,最后一定能归结到质量和效率两个维度。
从目的出发回到手段,就能发现为什么学校中的软件工程是一门有局限性的专业,最终往往沦为计算机科学的影子专业。
首先抛出一个问题:为什么软件工程的最终目标是高质高效?
软件本身也是一种产品,软件公司就是制造软件的工厂,只是这种产品的分发和盈利模式和传统意义上的「产品」不同而已。只要是产品,只要不是做慈善,一定是需要提高制造的质量和制造的效率以维持产品的竞争力或保持行业领先地位。这是企业的生命线,所以我们可以在百度公司的定义中明确看到「高质高效」,而在 IEEE 这样一个偏向学术的机构的定义中无法看到。
出于提升效率的目的,企业都会在成本和管理手段允许的范围内,投入尽可能多的工程师做同一个产品。如何管理好多名工程师,发挥出 1+N>N 的能力就是一个重要的课题。众所周知,人力的投入与效率的提升并不是线性的,跨过某一个分界点后,继续投入人力反而会导致效率和质量下降,这又带来了软件开发流程和软件版本管理上的问题,如何尽可能延后这个分界点,也就是如何尽可能高的提升人与人之间的协作效率,也是软件工程需要考虑的问题。
而人与人之间协作,在学校中是根本不会涉及的课题。这时候会有人问,不是有小组作业吗?
小组作业没有考虑到人与人之间能力和学习目的的差异,有人目的是高绩点,有人目的是不挂科;有人一个人就能把前后端设计产品测试的活干完,有人只会阿巴阿巴。人员配置不合理,并且没有完成目标的穿透对齐,这就给小组带来了巨大的沟通成本,最终很可能 1+1<1。又因为小组作业的工作量没有多到一个人干不完,所以最终往往一个大佬埋头干活,剩下的人打打酱油,课程就这么混过去了。
并且由于师生比悬殊,教师的精力有限亦或是学院有挂科率的指标等客观原因,教授所布置的作业,往往只有一个最低的要求,「能跑就行」真的不是一句玩笑话,这相当于完全不需要考虑软件质量,很多时候都是一个人开发一个能跑就行的软件就行,而这个软件的需求甚至可能已经连续多届没有变化了,把学长的代码直接复制粘贴过来就能愉快的过关。
失去了高质高效的必要性,学校中教授的软件工程就变的不那么软件工程了,各种课程自然变成了空中楼阁,学生听的不明所以,完全不知道有什么实际应用意义。
软件工程是一门年轻的学科,从从书本上可以学到的东西大致都类似于:“A’ 理论是为了解决软件开发中的 X 问题,在 A 理论的基础上提出的。”但是没有实际的经历过那个 X 困境,就无法体会为什么 A’ 理论相较 A 理论可以更好的解决 X 问题。所以直到我第二份工作,真正参与了大规模软件的开发后,才开始明白软件工程中学到的一些东西,而不是从学校的书本上学习。
软件工程的 B-Side
有哪些大山阻挡在追求质量和效率的道路上,而软件工程又是如何尝试去解决的?
“唯一的不变,是处处都在变”
唯一的不同,是处处都不同。
——iPhone 6s 系列 slogan
如果要说软件开发中有什么特点,那就是密集频繁的变化。
这里的变化指代的范围非常宽泛,软件功能的变化、开发计划的变化、市场需求的变化…在开始实际的软件开发之前,没有人能预料到所有可能会发生的变化,为了更快速的应对和响应变化,必须在软件开发流程上做出改进,而这份答卷就是敏捷开发。
国内的互联网产品,普遍采用了以敏捷开发为基础的开发工作流,简单的说就是将“提出需求——解决问题——验证结果”这个环节的速度加快。在一个周期内,需要完成对产品的一次迭代,这个周期在国内,普遍是两周甚至一周。
被压缩的工作周期意味着很多事情上线时并不是一个最佳的完全体,例如功能形态不一定达到最佳,所以需要做 AB 实验观察某一种功能形态是否更好。软件质量不一定是最佳,所以需要各种监控和修复机制随时待命。换句话说,如果没有这些对应的支持,也没办法很好的「敏捷」。
与之相反的工作模式,可以参考操作系统的开发,虽然近年来国产手机的操作系统也开始加入各种云控开关了。操作系统的特点是发布周期和维护周期都很漫长,以月甚至是年为单位,出现问题后修复的代价高,用户对致命问题的容忍程度低,且操作系统的功能和产品形态天然需要稳定,必须经过深思熟虑后再决定是否修改或添加某一项新功能。
以 iOS 为例,大版本的更新周期是 1 年,中途会发布 5 个左右的小版本进行问题修复和补充性功能更新。例如在 COVID19 爆发后,Apple 花费了 2 年的时间在 iOS 中添加了戴口罩解锁 iPhone 的功能。
与之对照,以百度地图为例,每个月都会有若干个小版本,每个版本的周期短至以周为单位,在发生热点事件后,可以在数日内快速上线相关的新功能。
两种开发模式并没有直接的优劣,这取决于产品的形态和目的。操作系统天然就需要追求稳定,而非功能的快速迭代。而互联网软件,有很大程度上比拼的是商业模式,所以快速迭代-快速验证的诉求就十分迫切。
变化给软件开发带来的最大问题就是不确定性。
没有人能确保某个功能未来是否会被取消或是进行扩展,基于这种背景,以客户端为例,发展出了组件化、混合开发、动态布局、热修复等等技术手段,而这些技术手段都是为变化而服务,确保在需要变化时,能以最快的速度实现变化的需求。
在面对不确定性时,除了尽可能的在工程设计上下功夫,还有一种原始但有效的手段,就是妥协。
软件开发无法追求 100% 的完美,没有任何人可以保证自己写的代码没有质量问题或性能问题。无法完美的代码如何达到理想需求的彼岸呢?在实际工作中,其实充满了很多妥协。
妥协不意味着在问题面前躺平,放弃抵抗,妥协是试图寻找平衡的一个过程,寻找那个各方都可以接受的平衡点,这里面充满了沟通和交流,实际上是人与人之间的一种艺术。如何让他人向你妥协。对于研发来说,其实就是如何说服产品缩减需求的细节,如何说服 QA 暂缓解决一些 Bug。
各司其职
随着团队规模的膨胀,高度分工是一件必然的事情。
以前自己开发一些小型软件的时候,一个人身兼从产品到设计,从研发到测试的各个环节角色。实际上最终做出来的东西质量是低的。要始终相信,专业的事情让专业的人做。
为了追求极致的软件开发效率和组织的灵活性,在现代软件企业中,也将分工推进到了极致,每个人大部分时候只负责自己最熟悉的事情,项目中不同职责的成员对项目共同负责,并在每个环节结束的阶段,通过明确可量化的标准,向下一个环节按时交付自己的工作成果,这实际上就是一种虚拟的流水线,每个环节都依赖自己的前置环节按时按质向自己交付自己需要的东西。
以客户端的交付为例。
以前在自己开发的小型项目中,交付的手段主要通过 TestFlight,从软件打包到 TestFlight 可下载,往往已经过去了小半天。
在企业中,有 CI/CD(持续集成与持续交付)机制,这一套机制有专门的同事负责维护,而我需要做的就是在代码提交时标记需要打包,然后一杯咖啡的时间,静待结束即可。但实际上这背后牵涉到许多团队的通力合作,这个过程哪怕对有技术背景的我而言,也和黑盒无异。我不需要了解其他人的工作原理就可以享受他们的成果,而我只需要专注于做好自己的事情,
同样的,换一个角度,我的下游团队也依赖于我提供质量足够好的阶段性工作成果(对我而言,这个工作成果就是软件包),这样他们就不需要了解我的工作细节。
这都是分工带来的影响。如果将不同的团队看作不同的软件模块,那么高效的分工可以让不同团队紧密的在一起协作,而不同团队又不需要知道彼此的工作细节和原理,不需要引入横向的复杂度。这就是所谓的「高内聚低耦合」在团队协作上的体现。在学校中学软件工程的时候,我怎么也不会想到这个概念还能应用到描述人与人之间的关系上。
番外:科学家和工程师
根据大众的朴素认知,研究科学的人叫科学家,研究工程的人叫工程师。所以计算机科学专业自然而然就是培养计算机科学家,软件工程专业就是培养软件工程师。
在现在国内的互联网公司中,一般很少有称呼科学家 Title 的,这是一个分量非常重的称谓,只有做出 Industry Leading 成果的人往往才担得起这个 Title。从事开发的人员一般统称 RD,也就是 Research and Development 的简写。其中 Research 指的就是科研开发人员,Development 指的就是工程开发人员。
从事科学研究工作的人员,一般称之为「算法工程师」,取决于研究的具体领域,还会添加前缀,例如「视觉算法工程师」、「推荐算法工程师」、「编码算法工程师」等。并不是说所有的科研人员都去研究算法了,而是研究算法的科研人员所占的比例是最大的。为什么只研究算法,一方面算法是很多计算机相关分支的底层原理,另一方面我国互联网的特殊环境也有一定的关系,这里按下不表。
算法工程师和一般的软件工程师的工作模式可以说完全是天差地别,我很有幸通过 2 份工作,分别与算法工程师和客户端工程师一起共事过,观察过两种工作的异同,而我自己目前也是一名客户端工程师。
从事算法的研究工作,需要大量的前沿资料摄取,思想碰撞,并不断的验证猜想,尝试获取更好的结果…不断循环这个过程,尝试逼近最优解。这类工作的「思考深度」往往也更深,人与人之间的协作相对来说没有那么紧密。
从事工程的研发工作,需要与业务方/甲方/上下游协作方进行大量的沟通,根据产品规格和需求,在指定的时间点,以指定的质量交付产品,后续继续维护和跟进,视情况进行迭代和优化,直至功能稳定,这就算完成了一个闭环。这类工作的「思考广度」会更广,需要考虑到的限制因素更多,尤其是业务规模复杂后。
还有一个有趣的不同,做研究工作,往往同一时间,一个人只负责一个问题的研究,绝大部分的精力都在同一件事情上,更考验人的「单核性能」。而做工程开发,常态化的状态是一个人负责多个不同进度的事情的推进,更考验「多核性能」。
两种工作状态之间并非有绝对无法逾越的壁垒,只是说不同的角色工作中的侧重不同,在特定的时机下重心是可以发生转换的。研究工作也有需要将研究理论落地到实际产品的时候;同样的,工程研发也有需要攻克技术难关的时候。
说了很多内容,但是还是感 觉没有完全表达出自己的想法。一方面是因为我不能详细描述自己所在企业中的工作流,另一方面也是因为自己的表达能力有限。
其实软件开发的模式有很多,在不同的模式下,对软件工程的诠释和应用也会有不同,目前我的思考都是基于已经历过的 2 份工作,随着工作经验的提升,我认为一些思考结果也会发生变化,当有一些全新的思考时,我会再次记录下来,希望这份全新的思考不要来的太迟,再会。
2022年8月20日
北京,夏暑渐散