的人越多,代码反而变得越烂似乎成为了趋势;为什么多人协作没有提高代码质量呢?...
作者:borisyang,腾讯 WXG 应用开发工程师作为程序员,刚刚开始学会写代码,常常是接过需求就开始撸代码。有时候发现,写完代码,需求变了。更多时候,觉得写业务代码枯燥无聊,没有技术含量。另外一边的事实却是,项目里面研发人数变多了,项目的质量缺却变低了,多人开发也不过是一个个单打独斗的组合而已。1 研发环境日益成熟经历过 PC 互联网的不断深入发展,移动互联网的蓬勃生长,互联网进入了成熟繁荣期,研发环境也发生了巨大变化;从原来一个人,一把键盘,写完代码就上线,变成了更加规范的研发体系和更多人参与的共同协作。研发流程不断加速为了尽可能提高需求交付速度,跟上市场的变化,我们通过不断提搞软件交付的速度,尽可能的,从需求,编码实现,测试,发布的流程中不断优化,利用 CICD,加速迭代。多人协作无处不在现在的软件开发团队,即使再小,也有 2-3 人一起研发,更别提测试,运维,运营,产品人员。一方面是软件产品的竞争日趋激烈,需求日益复杂,堆砌人力成了必然;另外一方面是专业性的要求,精密的行业自然要求精细化的职业划分。2 困局研发流程的速度提上去了,团队的人也变多了,但是,需求变更依旧让广大研发同学感到痛苦,项目质量还在日益变差。需求变更之痛需求变更的痛苦为难了广大研发同学,前脚刚为了优化性能,采用了 kv 存储,后脚需求就变成了要支持模糊查询;这是一种典型的架构设计不合理,导致业务需求的实现方式受限。更令人痛苦的,还有产品需求变动多,今天简单实现下,上线看看效果,明天用户脾气很大提了个诉求,再加一个功能上线,产品功能变成补丁加补丁。一方面是研发同学渴望一个完整又严谨的需求,提完需求进入研发阶段就不许改;另一方面是产品同学受到各方面的压力,只希望先把主要问题解决下,细枝末节以后再说。项目质量变差项目质量变差,一部分归功于补丁代码的产生,迫于时间受限,先上一个补丁,却打开了破窗的先锋,下一次,下一个同学就更敢于加补丁代码。一个个临时的 if else 不断堆砌,最终导致了整个项目的代码腐烂。我曾经维护过一个代码片段,超过 20 个 if else,中间还有些过时的错误注释夹杂其中,维护起来令人苦不堪言。项目代码腐烂的另外一个原因是多人协作,团队的人越多,代码反而变得越烂似乎成为了趋势;为什么多人协作没有提高代码质量呢?一方面,多人协作实际上只是分摊的需求实现而已,大多数需求实现的分配中,反而尽可能将协作变少,避免实现受阻。另外一方面是,不同人的代码模块,设计意图和代码风格也截然不同。维护前人代码,如果没有全局视角,了解设计意图,也只能是往里面加补丁代码了。项目代码腐烂容易导致程序员出现错觉,一是业务代码没什么料在里面,不如搞基础建设;二是业务需求不可能完整又严谨,最终也会变来变去的,最终质量低下的锅,一大半要给提需求的人。3 怎么办在写代码之前要进行设计和建模。相比历史短暂的 IT 行业,很多工业,建筑行业的精密性,都离不开前期的设计,在分析设计之后,按照图纸规划施工,写代码也应当如此。
设计建模的有效性源于,一,重新回到业务的跑道,跟业务一致;二,设计建模才能让协作真实有效;为什么研发实现需求跟业务一致很重要呢?研发和业务需求的摩擦,本质是研发实现跟实际需求不一致,无论是研发走偏了,没有理解需求,还是需求本身不能满足涉众的利益,都会使得最终上线的功能需要回炉重造,折磨项目组的成员。业务项目,需求很重要,是整个项目质量的源头,源头的问题不处理好,会一直发散扩大,问题传递到尾部,甚至到了产品上线,对整体造成的损耗越大。从设计的语言上看,设计的层次有所不同,不仅仅有代码细节上的设计,也有业务上高层次的设计,高层次的设计是用业务的术语去表达,最贴近业务实际情况,也能帮助研发同学发现业务中不合理的点。设计建模为何能让协作真实有效?我早先体验的协作流程,无非就是各自工作在自己的领域内部,彼此尽量减少要协作的内容,避免过多阻塞。研发侧的协作,因为缺乏设计,不好分工,另外一方面,多人写同一个模块,也会引发冲突,所以更多将协作放在在 code review 上。但 code review 作用范围也有限,一方面,review 成本较高,逐行阅读代码来厘清设计对代码质量要求很高,另一方面,review 时间节点往往发生较晚,临近发布的时候,调整设计也不大可能。进行设计建模能够让协作变得有效,一方面,设计建模前期是沟通和信息对齐,将协作的内容提前,一方面,采用合适的图形化工具,review 的成本是相对较低的。4 怎么设计和建模设计和建模分为好几个部分
[*]业务建模,关注业务,不关注具体的实现
[*]系统建模,关注所建设系统的边界,找准在业务中的系统的职责和约束
[*]分析与设计,定位核心领域,找到实际的类,厘清类的职责和类之间的关系,通过设计使得代码抽象复用
业务建模总体上来说,业务建模主要聚焦于分析涉众利益,厘清业务流程。从工具上来说,主要是用例图,流程图;从内容上来说,主要是找人(利益涉众,系统执行者),找业务实体(其余系统,相关的重要对象)。分析涉众利益分析涉众利益之前,需要找到涉众,一般要经历以下步骤:
[*]找到软件产品的愿景,愿景表达了软件产品带来的核心意义
[*]找到利益相关的的涉众和其利益诉求
表格是一个很好的表达方式,我负责的一个商户从第三方商城采购刷脸设备,由仓配系统配送设备的业务,可以表达如下:愿景:将设备更多更快且准确无误成本低地卖给商户。
涉众利益诉求
物料组老板在准确无误的情形下,更多更快成本低地将设备卖给商户
设备渠道商准确无误且快速地配送设备给商户
商户更快地获得自己购买的设备
物料运营更加准确的配送设备给商户
一般而言,涉众的利益是否被满足直接决定了软件产品的成功与否。而分析涉众利益需要进行详细的调研,研发同学可以根据产品的调研看到对应的涉众,及其利益。业务用例图知道了涉众的利益之后,就要分析业务流程,并对现有的流程进行改进。软件产品没诞生之前,业务是如何被处理的,找到原来业务的处理方式则可以梳理出业务用例。笔者负责的一个业务是向购买设备的商户配送设备,对于业务团队的实际业务来说,用户购买设备有业务价值,业务用例如下:
物流公司和设备渠道商都是辅助购买设备的执行者,因此放到右边。值得注意的是,业务用例要体现价值,虽然在实际业务流程中,商户同时做了很多事情,比如签收设备,但签收设备不能反映业务价值,故而只有一个购买设备的用例。业务流程分析了解了涉众的利益并且画出用例之后,需要分析业务流程,找到我们软件系统能够改进的流程片段;完整的业务流程图可能很庞大,需要关注的是其中最有可能影响涉众利益的流程片段,如下为购买设备业务流程中配送设备的流程片段,该片段不大符合涉众利益;
配送设备的业务流程可以看到,在原来的业务流程中,配送设备的流程是在全部业务流程中较为繁重,人肉工作量大的流程片段,不符合涉众利益;
[*]收集商城订单信息不及时,导致配送不及时,影响涉众的利益
[*]人作为节点参与处理,成本高,耗时长,也是不符合涉众利益的
所以很明显,我们的系统需要改进流程,替代人的部分工作
新的流程有效地满足了涉众“将设备更多更快且准确无误成本低地卖给商户”的利益诉求。业务序列图中,每一个箭头代表的是职责,在业务序列图中,需要考虑的是职责的层次问题,过于小的职责放入流程图中,会导致信息过载,忽略最有价值的职责。在上图中,运营核对设备的配送信息是一个很重要的职责,在原来的需求中体现比较弱,研发同学可以借助业务流程的分析来分析需求中不合理的地方,完善需求,避免后期的改动,前期越是完善,后期的损失成本越低。业务流程分析是一件很复杂的事情,研发同学可以利用需求中的信息,同时加上自己跟涉众的日常沟通和调研,把握核心的涉众利益,业务用例和业务流程,就可以解决大部分在需求上的理解偏差问题。系统建模系统建模关注的是系统与外部的边界和系统自身的职责系统建模需要做的事情
[*]画出系统用例
[*]写出用例规约
系统用例图系统用例图是业务流程中,系统执行者与系统发生的有价值的交互。系统执行者可以是人,可以是外部系统,甚至可以是时间。系统用例要体现系统的价值,系统会做很多事情来实现业务价值,我们应当关注业务价值。有些是低层次的职责,没有体系具体价值,如:“获取商城订单信息”是为了配送订单中的设备而发生,应当关注“配送设备”。如下是仓配系统的系统用例图:
系统用例规约有了业务流程图和系统用例图,需要根据进一步细化系统边界上的约束,保证系统的稳定性。系统执行者与系统的交互细化了详细的约束,系统的稳定性才能提高,如果没有仔细列出约束,有可能会忽略一些边界条件,导致系统的故障;如:仓配系统不考虑来自第三方商城订单要配送的设备数量限制,则会因为第三方商城出现的错误,导致资产损失。系统约束来源于系统用例,根据业务的规则,详细地描述了业务流程中的基本路径,扩展路径和约束。配送设备:
[*]系统每小时向第三方商城查询待配送的订单
[*]系统验证订单是否已经配送
[*]系统验证订单要配送的 SKU 是否合法
[*]验证订单中配送的设备数量是否超过最大限制 1000 笔
[*]系统向物流系统请求给订单中的用户配送订单中的设备
因为步骤 2,3,4 还有其余可能的路径,称之为扩展路径。
[*]订单已经配送
[*]系统忽略该订单
这里的约束不是告诉研发同学如何实现功能,这里的约束是业务规则,厘清系统执行者与系统之间的边界,以及边界上的约束。设计评审的时候,可以关注关键路径上的安全规则是否到位,这么做对提高系统的安全稳定有极大的帮助。类的分析与设计这可能是大多数研发同学比较熟悉的领域,经典的设计模式,类之间的关系,泛化,组合等。但是类从哪里来呢?是从需求之中凭空产生?又或者突然灵光一闪,有了类的雏形?对类的进行设计与分析之前,需要做的是找到他识别类经历过业务流程,系统建模,我们终于来到了系统里面,来寻找类。我们所熟知的类有三种,边界类,控制类和实体类。边界类是外部系统在系统内部的映射,借由边界类,系统和外部系统交互。所以一些接口请求,输入输出都属于边界类的职责。在仓配系统中,商城就是一个边界类,将外部系统转移到内部系统来,屏蔽了接口请求相关的细节。控制类往往是体现用例流程,一般而言,一个用例就是一个控制类。实体类则是系统的核心,实体类良好设计能够提高系统的复用程度,减低系统的复杂性。找实体名词知道了有这三个类还是不足够我们识别具体的类,识别具体的类需要去业务流程,系统流程,系统规约中经常出现的名词。在上面的流程图中,订单,商城,设备,物流,用户是反复出现的名称,说明这些类必然存在。
找到这些业务实体,就是找到类的第一步。找到属性类的属性也不是凭空产生的,需要对业务实现有价值,用户不一定有姓名,在物流上下文中,用户的属性就只有 ID,收货地址。找到那些对于系统实现必不可少的属性,放到正确的类中。如仓配系统中的订单,包含订单号,商品,用户。用户则有收件地址。
在仓配系统中,用户只有一个地址,在商城的系统中,用户则有多个地址,充分说明了,不同的上下文中,类的属性不是固定的。找职责从业务规则和约束中,可以找到一些实体应当有的职责,如订单,就有验证合法性的职责。
有时候,有些对象看起来信息很富裕,但是却没有什么职责,说明他是一个值对象,像上图中的用户和收件地址,我们不关心他的 id,只关心收件地址,收件地址就代表着这个用户。状态机找到类和对应的职责,对于一些主要的实体类,还需要设计出他的状态机,清晰的状态机能有效地厘清系统内的一些事件和状态,增强系统整体的健壮性。仓配中的订单,从用户购买的待发货状态,到通知物流发货,再到实际发货,物流签收有一些列状态的演变。
5 总结在多人协作的项目中,不断提高项目质量,除了依靠代码之外的工程手段,还要依靠设计建模。从具体实践的角度来看,设计建模在不同的环境中,调整具体的流程和侧重具体的节点,也能够实现快速高效,不会导致繁琐和低效能的现象。在写代码之前应该做的几件事情:
[*]找准涉众利益(越是新的项目,越是要分析好,如果是小功能的迭代,则可以从产品需求文档中寻找);
[*]画出原来的业务流程图,和改进之后的业务流程图(业务流程图是对业务理解是有极大帮助,在绝大部分场景中都不应该省略);
[*]分析系统的职责边界,画出系统用例(往系统中添加较小的功能可以考虑不画出来,心中有数即可);
[*]写出具体的用例路径和约束(对系统安全和稳定越是关注,越应该去写出具体路径和约束);
[*]识别主要的类,属性和职责,画出重要实体的状态机(简单功能则直接用代码来表达即可)。
想和一群喜欢折腾与思考的同学一起提升吗?来微信支付吧,这里鼓励思想的碰撞与应用,这里有海量的数据,有令人激动的业务场景,有各有所长的优秀伙伴,快来一起玩耍吧。微信支付欢迎你参考文献:
[*]《软件方法》
[*]《软件建模与设计》
页:
[1]