• 如何衡量软件系统的复杂度(三)


    一、背景

    前面两篇已经大体讨论了整个软件系统中的复杂度相关的衡量指标,但是如何相对客观的通过复杂度模型来构建出来并输出相关复杂度数据,就需要对不同的衡量指标和影响因素做量化,同时需要仔细考量不同复杂度因素之间的相关性和其在真实业务场景下的权重。本篇文章将通过一些分析思维和建模方法来构建一个软件复杂度评估模型。
    当然,评估模型可能确实不够准确,或者有些粗糙,需要理论研究的话还要继续优化,可以参考下。

    二、复杂度向量列表

    2.1 向量分类

    根据前面的分类我们这里将整个向量分成三种,就是时间,空间和钱。我们对每一种的向量单位做一些说明。

    2.1.1 时间

    这里我们根据不同的软件周期和需求交付时间可以把,小时,天和周,月来当作复杂度在时间上的衡量单位。

    2.1.2 空间

    前面说的一些复杂度因素在空间上可能指的比较广泛,所以这里的衡量单位可以是个数,和大小等计量单位。

    2.1.3 钱

    在成本复杂度方面,我们最直观的感受就是钱,如果一个系统能够带来的利润预期上可以覆盖成本,那么对这个系统的投入是值得的。所以钱方面,在RMB上,我们可以以千元和万元为基本单位。

    2.2 复杂度因素表格

    这里为了方便统计和构建模型我用了一张表格来构建关于软件系统复杂度的影响因素。
    表1-1:复杂度因素表格

    复杂度分类复杂度因素(单位)说明
    时间复杂度需求收集的时间(天)包括需求调研沟通相关的时间
    时间复杂度需求文档编写时间(天)需求相对确定可以做的时候,落地到文档的时间,上面和下面可以分开也可以合并计时。
    时间复杂度需求技术方案文档编写时间(天)可能也包括前期的技术调研和方案设计讨论这里可以分开也可以合并计时。
    时间复杂度需求技术方案评审时间(天/周)一般情况下如果技术评了多轮无法确定则单独计时。
    时间复杂度软件开发时间(天/周/月/年)
    时间复杂度软件测试时间(天/周/月)开发完成到验证的时间。
    时间复杂度软件验收时间(天/周)
    时间复杂度软件发布时间(天/周)
    时间复杂度问题排查时间(天/周/月)这里需要指自己负责系统范围内排查问题的时间
    时间复杂度文档熟悉时间(天/周/月)
    空间复杂度系统数量(个)指单独可运行的系统数量,如mysql,单独的微服务,或者单体服务。
    空间复杂度系统模块数量(个)模块之间有子模块,这里可以按一定维度做模块数量的衡量。尽量覆盖全。
    空间复杂度系统模块文件数(个)软件系统运行涉及到的全部文件数量
    空间复杂度系统模块方法数量(个)也包括对外API
    空间复杂度系统依赖的外部系统数量(个)
    空间复杂度系统依赖的外部系统模块数量(个)
    空间复杂度系统给外部系统提供服务的数量(个)
    空间复杂度系统接口被调用的数量(次)
    空间复杂度软件服务运行所需要的机器数量(台)
    空间复杂度软件数据存储所需要的内存磁盘数量(TB/GB/PB)
    空间复杂度软件提供服务所需要的带宽量(TB/GB/PB)
    空间复杂度软件使用的用户数量(人)
    空间复杂度软件开发和运维相关的人员数量包括涉及到的产品测试UI(人)
    空间复杂度软件依赖的其他技术或者组件或者开发环境软件的数量这里将软件开发和运行依赖的一些偏底层和工具相关的数量划为一类,如果有特定情况可以分开计数。
    空间复杂度软件开发运行维护相关的组织部门数量这里涉及到跨部门系统或者组织与系统相关的关联关系,类似于康威定律的一个统计因素。
    成本复杂度软件开发依赖的人力资源成本这里我们只统计了人相关的成本,当然与复杂度关联比较弱或者没有什么直接关系的成本则不在此范围内。
    成本复杂度软件开发依赖的专利采购成本
    成本复杂度软件开发依赖的特定软件环境成本
    成本复杂度软件运行依赖的硬件成本(网络,机器,内存,磁盘)这里也可以拆开来统计成本
    成本复杂度政策变化导致的需求变更调整成本这里对特定行业和场景有一定成本,当然也可以不计入分析

    2.3 复杂度因素相关性说明

    上面的复杂度其实有些可能是重叠的有些则具有一些正相关和负相关的关系。所以这里我们需要对一些明显的复杂度因素之间的相关性和复杂度本身与因素相关的关系做一些阐述。比较明显的一个例子就是生孩子,一个人10个月可以生1一个孩子,但是10个人一个月一个人也生不出来。对一个因素进行特定的投入可能会因为客观规律和其他因素导致产生混乱,就是其他复杂度因素会变的很高或者很坏。

    2.3.1 软件开发时间和开发人数的相关性在这里插入图片描述

    2.3.2 软件开发时间与成本的相关性

    在这里插入图片描述

    2.3.3 其他相关性

    这里因为篇幅问题不再一一详细介绍所有存在相关性的复杂度因素,感兴趣的可以根据自己的实际情况评估下还有哪些因素之间有相关性,可以加我好友一起讨论哈。

    1. 软件开发时间与软件依赖的开发技术栈
    2. 软件发布时间与依赖的发布技术栈(工具集,平台,中间件等)

    2.4 复杂度因素权重说明

    在实际情况中,复杂度因素其实不能按上面的进行单一维度计算,比如计算代码行数或者方法数,这里可能让指标统计产生一定偏差,所以我们可以按数学向量或者多项式的方式对每个复杂度因素进行合理的权重评估。
    这里我们应用权重的时候其实有以下几点作用:

    1. 突出某方面复杂度,其权重会比较大,这样在整体上该复杂度对于软件系统的痛点则更明显。
    2. 客观与主观并存,权重在不同的时间阶段,不同的角色职责下取值区间不太一样。所以定权重系数的时候一定需要一个合适的评估值。
    3. 存在相关性的复杂度因素其权重之间的相关性也会跟着变化。比如有些指标之间存在正相关或者负相关,则可以用权重系数来表达。

    三、复杂度模型

    3.1 构建模型

    3.1 模型公式

    这里用上面提到的向量分类来做模型的公式因素,用代数表达一下。假设如下:
    
    • 1
    1. 时间方面的复杂度值代表T(Time)
    2. 空间方面的复杂度值代表S(Space)
    3. 钱方面的复杂度值代表M(Money)

    由于整个系列的精华可能就在这些数学公式了,可能确实功底不够,所以这里我们用一个比较浅显也比较low的多项式公式来表达不同方面的复杂度:
    T=X1W1 + X2W2 + X3W3+…+XnWn.
    S=Y1W1 + Y2W2 + Y3W3+…+YnWn.
    M=Z1W1 + Z2W2 + Z3W3+…+ZnWn.
    总体复杂度可以认为是三者之和,这里我把复杂度的值用C(Complexity)来表示。
    那这样的话整体复杂度可以表示为C = T + S + M.

    3.2 权重评估

    上述公式中的X,Y,Z基本都比较容易评估出来,但是对于W来说不同的人在软件开发阶段看到的复杂度不一样,所以其权重评估值也会不一样,所以有些很明显的导致软件复杂的问题其权重都会比较高,也就是说在某几个复杂度因素中,其权重评估值会得到共识。另外一方面在软件需求到交付的整个生命周期中我们关注的效率问题,软件需求交付问题都将从权重上得到一些衡量。
    在应用权重评估的时候可以根据自己的实际情况以及团队所处的情况进行合理评估,实际上答案不止一个,可能是一个区间。

    3.3 复杂度模型建模

    在这里插入图片描述

    3.2 模型输入

    这里以时间方面的复杂度来模拟计算一下,代码如下:

    package com.coderman.complexity;
    
    import com.coderman.complexity.enums.ComplexityTypeEnum;
    import com.coderman.complexity.enums.TimeComplexityEleTypeEnum;
    import com.coderman.complexity.model.CalculateResultBO;
    import com.coderman.complexity.model.ComplexityEleBO;
    import com.coderman.complexity.model.MeasuringUnitBO;
    import com.coderman.complexity.service.CalculateService;
    import com.coderman.complexity.service.impl.TimeCalculateServiceImpl;
    
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * Hello world!
     *
     */
    public class App 
    {
        public static void main( String[] args )
        {
            CalculateService calculateService = new TimeCalculateServiceImpl();
            List<ComplexityEleBO> complexityEleBOList = buildTimeComplexityEleBOList();
            //这里以天为时间方面的复杂度的衡量标准
            CalculateResultBO calculateResultBO = calculateService.calculate(complexityEleBOList, MeasuringUnitBO.getInstanceDay());
            System.out.println(calculateResultBO);
        }
    
        /**
         * 模拟时间方面的复杂度因素
         * @return
         */
        private static List<ComplexityEleBO> buildTimeComplexityEleBOList(){
            List<ComplexityEleBO> complexityEleBOList = new ArrayList<>();
            complexityEleBOList.add(new ComplexityEleBO(ComplexityTypeEnum.TIME.getCode(), TimeComplexityEleTypeEnum.REQUIREMENT_COLLECTION,
                    1.0d,2.0d,MeasuringUnitBO.getInstanceDay()));
            complexityEleBOList.add(new ComplexityEleBO(ComplexityTypeEnum.TIME.getCode(),
                    TimeComplexityEleTypeEnum.REQUIREMENT_DEVELOP,1.5d,8.0d,MeasuringUnitBO.getInstanceDay()));
            complexityEleBOList.add(new ComplexityEleBO(ComplexityTypeEnum.TIME.getCode(),
                    TimeComplexityEleTypeEnum.REQUIREMENT_DEVELOP_TEST,1d,2.0d,MeasuringUnitBO.getInstanceDay()));
            return complexityEleBOList;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    3.3 模型输出

    模型的输出则相对简单,这里仅输出复杂度的衡量单位,复杂度类型和复杂度的值。

    四、衡量系统

    这里我们用一些实际的例子来看一下如何基于上面的复杂度模型来计算复杂度值。为了比较方便的抽取复杂度信息,我们将上面的表格稍微调整下,如下:

    复杂度分类复杂度因素(单位)复杂度值(区间范围)复杂度权重值(区间范围)复杂度因素计算结果(区间范围)说明
    时间复杂度需求收集的时间(天)包括需求调研沟通相关的时间
    时间复杂度需求文档编写时间(天)需求相对确定可以做的时候,落地到文档的时间,上面和下面可以分开也可以合并计时。
    时间复杂度需求技术方案文档编写时间(天)可能也包括前期的技术调研和方案设计讨论这里可以分开也可以合并计时。
    时间复杂度需求技术方案评审时间(天/周)一般情况下如果技术评了多轮无法确定则单独计时。
    时间复杂度软件开发时间(天/周/月/年)
    时间复杂度软件测试时间(天/周/月)开发完成到验证的时间。
    时间复杂度软件验收时间(天/周)
    时间复杂度软件发布时间(天/周)
    时间复杂度问题排查时间(天/周/月)这里需要指自己负责系统范围内排查问题的时间
    时间复杂度文档熟悉时间(天/周/月)
    空间复杂度系统数量(个)指单独可运行的系统数量,如mysql,单独的微服务,或者单体服务。
    空间复杂度系统模块数量(个)模块之间有子模块,这里可以按一定维度做模块数量的衡量。尽量覆盖全。
    空间复杂度系统模块文件数(个)软件系统运行涉及到的全部文件数量
    空间复杂度系统模块方法数量(个)也包括对外API
    空间复杂度系统依赖的外部系统数量(个)
    空间复杂度系统依赖的外部系统模块数量(个)
    空间复杂度系统给外部系统提供服务的数量(个)
    空间复杂度系统接口被调用的数量(次)
    空间复杂度软件服务运行所需要的机器数量(台)
    空间复杂度软件数据存储所需要的内存磁盘数量(TB/GB/PB)
    空间复杂度软件提供服务所需要的带宽量(TB/GB/PB)
    空间复杂度软件使用的用户数量(人)
    空间复杂度软件开发和运维相关的人员数量包括涉及到的产品测试UI(人)
    空间复杂度软件依赖的其他技术或者组件或者开发环境软件的数量这里将软件开发和运行依赖的一些偏底层和工具相关的数量划为一类,如果有特定情况可以分开计数。
    空间复杂度软件开发运行维护相关的组织部门数量这里涉及到跨部门系统或者组织与系统相关的关联关系,类似于康威定律的一个统计因素。
    成本复杂度软件开发依赖的人力资源成本这里我们只统计了人相关的成本,当然与复杂度关联比较弱或者没有什么直接关系的成本则不在此范围内。
    成本复杂度软件开发依赖的专利采购成本
    成本复杂度软件开发依赖的特定软件环境成本
    成本复杂度软件运行依赖的硬件成本(网络,机器,内存,磁盘)这里也可以拆开来统计成本
    成本复杂度政策变化导致的需求变更调整成本这里对特定行业和场景有一定成本,当然也可以不计入分析

    这里加了三列,按照上面的公式可以方便的计算出某个系统的复杂度,当然也可以通过计算机程序输入然后得到输出结果。前面说了,复杂度的计算结果可能是在某个区间,所以计算机程序仍然需要计算出复杂度结果所在的区间范围。

    4.1 单体组件级别的系统

    这里我们把单体组件级别的系统看作可以独立运行的一个Java进程或者一个微服务实例。或者一个大的单体服务。所以上面的表格中要填写的内容会简单一点。但是仍然要相对全面的从技术业务和人的角度评估。

    4.2 分布式服务级别的系统

    这里就是分布式微服务或者集群维度的系统衡量了,比如商品系统或者营销优惠系统,这些系统一般来说是一个团队去负责的,所以系统边界与团队边界是相互契合的,同时也符合康威定律。划定了系统范围则划定了系统个数和开发使用人员的大概范围。但是要注意的是需要评估人员变动,服务资源动态规划的相关内容,所以在范围上尽量做合理且真实的评估。

    4.2 单体集成式系统

    这里指的是表面上是一个系统,比如操作系统,但是还有很多子系统一起合并到一个可执行exe或者jar包里,或者安装文件里,通常这种模式下跟我所开发的微服务应用不太一样,但是仍然可以进行复杂度评估。

    五、与复杂度相关的系统

    5.1 工程效率平台

    很多公司都会用到工程效率平台,或者自己开发维护一套,这么做的原因一方面是辅助记录各种信息,跟踪项目,同时帮助提高软件研发效率和交付能力。工程效率平台也涉及到方方面面,个人也觉得就是DevOps的外延。此时可能就是开发Owner全部了。在工程效率平台中不同角色的人可以更好的应对其职责范围内的复杂度。
    当然其本身是否能更好的支持软件研发的生命周期,也需要做工具平台的人逐步去优化和改良,大多数情况下买来的工程效率平台可能都不太适应公司的实际情况。

    5.2 研发效能平台

    研发效能平台与上面的工程效率平台可能差别不大,或者就是叫法不一样,但是实际上研发效能平台可能范围更广,服务的业务线和场景也更多,同时会附带很多统计信息类似于把jira或者confluence的文档功能结合在一起了,但是很多时候研发效能会被一些开发人员抵制,或者心理上产生排斥心理。这块会在后面的文章专门讨论。
    话说回来,研发效能与复杂度有什么关系,因为研发效能重在统计和衡量,所以简单来说研发效能就是复杂度衡量的一种简单方式。只是没有特别全,有些场景在研发效能里占比非常少,但并不能衡量系统的复杂度。

    5.3 敏捷框架与方法论

    关于软件复杂度和研发效率不同的人关注点也不一样,所以如何衡量复杂度看上去不是特别重要,但是应对复杂度的方法却层出不穷。这里从敏捷框架和方法论上简单介绍一下降低并控制复杂度的理论方法。

    5.3.1 敏捷开发方法论

    1. Scrum
    2. XP
    3. 精益开发

    5.3.2 Cynefin框架

    这里放一张Cynefin的官网图:
    在这里插入图片描述

    官网链接如下:https://thecynefin.co/
    总体上是将复杂度因素按不同维度进行划分和归类,这样对于人来说更容易理解和管控。

    六、经常关注的系统复杂度

    上面的一些复杂度维度是偏理论的,实际上作为开发者,互联网架构上面会从下面的几个方面去看系统复杂度,这里我也简单介绍一下。

    6.0 思维导图

    在这里插入图片描述

    6.1 代码复杂度

    代码方面的复杂度其实在需求设计阶段是看不出来的,在方案上可能会有些体现,这里重点还是在开发阶段,代码上的不同评估因素都在影响整体复杂度。比如模块,接口设计,实现链路等。

    6.2 组织复杂度

    从管理者的角度来说组织上的复杂度其实也需要考虑,尤其是互联网行业,在互联网行业组织变动是非常频繁的。组织变动也会带着系统复杂度出现动态波动。

    6.3 架构复杂度

    现阶段的互联网技术其实已经趋于稳定了,分布式应用也已经发展了很多年,个人觉得快到瓶颈了,在刚发展的几年里部分工程代码还有拷贝的可能,但是架构却不一定有人能给你正确的参考。但是现在不一样了,很多大厂探索出的架构应用。

    6.4 工程效能与软件复杂度相关文章

    如何从容应对复杂性:https://mp.weixin.qq.com/s/8YD9sqTuZGJEpVZPYY-aBQ
    系统困境与软件复杂度:https://mp.weixin.qq.com/s/npTzzZiJ_pvbXgFOTGmZ0Q
    研发效能度量核心方法与实践系列文章:https://www.infoq.cn/article/kINcaQBBC0uyvFmVlvzx

    七、总结

    在整个衡量过程中我们可以得到一些想要的信息,或许结果并不重要,但是我们依然可以通过上述复杂度模型来更好的帮助我们找到应对复杂度的方法。

  • 相关阅读:
    Spring Bean 的一生
    3ds MAX 基本体建模,长方体、圆柱体和球体
    2022届秋招Java岗高频面试题盘点,老司机也未必全会,真的太卷了
    PHP使用 FormBuilder 创建表单
    JSP1410 科研项目团队建设经费管理系统mysql
    计算机毕业设计springboot驾校管理系统o2ehg源码+系统+程序+lw文档+部署
    合宙ESP32C3 更换Flash调整固件教程分享
    双点重发布&路由策略实验
    面对突如其来的 GC 问题如何下手解决
    在树莓派中,Ubuntu系统下使用vim编辑器
  • 原文地址:https://blog.csdn.net/u010504064/article/details/126193841