Hello~,上篇文章收到多位读者的鼓励,在此表示感谢!受其中一位上海交大毕业现“鹅厂”工作的读者启发,写于此文,总结了软件工程的“三重门”及“摩尔定律”。
在开篇之前,想先回顾下这周小米十周年雷布斯的公开演讲,作为程序员技术出身的雷布斯可谓是写得一手好“诗”(代码),软件界笔者最佩服的人之一,当然还有“中国第一程序员”之称的求伯君(据说其在24 岁的时候在常人难以忍受的孤独中,用汇编语言写下了十几万行代码),2000年前的金山科技想必是如雷贯耳(1989 年, WPS 1.0 发布,填补了我国计算机中文字处理的空白),其"WPS"全家桶说实话某些方面比微软三大件做的还要好,想想看,在计算机软硬件不成熟、网络不发达、电子非电子资料木有的年代,能把一行一行的代码手把手敲成一个产品,属实不易,实在佩服,哪像现如今,反手就是一个“copy”。雷布斯因为代码撸的好受求伯君赏识进了金山,临危从技术到CEO,创业多次仍然激情满满,如今小米的生态圈也属实在国内创造了许多第一。演讲很激奋,在这里笔者截取一段share一下:
2020 年,非同寻常的一年,全球都在面临巨大的挑战。这场全球大变局,深刻影响着我们每一个人的生活。大家都在问,“面对这样的局面,我该怎么办?”很多人都很焦虑,其实我也很焦虑。
但焦虑没有用!这个世界有很多事情是我们改变不了的,但我们可以改变的是自己的心态。我们下决心改变自己的心态,才能积极面对这个复杂的世界。
我选择了一种最简单的方式去改变,走路!
我定了一个目标:每天走10公里,先走一个月吧。这个目标对我来说,还是很夸张,我也很怕自己做不到。
但没关系,向着太阳,一直走,你就会重新感受到内心的平静和温暖的动力。向前每多走一步,热爱和勇气就会多一分。
生活中还是需要一些仪式感,生活更需要积极面对。
回到主题,何为软件?工程界最常听的两个词:硬件、软件。--“你是搞软件的吧?硬件不懂?”、“硬件搭牢固了,软件才好搞”、“我发现硬件基础决定上层建筑”、“这是软件的bug,这锅我硬件的可不背”、“你是搞硬件的也要写代码?”······似乎“硬件”“软件”这两者涵盖了整个“计算机工程”,甚至有初创公司以为一软一硬就能打造一款像样的物联网产品,可能外加结构、美工、测试(可省)、运维就更别说了。这里也打个岔,我GF会经常问我:“FS,在干嘛?” 我:“······(可能过了许久)刚在敲代码” GF:“······你不是搞硬件的吗” 我:“······我,,,“全栈””
所谓软件,在笔者看来其是由代码封装的实例,分学术研究和工程应用,可大体划分为互联网软件(基于Win、Android、Linux、ios等平台开发的业务应用(BS/CS架构、移动APP······))、嵌入式软件(基于ARM、RISC-V等内核架构打造的处理器平台做裸机、RTOS、NRTO软件开发(如设备(一切接口皆外设)驱动、嵌入式应用 、GUI 、通信协议栈固件等)、OS软件(平台类软件如win、鸿蒙、红旗等)、”内核“软件(封装可以为第三方提供API或开源,如浏览器、SDK、深度学习框架、解释器、EDA等)、数据库软件等等等等。当然,对软件的分类没有严格的定义,亦或者”萝卜青菜、各有所爱“,因为工程学涉及的知识实在太广,有很多也不在笔者的认知范围内,而且大小公司体制不同(包括行业、业务类型),自然软件技术岗位的职责也不同,小企业一岗位可能身兼多职(电路设计是你、PCBA layout是你、软件驱动还是你)(前端设计是你,后台业务是你,环境部署还是你),大公司则更多要求在”专“;of course,有些企业做的产品其实很简单,软件层几十或几百行代码就能构建成型,而有些大型产品却要几百万行甚至几千万行。
所以,小公司的”身兼多职“可能很累,大厂的可能“螺丝钉”工作可能很枯燥,但不管怎样,在笔者看来,一名合格的技术人在技术上应有“精”度作为质的保质,还要有“广”度作为量的优化,同时工程学技术本质上原理是相通的,要善于去做知识连贯。还有程序=算法加➕数据结构?这句话的算法不是指查找、排序等狭义的算法,而是“逻辑”,业务逻辑也是算法。大多数程序员也许一辈子也不会去写狭义的算法,只有少数算法岗经常和狭义算法打交道,软件从业员的考试和认证,应该和工作相结合,不能脱离工作。
何为软件的“三重门”,这个是笔者对做好软件方法论的一种命名,是经过自身知识认知以及和圈子内的实践认知做了系统性的思考总结,主要依据大厂体制的软件工程,像华为、腾讯、字节跳动,有些软件系统的研发可能需要一年甚至多年才能上市,多部门协同研发,“三重门”亦是做软件的基本原则:
**1.复用**( 不要再发明相同的车轮子了)
复用,就是具有一定集成度并可以重复使用。写代码门槛不高,菜鸟上来也能编几行。复用却很难,思考代码的复用,不仅是一定水平,还是一种美德。重复造轮子是很大的浪费,复用是提高质量与生产率的最佳手段。复用在软件中无处不在,大到平台组件,小到函数和数据结构。软件的设计,大部分工作都是对复用的设计,做好软件,从复用开始。
**2.分而治之**(把复杂问题分解成若干个简单问题,再逐个解决)
复杂是一个中性的词,处理复杂性?软件处理中,第一步是把它分解成可解决、可衡量的问题。分解的过程要符合两个要素:(1)每个分解问题能否用程序实现?(2)所有程序最终能否集成为一个软件系统并有效解决原始的复杂问题?
分解的方法就是架构,架构有很多种,有兴趣的童鞋可以看看软件《架构入门》(软件的架构模式和设计模式还不一样。经典的架构模式超过20种,设计模式更多,据说超过300种,软件架构就是软件的基本结构。架构的本质是管理复杂性。如果你觉得架构不重要,可能你做的事情不够复杂,或者是你没有管理好复杂性。架构模式虽多,但常用的适合ICT软件,也就那么几种:分层架构,(最常见)事件驱动架构,微核架构,微服务架构,云架构)分而治之,就是软件的方法论。
**3.重构** (降低腐化的遮减过程)
重构代码一时爽,生产运行火葬场?
软件是个弹性的系统,在功能演进、bugfix的过程中,架构和代码腐化不可避免。就如一个集装箱,在出港时,里面的货物放的整整齐齐,但在途中不停地卸货,装货,就变得乱七八糟。唯一的解决办法就是定期重整。
重构是代码不断增加,删减,修改后的再次有序化过程。从技术上,笔者总结出有两个方法可以遵循:
(1)消除重复:代码在变化后,原来的复用设计可能部分失效了,就要重整复用,消除重复,尽量将重复代码归一,最大化实现结构体和代码共享,在消除重复的过程中,补齐重构后需要的测试防护网。
(2)特性和组件解耦:代码在变化后,原来的边界设计也可能部分失效了,需要重整依赖和调用关系,比如:
辨识业务变更方向:专有/公共代码(特性),降低耦合;
向稳定的方向依赖:基于产品/业务专有最大化,以及依赖范围最小化,抽象出来的应该是最稳定的公共组件接口;
缩小依赖范围:为了使业务特性跨组件的高效交付,最小化被依赖的接口。
何为软件的“摩尔定律”,摩尔定律是由英特尔(Intel)创始人之一戈登·摩尔(Gordon Moore)提出来的,半导体集成电路有,软件亦可有。
软件的“摩尔定律”,是不断提升软件效率,以降低软件的开发成本和上市时间。在软件“摩尔定律”的驱动下,诞生了各种软件开发语言、架构、工程和模式。每年都有新的编程语言出现。设计模式,据说超过300种,各种眼花缭乱的概念和技术,有些还相互矛盾,真伪难辨。借用政治经济学的一个说法,符合生产规律的,就促进软件生产的发展,不符合软件生产规律的,就阻碍软件生产的发展,软件生产的规律是什么:减少开发人员之间的等待和依赖。
《人类简史》上说,一个猩猩自然群体中的个体数,一般不超过20~50个,人类高等一点,增加了八卦等链接力,也不超过150个,再多就要靠文化、制度、意识形态等虚拟的东西来连接了。作为一个软件开发团队,也不是越大越好。团队规模越大,开发人员之间的沟通、配合、等待和依赖越多,效率就越低。但开发人员太少也不行,靠什么来解决这个软件生产上的矛盾呢?
先来回答一个问题,什么是架构?越熟悉的词,越不好找一个准确的定义,这里抛一个我觉得最接近的,Architecture一词最早来自建筑,是在复杂场景下,解决人的分工、沟通和配合问题,以达到更高效率和更高目标?软件架构就是解决软件生产过程中人员的分工、沟通和配合问题,以及软件生命周期中的维护和扩展问题。显然,解决这个矛盾,架构很关键。
架构让生产人员在技能、工序、配合上充分解耦。盖一栋楼,先是打地基搭框架的人上,然后是砌墙的人士,最后是装修的人士。做软件也一样,如果每个人能够在不相互等待和依赖的情况下,把自己负责的部分做好,拼到一起就能run,那就是最理想的情况。
从模块化,组件化到服务化,基本也是顺着这个思路在发展。一个团队,接到一个开发任务,先做需求分析,系统分解,每个人分一个模块,各人先聚焦自己的模块。在开发阶段,基本是可以做到解耦的。但在验证和运行阶段,麻烦就接踵而至。一个模块的验证要等待其他相关模块就绪后才能进行,一个模块的修改可能会依赖另一个模块的同步修改,一个模块的问题可能会导致另一个模块崩溃。随着时间的推移,血肉模糊,纠缠不清,无数软件开发人员的大好青春就浪费在这了。
经过血泪的教训,开发人员自然会想到,把一些公共的功能和框架提取出来,做成组件,提前开发并验证好,确保组件部分没问题,然后再基于组件,增加适配代码去搭建系统。相比初始的模块化,组件化在效率上肯定有优势。但组件并不能端到端地独立验证和运行,并没有从根本上解决耦合和依赖问题,组件化本质还是一种模块化。由于组件共享程度高,依赖大,还容易成为瓶颈。
服务化架构是在此基础上的进一步发展,不但是在开发阶段解耦,而且是在验证和运行阶段解耦。为什么在验证和运行阶段解耦很重要?老开发人员知道,一个全新系统的开发,耗时最长往往不是开发阶段,而是联调阶段。联调特别耗时耗力,因为各部分都不能独立验证和运行,需要集成在一起才能验证和运行。联调中耗时最多的就是等待。服务化,就是希望每部分尽量都是一个自治的系统,能够独立的开发、验证和运行。一个服务内部的修改不需要其他服务同步修改,一个服务内部的问题对其他服务的影响降到最低,把开发人员之间的配合,等待,依赖和影响降到最低。模块化到服务化开发模式的区别。
服务化架构(SOA)早就有了,为什么近来微服务很火?道理也很简单,当前微博比博客流行,微信比信流行,之后就能有更多的应用场景。深层次的原因,感兴趣的可以去探究。
模块化、组件化 、服务化只是架构的一类模式。基于适合的场景,尽量减少开发人员之间相互的等待和依赖就是好架构。
问题要早期暴露和快速修正,就像写完一篇作文后就检查,发现的错误立刻修正,效率最高,如果等几个星期甚至几个月后再去检查,灵感和思路全无,效果肯定不好,这是生活中的一个常识,同样适合于软件生产。软件生产很重视代码review,UT,IT,ST,UAT,生产过程从瀑布模式到迭代模式,这些都是为了问题早起暴露和修正,但光靠这些不够,还是会有缺陷遗留到后端,让问题在开发过程中早期暴露,快速修正,看似一个常识,但可怕的是,有些开发人员在泥潭中迷失了这几个常识。
不管是多么先进时髦的软件架构,工程或技术,都不要脱离软件生产的本质,或者是常识,这些常识是不依赖于需求,不依赖于场景。架构,工程或技术都是有场景的,在一定的场景下,符合这些常识的架构,工程或技术,就促进软件生产的发展,不符合这些常识的架构就阻碍软件生产的发展。
软件的本质就是拥抱变化,管理变化。什么是变化,变化来自于商业、业务 、交付与使用的下游对象,姑且称之为终极目标吧,终极目标到软件的信息传递会失真;所以,真正要做的是,不管是商业的方法,业务的方法,架构的方法,目的就是缩短终极目标与软件之间的链条。
做好软件并不难,贵在坚持和实践。
最后借雷布斯的一句话:“热爱是所有的理由和答案”,但是,现实中很多人压根不知道自己热爱什么,或者由于种种原因,可能无法从事自己的“热爱”。在哲学里,“热爱”其实是有方法论的,舍得为你喜爱的事物花时间,这只是第一步,接下来的第二步更关键:持续不断地为你喜爱的事物花时间。很多人好像忘了,热爱是要付出长时间的精力和心血的,我们在一件事上花费的时间越多,我们就会越热爱这件事。反之,如果你什么都只试试就算了,那么永远都找不到一件可以让你全情投入的事。不管对人还是对事,你投入的时间和感情越多,你就会越热爱他(它)。那些没有付出时间的热情,很快就会消退,唯有不断地投入,才能巩固你与所爱事物之间的关系。当你越投入,这件事情带给你的回报就越丰厚,你会享受到难以言喻的快乐和成就感,从而促使你进一步投入,渐渐形成了良性循环。
成功不可复制,但一切有迹可寻,所有的技术人儿与非技术人儿,加油!