再论软件系统的复杂性-没有银弹,只有焦油坑
作者:互联网
今天准备单独写一篇文章来谈软件系统的复杂性问题,特别是最近几年对于低代码开发平台,DevOps持续集成和交付,Serverless无服务器化,各种高级编程语言,包括AI人工智能兴起后,总会给人一个找到银弹的错觉。
所以在这里首先再次重申个人观点,即:
面对复杂的软件系统,没有银弹,只有焦油坑。
对于《人月神话》一书可以讲是软件工程和IT项目管理领域的一本经典著作,内容源于作者Brooks在IBM公司任System计算机系列以及其庞大的软件系统OS项目经理时的实践经验。我个人的网名人月神话也来源于该书。
如果你还是学生或刚毕业参加工作,实际上这本书里面很多内容你无法真正搞明白,我在2002年第一次看这本书就是这个感觉,而在当了几年架构和项目经理后再重读该书,才将很多内容真正读明白。
没有银弹和焦油坑这些概念就来源于这本书。这本书里面有几个观点我印象深刻,其一就是大家经常说的人月不能互换,还有就是类似外科手术对外的概念完整性和架构统一,再次就是里面提到的没有银弹的概念。
书里面明确提出了:
There is no single development, in either technology or management technique, which by itself promises even one order-of-magnitude improvement within a decade in productivity, in reliability, in simplicity。(没有任何技术或管理上的进展,能够独立地许诺十年内使生产率、可靠性或简洁性获得数量级上的进步。)
简单来说就是你不要期望任何技术和管理能力的提升,将软件研发研发生产率得到指数级的提升,即使到今天这句话仍然适用。
首要任务和次要任务
在书里面提到软件活动的首要任务和次要任务的关键概念,即:
所有软件活动包括根本任务——打造由抽象软件实体构成的复杂概念结构,次要任务 ——使用编程语言表达这些抽象实体,在空间和时间限制内将它们映射成机器语言。
软件生产率在近年内取得的巨大进步来自对后天障碍的突破,例如硬件的限制、笨拙的编程语言、 机器时间的缺乏等等。这些障碍使次要任务实施起来异常艰难,相对必要任务而言,软件工程师在次要任务上花费了多少时间和精力?除非它占了所有工作的 9/10,否则即使全部次要任务的时间缩减到零,也不会给生产率带来数量级上的提高。
对于首要任务,核心根本问题书里面谈到:
我认为软件开发中困难的部分是规格化、设计和测试这些概念上的结构,而不是对概念进行表达和对实现逼真程度进行验证。
如果你认可这点,那么软件开发始终就是困难的,没有银弹。同时书里面对软件系统的内在特性,复杂度,一致性,可变性和不可见性详细展开了阐述,在这里不再给出解释。
下面还是谈下个人对软件系统复杂性的一些关键理解。
从问题域到解决方案设计和映射
那么如何来理解上面这段话?
任何一个软件产品从研发到交付实际上包括了两个方面的重要工作。
1.从需求和问题域提出最终的架构设计和解决方案
2.基于架构方案进行编码实现和上线交付
我们可以把第一点理解为软件活动中的首要任务,即完成现实世界中问题域到解决方案域的抽象,而这个抽象本身又可以分为两个层面。
1.业务建模-将现实世界的需求抽象为需求模型或业务模型
2.技术建模-将业务模型抽象为可以进行开发实现的技术模型
在两次抽象中,业务模型的第一次抽象又至关重要,只有业务建模抽象合理后,技术架构实现的抽象才能够匹配。我们经常会遇到一个典型场景,即软件产品上线后发现对业务需求变更的灵活响应能力很弱,动不动就需要调整代码重新部署,而不是能够简单通过业务配置方式来解决。这就是典型的业务建模出现问题。
如果你只是实现一个你们公司的定制化报销系统,你会发现业务建模和架构设计都很简单,即问题域到解决方案域映射往往是1对1映射。但是如果你是做一个产品化的产品,类似ERP这种套装软件,你会发现业务建模异常复杂。
其核心原因就是面对不同业务场景下的多样化需求,你需要抽象出业务共性,找寻最核心的业务对象和领域模型。简单来说就是一个复杂的业务流程,完成需要多个业务对象之间相互交互和集成,才能够完成。你需要搞清楚有哪些业务对象,然后搞清楚各业务对象间如何协作。
业务建模和技术建模能否分离?
基于前面的阐述,可以看到业务建模和技术建模实际是完成现实世界问题域到抽象软件实现的关键抽象。
在早期可以看到这两件事都是由类似系统工程师,系统分析员的角色来完成。而现在两者逐步分离,由独立的业务建模或需求分析人员,也由独立的架构设计师。
但是这种分离却出现了新的问题,业务建模人员往往没有足够的技术储备来完成这种建模和抽象,而技术人员往往技术驱动只做技术架构设计而忽略了业务。
在人月神话里面一直在强调,需要有一个类似外科手术医生这种角色来完成架构设计,以保证高度的概念完整性。但是这种建模设计工作被拆分掉了,导致后续整个软件系统设计模型本身在扩展性,可靠性和和需求满足方面出现问题。
软件活动为何不能形成流水线工厂
一直以来,在软件领域有大量的人投入大量的精力在研究将软件研发活动自动化,或者说将软件研发活动变成流水线工厂。
但是截止到今天,没有任何人,方法或工具取得突破。
任何一个软件系统可以看到,面对现实世界的业务需求和抽象,形成了软件实体和操作接口等,但是这些内容基本没有任何可重复性和可复制性。如果一个工作可重复,那么就一定可以自动化。但是实际可以看到对现实世界的抽象这个动作很难重复,也很难完全自动化。
软件开发的核心要素是人,人本身是感性的动物而非机器,人和人之间还需要沟通和协同,这些又进一步加剧了整个软件活动的复杂性。
如果我们将软件活动进行分解,可以理解为:
现实需求-》业务建模-》技术建模-》实现交付-》批量交付
那么对于类似批量交付光盘这种动作才能够对应到软件流水线工厂,这是完全的机器能够完成的事情。截止到今天,大量的类似MDA,类似快速开发平台和低代码开发平台,实际在做实现交付的工厂化。
但是前面谈到的业务建模和技术建模这个首要任务,仍然难以批量复制。
产品研发的模组化说起
如果一个产品生产工厂要实现流水线和自动化,那么这个产品本身用到的各种元器件,材料要足够的标准和模组化。如果你自己想去开一个工厂,那么需要的就不是元器件这个层级的标准化,而是需要半成品这个层级的模组化,这样你才可能快速组装。
华强北可以说是山寨机的鼻祖,为何在2005年左右时间野蛮生长并发展迅猛,这里面不得不说的就是联发科的贡献。
2005年,联发正式推出了"交钥匙方案",帮助完成芯片、软件平台和设计,手机制造商只需购买屏幕、摄像头、外壳、键盘和其他简单部件就可以生产手机。因此,这种更方便的包装方案,受到了山寨手机制造商的热烈追捧,山寨手机市场蓬勃发展。
也就是说你只要投入一个几百万,你完全可以自己搞一个工厂生产山寨手机。
也就是说山寨机的生产变成了简单的产品模组的组装,你唯一需要考虑的就是产品外形或工业设计等方面的一下简单内容。
各个产品模组可复用性极强,而且联发科由于出货量巨大,产品模组本身也足够稳定。
由于手机本身不是属于一个太复杂的产品或系统,同时由于联发科的贡献,彻底实现了手机产品制作的跃迁。也就是实现了产品研发和生产制造的分离。
手机生产厂商你可以不用具备研发能力,完全只是组装即可。
那么从手机到汽车生产制造,同样出现了类似的跃迁,这个跃迁简单来说就是新能源汽车带来的革命。即你不再需要复杂的发动机,变速箱和传动系统,所有的都是电力驱动足够简单,汽车的生产同样也可以像类似手机生产一样组件化和模块化。
类似现在你可以看到的,大量的非传统车企开始造车,开始接入这个行业,包括恒大汽车,包括最近发布的小米也准备进入到汽车制造行业。
简单总结就是一个完整的流水线工厂往往具备两个条件。其一是研发和制造分离,研发仅仅由少量技术驱动型企业来完成;其二就是产品足够的模组化,可以灵活的组态。
软件活动是否具备这两个特征?
再回头来看软件活动,一个完整的软件研发实际上涉及到软件开发框架和软件技术平台,其次是平台上面的软件功能模块实现。
对应技术平台由于和业务无关,因此类似数据库,中间件,包括开发框架环境等可以做到足够的通用化可复用,你不再需要通过类似汇编语言来开发应用,这是一个很大的进步;但是平台上面的业务功能实现层,却很难做到足够的通用和模块化。
也就是说这些业务模块的开发往往很难复用,和业务场景和需求强相关。
你很难将软件研发中的研发活动和软件生产制造活动分离开。比如对于CRM领域,存在一个类似联发科的CRM各个组件模块的生产厂家,然后又衍生了大量的基于这些组件模组来开发垂直行业的CRM应用的厂商。
那么我是否可以在IT系统建设到一定程度后,通过已有IT系统可复用的API接口能力来快速的组装,编排新的应用呢?
这个就是我们常说的SOA架构思想里面的关键概念,基于服务组装编排来实现新的业务和流程。这个看起来挺不错,但是仍然逃不脱前面谈到的软件活动的首要任务。
即你按照SOA参考架构和BPM的做法,你首先是要将业务流程进行BPM流程建模,比如通过BPMN流程建模语言对业务进行抽象,这个抽象本身又足够复杂,这个首要任务无法通过系统自动来完成。
在过年前我和一个做大中台规划的厂家交流,他们提出一个宏大的产品规划构想。即将企业需要的所有IT能力全部识别和实现为一个个独立的原子服务,构建覆盖企业所有业务的大中台能力服务层。以后业务人员自己就可以上去通过拖拽原子服务的方式完成新业务,新流程的配置,原子服务足够可复用,应用扩展也不再需要开发人员。
这是不是有点找到银弹的感觉?
现在你可能看到的就是大量没做过大型软件研发,软件项目建设实施的人在忽悠客户实施上面的产品规划和架构。拿着一些高大上的概念把客户忽悠得团团转。
当你考虑前面的软件复杂性首要任务的时候,你就很容易看到原子服务本身的可复用度服务保障,新业务的抽象建模无法落地等关键问题。所以注定这些思想都是空中楼阁。
再谈低代码开发平台
对于软件活动可以理解为:
现实需求-》业务建模-》技术建模-》实现交付-》批量交付
当前很多低代码平台本身也是同样思路,即你只需要进行建模和配置,然后软件功能自动实现,自动部署和交付完成。
这里面的关键点在哪里呢?
即业务建模和技术建模本身的复杂度问题。
如果仅仅是一个简单的请假单,挂接一个人工审批流程,这种场景你当然很容易完成业务和技术建模,表单配置,并自动化实现和交付。但是如果你面对的是一个复杂业务的实现,里面涉及到多个软件实体对象,包括实体对象之间的复杂关系和协同。
在这种场景下先不说能否做到完全自动化的实现,仅仅是业务和技术建模就不是一个般人能够完成的,那么低代码开发平台本身也无存在的意义。
同时,对于要给大型复杂系统的实现,除了最基本的业务功能实现,还包括了软件系统的可靠性,性能,可扩展性等诸多非功能性需求。一个OA系统你可以看到,市场上5万块钱你也可以买一套,但是很多集团企业的OA系统建设往往上千万。
差别就在于大型集团的系统对高并发,高性能的要求导致整个技术建模和架构复杂性的增加,你可能会引入类似分布式,消息中间件,缓存,全文检索等诸多的新技术,这些都间接进一步增加了软件系统的复杂度。
软件系统的复杂性的应对
如果说大型软件系统的复杂性应对,只说一点的话就是分而治之。
当你面对如此场景的时候一定考虑的是分而治之,首先进行分类和大区域的划分,比如引入相关的归类装置进行简单的归类整合。
即使这样你还会发现一个归类里面还有很多的网线,为了进一步进行区分,你还需要进行抽象或者标签化。
斯坦福大学计算机系教授John Ousterhout在《软件设计哲学》一书专门谈到一点,降低复杂性的基本方法就是把复杂性隔离。"如果能把复杂性隔离在一个模块里,不与其他模块互动,就达到了消除复杂性的目的。"
这实际和我前面讲的事物认知,理解和分析事物的思路完全一样。即当你面对的是一个复杂事物的时候,你一定需要对事物进行分解,分解为几大块后各个攻破,然后再来考虑分解后之间的组件如何协同去完成一个业务目标。
最终的企业信息化软件并没有分出这么的子系统,就是一个大系统。但是后续随着复杂性的增加细分了诸多的业务子系统出来,类似SRM,CRM,ERP,MES,WMS,CMS等。乃至SRM系统构建中你觉得招投标模块复杂,还可以基于招投标模块单独构建一个子系统。
当前大家谈得多的是微服务。
即使是现在的类似CRM这种单体系统也足够复杂,还需要进一步细粒度拆分。实际上前面一个大系统拆分为多个子系统同样是微服务的思想。
那么一个大型软件系统实际最复杂的点就在于,究竟应该拆分为几个子系统或组件,各个组件之间应该如何去集成和交互,来完成一个业务流程或需求场景。
即我们前面谈到的业务建模和技术建模的关键动作。
当这个步骤完成后,剩下的事情往往仅仅是单个业务功能模块里面单个功能的实现,API接口的设计和开发,并没有什么技术难度。
这个建模谁来做?
即前面谈到的类似外科手术医生,具备业务+技术双重能力的软件架构师。
当前的架构师往往更多变成了技术驱动的纯技术架构师,核心能力在搭建一个技术平台,搭建要给分布式,高可用的集群系统。而忽视了业务架构和业务建模,同时导致了业务建模和技术建模完全脱节。
在完成这个核心建模后,你才能够考虑提升效率的点。即前面谈到的软件活动里面的次要任务,这些次要任务都是可以考虑提升效率或做到完全自动化的点。
比如核心的技术组件和技术平台,这个往往和业务无关并且可以复用。再比如软件代码的自动生成技术,功能的灵活配置技术。当然也包括了当前我们谈得比较多的采用DevOps后的持续集成和持续交付自动化能力提升等。
但是你要意识到这些始终是次要任务,无法实现指数级的软件效率提升。
分布式架构进一步引入复杂性
在软件活动的早期,特别是传统IT架构模式情况下,实际上对于技术架构往往并不复杂,即数据库通过类似HA架构,应用层通过集群架构来解决了高可用和扩展性问题。
当你采用的是一个非分布式基础设施的时候,比如你按照的Oracle数据库,这个时候是同时满足了高可用性和一致性的,即CAP中的C和A同时满足。
随着整个IT信息化的发展,业务量和并发的需求扩展,包括当前云原生,中台,微服务等各种新技术和架构的引入,特别是分布式架构的引入。你会发现技术架构的复杂性进一步提升。比如常说的在一个分布式系统,CAP里面高可用性和高一致性你往往并不能兼顾。
要么是满足CP优先,要么是AP优先。
一个分布式架构的引入,虽然提升了性能和可扩展性,但是却大大增加了分布式系统本身的可靠性,开发的复杂性,后期运维的复杂性。
一个传统的企业,当你传统IT架构都做不好的时候,一定不要轻易的进入到类似前面谈到的微服务,分布式架构引入这个阶段。简单来说引入分布式架构后复杂性变成了两个部分。其一是前面谈到的业务和功能性架构建模。其二是纯技术架构设计和实现。
你会看到软件研发的精力被分散,本身应该将精力放在系统分析和设计,业务建模上。但是由于技术复杂性引入,你将更多精力放在了类似SpingCLoud微服务框架,服务限流熔断,分布式事务一致性解决这些事情上。
比如我们常说的很多企业实际在传统的IT规划和IT架构上本身就存在业务系统划分不合理,接口设计不合理,集成混乱,主数据不一致等诸多问题没有解决。这个时候你应该优先去考虑解决这些问题,而不是将传统单体进一步拆分为微服务,或者引入分布式架构来加入更多的复杂性内容。
从现实世界到抽象世界的映射
再次说明下对于软件活动可以理解为:
现实需求-》业务建模-》技术建模-》实现交付-》批量交付
那么在技术建模完成后到实现交付是否可以做到完全的自动化。在前面我已经表面了一个观点,即对于简单系统可以,但是对于复杂系统这块也很难完全自动化实现。
MDA(Model Driven Architecture)是模型驱动架构,它是由OMG定义的一个软件开发框架。它是一种基于UML以及其他工业标准的框架,支持软件设计和模型的可视化、存储和交换。和UML相比,MDA能够创建出机器可读和高度抽象的模型,这些模型独立于实现技术,以标准化的方式储存。
MDA包括PIM(Platform Independent Model,平台无关模型)、PSM(Platform specific Model,平台相关模型)和代码。PIM是具有高抽象层次、独立任何实现技术的模型。PIM被转换为一个或多个PSM。PSM是为某种特定实现技术量身定做。
对于MDA实际希望做到的就是建立完整的PIM和PSM模型,然后能够实现代码自动生成,功能自动实现,实现抽象模型和应用实现的统一。
但是实际情况这应用得并不好,包括现在MDA提起的人很少了。
也就是说从抽象模型到软件实现,这个过程同样复杂,并不是想象的那么简单。简单来说如果你希望能够完整地应用自动实现,那么你在通过PIM或PSM来描述你的模型的时候,你同样需要花费大量的时间和功夫。
比如一个规则引擎,当面对复杂规则的时候你会发现对规则的脚本化描述工作量实际还大于实际通过代码来实现规则。在通过JUnit进行自动化单元测试的时候,你会发现如果要实现全业务和流程覆盖的测试,你写的单元测试代码的量往往超过功能实现源代码。
如果仅仅定期清楚需求或实现到PIM,你会发现同样一个需求或模型,当你分发给两个不同的软件开发团队开发的时候,实际最终实现的应用代码千差万别,很难找到任何共性可复用的特征。
也就是说现实世界和抽象世界之间并没有真正实现完整映射。
你虽然可以通过类似可视化,原型驱动开发等方式,但是仅仅停留在界面一致性,而内部功能逻辑实现代码仍然差异极大。
有趣的皮囊千篇一律,但是好看的灵魂却五花八门。
但是一个建筑,当设计师设计好详细的图纸,缺点好材料后,你却可以做到分发给不同的建筑公司都能够实现基本一致的实物。特别是在当前BIM模型进一步推广的时候,这个更加能够做到所见即所得的效果。类似的例子还有在数字化转型里面,智能制造中的数字孪生技术应用,真正实现了抽象模型和现实世界的统一。
但是在软件活动中,我们并没有找到类似的方法。
标签:架构,实现,焦油,业务,建模,抽象,软件,银弹,再论 来源: https://www.cnblogs.com/ebuybay/p/15858633.html