写点什么

提升研发效能的低代码思路

作者:赫杰辉
  • 2021 年 11 月 23 日
  • 本文字数:8152 字

    阅读完需:约 27 分钟

提升研发效能的低代码思路

前言

研发效能是最近几年比较火的概念,各大公司纷纷成立团队将其作为专项工作来做。本文将从研发人员视角出发,结合作者自身的经验与思考,探讨一下研发效能相关概念,提升瓶颈和基于低代码的突破建议。

研发效能定义

研发效能是指研发的效率和能力。提升研发效能是研发管理的本能追求,一般通过研发流程,研发工具,研发规范和效能度量的建设与推广,来持续优化改进。落实到系统建设上主要集中在项目生命周期管理,源码管理,开发环境优化,续集成持续发布,效能指标收集与分析等方面。无论采取什么手段,目的都是让研发工作变得更有效率。


虽然与研发相关的工作很多,但站在研发人员的角度其实就两种,开发相关和非开发相关。对研发人员来说,只有开发工作才是其最关心的本质工作,其他的都是辅助性工作。因此研发效能可以细分为两种,一种是核心研发效能,对应核心研发活动,包括系统设计,编码和测试的速度与质量;一种是辅助研发效能,对应除了核心研发活动之外的一切活动,比如开发中与研发管理相关的事务性工作,发布上线必须走的流程等等。

有效性标准

研发效能建设是否真的有效,一般通过效能度量指标来说明。但如何定义具体指标其实存在很大的难度,有一种观点干脆认为研发作为高度创造性的工作,其效能本质上是无法衡量的。确实一些常见指标细究下去并不合理,比如最常见的代码行数统计。其实按照核心和辅助研发效能的分类,我们可以把能否提高单日内核心研发活动的有效时间作为有效性判断标准。


有效时间定义为时长*效率。道理很简单,在总工作时间不变的情况下,如果每天能够以更多的时间,更高的效率进行设计,编码和测试,那么研发效能自然可以提高。所以研发效能建设的途径就是减少非核心研发活动的耗时,与增加核心研发活动效率这两条。


如果效能建设导致有效时间降低,或者研发人员额外付出的时间不能带来更大的有效时间收益,那么这种效能建设就不能认为是成功的。例如编译发布变得更加繁琐,耗时更长;同一研发任务在日报,周报,项目管理工具中重复填写;流程导致的会议数量与时长的增加,等等。


此外研发效能建设不仅仅只看指标,还要看研发人员的主观感受。是否有效,问一问研发人员比花费大力气搜集数据,提高精度更可靠。搞清效能建设的目的与手段是其成功的关键。

效能提升瓶颈

在进行研发效能建设后,研发人员和管理层可能会觉得效能提升并不是特别明显。例如客观上每周线上问题并没有减少。主观上工程师每天的工作时长似乎也没有减少。这主要是因为辅助与核心研发效能提升都存在一定的瓶颈。

辅助性研发效能瓶颈

有效时长上限

有效时长决定了辅助性研发效能提升的上限。有效时间由有效时长和研发效率决定,先不谈效率,假定每日平均净工作时间为 8 小时,在研发效能引入之前,4 小时用于开发等核心研发活动,4 小时用于辅助性活动。那么全部的优化空间就只有最多 4 小时。因此辅助效能建设每日最多只能增加 4 小时的研发时间,也就是说最多能帮助核心效能提升 2 倍。但实际上多出来的时间并不能保障全分配到核心研发活动上,而且普通人也很难做到全天保持高效,因此总体感觉效率提升不明显。

辅助性效能建设建议

辅助效能建设的主要目的是尽量排除开发工作的干扰。在进行流程,规范,工具等方面的辅助效能建设时,应尽可能利用自动化手段来消除重复劳动,减轻工作负担,减少非开发时间消耗。尽可能创造一个让研发人员能全身心开发的环境,减缓由人员规模和系统复杂度增长带来的效率下降趋势。


辅助研发效能提升的手段不仅仅限于流程工具等,合理安排时间,人性化管理也能达到很好效果。例如尽量少开会,或者把必要会议集中在同一天开完,通过对接人屏蔽外界对研发团队的干扰,主动给研发人员升级开发机器,留出午休区域,主动关心下属等等都可以帮助提高效能。


研发效能的建设目的是效能本身是否有实质的提高,而不是效能度量的全面与精确。度量本身只能反映而不能直接提高效能,没必要让研发人员为提供准确数据花费太多时间。一个团队,工作是否饱满,人员是否胜任,作为 leader 应该要做到了然于胸,不能被动的依赖指标。

核心研发效能瓶颈

辅助效能建设只能减少对核心研发活动的效率损耗,只有提高核心研发效能才能带来真正的效能提升。但是核心研发效能的提升一直都非常缓慢,并不是今天才有的问题。

停滞的现状

常见的核心研发效能建设主要集中在研发工具上。比如利用 IDE 插件对代码规范进行自动检查,对缺陷进行静态检测;在发布流程设定单元测试覆盖率卡点,建立建全自动化测试等。但我想任何客观的管理者或开发者应该都会深深的意识到,这些对提高核心研发效能其实并没有太大用处。

停滞的原因

我认为归根结底是研发过程中的设计环节出现了问题。很多公司都会有代码规范,但很少看到设计规范。针对设计阶段的效能建设,无论是规范,工具还是度量标准都很缺乏。设计作为上游环节一旦出现问题是开发,测试等下游环节无法弥补的。


设计环节的问题主要是设计方法不够实用,难以掌握和运用。目前的设计方法传统的比如说 OOAD,设计模式,一些公认的设计原则等,新的有 DDD 等。这些方法本身理论都不错,但每个研发人员都可以坦诚的问问自己真的掌握好了吗?我觉得绝大多数人都不能肯定的回答。如果这些设计方法真的简单易用,可能根本就没必要搞研发效能。软件研发本质上是披着工业化外衣的手工业,主要依赖个人能力。这在关于软件研发中的错误假设中有进一步的讨论。


设计方法不实用首先会影响设计质量。设计本质上是一种系统分解的手段,不好的设计会导致系统分解过度,不到位,或不合理。映射到代码层面就表现为包结构不合理与类功能不单一等质量问题;其次是影响代码质量。作为一种通用能力,设计能力不足表现在开发阶段就是代码整体结构不清晰,同层代码抽象程度不统一,代码过长或层次过深等;最后是影响测试质量。低质量的代码不仅很难做好单元测试,而且由于经常需要修复问题而频繁打断测试工作,无法在有限时间内完成高质量测试。


不解决设计问题而强行提高开发与测试的质量会导致一些不合理现象。例如在不留出重构时间的前提下强制提高单元测试覆盖率,就会看到研发人员去写没有 assert 校验的无效单元测试;或者运动式的搞一波单元测试,但后继维护工作没跟上,时间一长单元测试代码就慢慢失效了。

提升核心效能的必由之路

提升核心研发效能的关键是解决设计问题,并结合开发,测试环节的改进。只有提高设计能力才能保障充分合理的分解系统;拥有分解合理的系统组成部分才能让提高代码质量成为可能;具备高质量的代码,才能减少 BUG 数量和修复时间,才能为测试质量提供保障。

优化设计方法

好的设计方法首先要简单易学,这种设计方法必需是大家都能快速理解的,符合直觉的,这样才能简单易学;其次要求适用范围广,要能够普遍适用于系统顶层,中层和底层的分解;最后是直观高效,既能够在较短的时间内将需求转化为设计,并保证设计的逻辑清晰,大小适度。持续设计直到将系统分解待需要借助代码或其他方式来实现的粒度。


单靠规范和强制是无法做到以上要求的,必须借助工具让人们使用中不知不觉的符合要求。低代码就是这样的工具,后面会进一步说明。

选择高效实现

写代码是一种通用的需求实现方式,但并不是唯一的需求实现方式。对于特定领域的问题,如果有更高效的做法,就应该果断采用。复杂逻辑判断和业务对象状态管理是后台应用常见的需求,使用代码形式实现会导致代码冗长,从而降低可理解性,可维护性与可测试性。这种情况应该使用 DSL 来更高效直接的满足需求。

提高代码质量

在设计充分到位,部分需求已经其他方式实现后,实现最小粒度设计所需的代码行数应该较少,结构也应该较简单。在此基础上提高代码质量相对较容易。提高代码质量的主要手段是降低代码复杂度。


如何度量代码复杂度?作为程序员肯定都知道代码越长,嵌套层次越深代码就越难理解。如果将代码长度定义为代码行数,代码高度定义为嵌套深度,那么:代码复杂度=代码长度*代码高度。很明显减少代码的长度和高度就能降低代码复杂度。


减小代码长度的主要做法是将同层中抽象程度过低的代码段抽取为方法,从而保持同层代码的抽象程度一致。例如原来需要 5 行实现的代码,经过修改后只需要 3 行,那么后者自然比前者更好理解。比如:


A

B.1

B.2

B.3

C

可以改为:

A

B

C

...

B(){

1

2

3

}

可以看到原来层面的代码行数减少了。复杂度从 5 降到了 3。并且通过函数的方式增加了总体的可维护性,针对 B 的修改可以跟上层调用代码很好的隔离。同一层次需要阅读的代码越少,理解难度越低。


减小代码高度的办法是 early return。例如:


if(A)

if(B)

if(C)

Action;

可以改为:

if(!A) return;

if(!B) return;

if(!C) return;

Action;


可以看到原先复杂度是 4*3,改后是 4*1,只是修改一下逻辑结构就能将复杂度降低 3 倍。并且降低高度并不会显著增加代码长度。


降低高度的目的在于减少理解时候的头脑负担,类似函数调用,当人们进入新的代码层面时,需要暂存原来层面的理解,层数越深,头脑负担越大。但是采取 early return 之后,我们的头脑在每一次 return 语句之后都不用再记录之前的理解,负担低了,自然理解起来轻松。


保障代码质量的办法在于始终保持代码的简单状态,当出现代码增大或缩小的情况时,研发人员要主动在设计层面做分裂或合并以保持粒度大小合适来反映代码层面的变化。只有这样才能保证系统在长期的维护中不腐化。

提高单测质量

提供没有缺陷的代码是研发人员的本分。通过前面的设计和代码优化后,我们应该有了逻辑清晰,大小适中的代码。接下来就是通过单元测试进一步提高交付质量。


写好单元测试的最重要的技巧就是讲代码中所有的外部依赖参数化。例如 A 类的 a 方法依赖 B 类的 b 方法完成自身功能,b 方法可能是一次外部调用,一次数据库请求,或者某种复杂运算。针对 A 类 a 方法的单元测试需要:

  1. 把 B 类通过构造函数或 setter 从内部属性变为外部依赖

  2. 对 B 类做 mock,重载 b 方法使其能返回特定测试所需要的值

  3. 通过构造函数将 B 的 mock 注入到 A 类中

  4. 基于重载的 b 方法来实现 a 方法的单元测试


单元测试两个作用,首先是代码单元正常或异常处理流程是否符合预期;其次是系统其它改动是否影响到本代码单元,或者我们的改动影响了其他地方。一个合格的单元测试要做到所有逻辑路径 100%覆盖,除了特别简单的代码,例如标准的 getter/setter 外。

提高专注程度

效率跟熟练程度正相关。只有较长时间专注在某个领域才能达到对系统了如指掌的程度,从而能够看到问题合理优化。因为整个系统是多团队,多人并行开发,同一块代码几次发布后很快就会有很大改变。应该避免把研发人员在不同业务域频繁调度的做法,这并不能把人训练为全才,而只会增加每个研发任务的时间成本和质量风险。

低代码能起到的作用


尽管核心研发效能难以突破,但在多年的探索发展后,低代码技术目前看来是最有希望的一个。低代码在提高研发效能中能起到的显著效果已经通过很多实际案例获得证实。最知名的可能就是特斯拉利用低代码技术在很短的时间内开发了一套 ERP 系统的故事。

关于低代码的争议

很多人在进一步了解低代码/无代码后会觉得这种技术并不能满足自己的需求。主要问题在于具体工具的适用场景与能力和大家的需求不太吻合。


很多低代码框架立足于解决广度问题,既以更低成本,更快速度,开发更多领域的应用。主要用于 ERP,财务,人事,物流,教育等领域管理软件从 0 到 1 的快速研发。功能主要包括界面快速生成,数据库建模与访问,外部服务调用,工作流可视化搭建等等。学习与使用难度比较低,对技术小白友好。


而大部分现有系统的研发人员更希望解决的是深度问题。主要技术挑战集中在如何构建与治理中台,如何维护已经达到百万,千万,甚至亿级的代码库,如何梳理与监控海量后台服务调用,如何抵抗团队增长和系统规模扩张带来的一系列效能损失。


如果低代码工具只能创建带界面的数据库应用,支持简单工作流场景,显然不能打动后者。这种差异我在技术支持群里也遇到过:


x-series 简介

x-series 是一个面向后台专业研发人员的开源低代码框架,包括三个组件,流程图框架 xunit,决策树框架 xdecision,以及状态机框架 xstate。流程图(注意不是工作流)主要用于构建系统各个功能模块的后台逻辑处理流程;决策树主要用于对需要根据多个变量做复杂判断的场景建模;状态机主要用于管理业务模型的状态变迁。

xunit 实际效果图

x-series 产品定位

x-series 的设计目的是快速构建可理解的系统,减少设计与开发工作量,在长期的维护中保持设计不腐化,并为提高代码质量,促进代码的可测试性提供保障。它的详细定位如下:


它是低代码工具,能够在少写甚至不写代码的情况下完成传统方式下需要写很多代码才能实现的功能。

它主要面向研发人员,要发挥 x-series 的能力需要具备基本的编程能力,要理解流程图,状态机以及决策树的概念和用法。专业的研发人员能够用 x-series 发挥极大的生产力。

它是个高效的设计工具,流程图模型能够将系统从动作维度快速充分的分解到可以进行简单编码的程度,状态机和决策树模型能够将核心业务逻辑可视化并脱离代码单独维护。用它能够将系统设计的过程从天压缩到分钟级。

它是一个轻量级的框架,可视化编辑器支持 Eclipse 和 IDEA,无需脱离开发环境使用。使用可视化模型编辑器和运行引擎都不依赖任何外部服务或数据库。


为说清楚 x-series 的定位,还必须明确它不是什么:

它不是可视化编程工具或代码生成工具,不是对行级别代码编写的替代。用户通过编辑器产生的模型与运行时模型是同一个,不需要任何代码转换或同步,具备原生的设计与实现的一致性。

它不是界面生成工具或数据库访问工具,可视化编辑器只针对系统模型而不是用户界面,不提供界面元素与数据库表字段的映射。

它不是工作流引擎或者工作流可视化开发工具,工作流相关概念工具已经非常成熟,有很多选择,没必要在这个方向上重复建设。

它不是一个独立的运行平台,执行模型的内嵌引擎需要用户自己在应用中通过代码进行调用。因此它可以适应任何运行环境或平台。

它并不影响或取代其他设计方法,在动作维度之外,研发人员还是可以在代码中使用其他视角的设计方法,两者可以并存。

利用 x-series 提升核心效能

高效一致的系统设计

如果大家都用同一个方法设计,可以极大的降低设计理解和评审的难度。x-series 可以在公司层面提供统一的设计方法。使用 x-series 做设计一般先用流程图框架 xunit 对系统进行快速分解。之所以用流程图,是因为流程图具备以下优点:


首先是简单易学。流程图是计算机行业里面最基本的图形,即使没有任何基础的普通人也能够容易的读懂。甚至稍微留意一点就会在银行,商场或社区办事大厅里面发现各种说明服务的流程图。使用流程图作为统一的设计方式可以获得最大的共识。xunit 支持丰富的流程图组件和预定义结构,能够充分满足不同需求。


其次是适用于各个层面的设计。xunit 的模型,子图和节点可以分别对应系统的业务领域,功能集合与实现步骤。还可以利用引用功能将某个步骤和当前或其他模型的某个流程图作为关联起来,从而提供优雅的自顶向下设计手段。xunit 还支持并发分支,在节点上简单配置即可实现并发逻辑。


最后是高效。需求出来后,利用 xunit 可以几分钟内就将业务逻辑以流程图的方式创建出来,极大的缩短了设计时间。每个节点都提供了缺省实现,可以打印固定信息,或做简单的分支判断。这样在设计阶段不用写代码就模拟实际运行效果,从而让研发人员很方便的跟产品经理就设计进行验证和调整。


低代码与传统研发模式案例对比里面可以直观的看到效率差异

减少必要代码量

在保持相同质量的前提下,完成相同工作所需要的代码量越低,其单位时间能完成的工作自然越多。而要想不在代码中发现 BUG,只有不写代码才能完全做到。x-series 不但可以让研发人员用少的多的代码完成普通需求,还可以在不写代码的情况下实现特定需求。


x-series 框架通过通用调度和执行功能来减少必要代码量。类似管理分层,代码也可以大致分为调度代码和功能代码两类。前者通过调度后者执行特定动作来完成某种服务。这两种代码在一般系统中的比例大致为 1:1。在利用 xunit 将系统以流程图的形式分解时,研发人员只用提供步骤对应的功能代码,调度功能由引擎根据流程图模型自动完成。不仅如此,图形化方式能够让研发人员更容易的发现和提取通用化的模块,配合 xunit 的配置功能,可以很容易做到组件复用。使用 xunit 可以考虑建立公司自己的通用组件库。技术强的可以去做通用组件,业务强可以做系统定制和组装。


对于可以用状态机和决策树模型来实现的需求,甚至可以不写代码。使用 xstate 实现业务状态模型时,如果不需要自定义状态变迁触发器则不用写任何代码。使用 xdecision 实现复杂分支判断时,如果不需要自定义表达式或决策解析器,也不需要代码。

提高可理解性

可视化模型可以大幅提高系统的可理解性,并让更多的研发关联方参与进来。


首先是提高设计层面的可理解性,让产品经理能更有效的参与设计评审。俗话说一图胜千言,但很多研发人员的设计主要是以类图为主,不要说产品经理,就是换个研发人员也很难看懂。有了流程图,状态机和决策树后,产品经理可以更轻松的理解系统设计,更容易的指出问题,找到遗漏。


其次是提高系统架构层面的可理解性。研发人员最痛苦的事莫过于不得不看很多别人写的代码去理解系统的整体结构。看流程图就像看一栋大楼的设计图,各个楼层,各个房间,各种通道都清晰可见,有了流程图就相当于有了一张系统的代码地图,永远都不会在几个方法调用后迷失在浩瀚的代码海洋里。研发人员可以更快速的理解整个系统的处理过程,更准确的的定位到具体的功能代码。而用状态机和决策树更是连代码也不用看就能快速理解业务逻辑。


最后是从侧面提高代码层面的可理解性。从流程图节点进入功能代码就好像从楼道进入房间一样,某个房间内部是整洁,是凌乱,都不会影响大楼整体结构的清晰,对模型或代码的改动可以做到互不干扰。这会让研发人员更有勇气和信心去理解代码。即使遇到较复杂的功能代码,也可以用流程图对系统做进一步分解。


此外清晰的结构也可以帮助测试人员更容易的创建覆盖特定执行路径的用例。

提高代码可测试性

提高代码可测试性的秘诀就是降低代码复杂度,x-series 从工具和设计层面保障了这一点。


利用流程图将系统划分为流程调度与一个个独立的功能代码单元,把系统整体复杂度划分到模型与代码层面,从而降低代码复杂度。低复杂度的代码更容易符合高内聚低耦合的标准,也更容易测试。xunit 框架为绝大部分功能代码单元提供了函数式接口,相当于在代码层面提供了统一的预设计。函数式接口只有一个公共方法,研发人员仅需要实现其即可。


写代码时可以参考上面提到的提高代码质量一段所提出的要求和手段。建议方法体不要超过 30 行,嵌套深度不要超过 2 层,尽量多用 early return。不要嫌烦,把代码写复杂很容易,把代码简化才是真本事。只有简单的代码才是可测试的代码。以 xunit 中 processor 接口为例,好的代码看起来是这样的:


public void process(Context ctx) {

if(cond_1)

return;


if(cond_2)

return


do_something();

}


根据我自己的经验,功能代码和单元测试代码的比例大概在 1:1.2 到 1:5 左右。虽然看起来单元测试代码更多,但由于单测代码主要是参数构建和结果验证,结构和逻辑相对简单,因此编写时间大概是 1:0.5 到 1:1 之间。并且使用 x-series 还可以省下测试调度逻辑的时间。

x-series 应用效果

x-series 已经在各个实际项目中使用好多年,有经验的后台研发人员会觉得这就是他们一直想要的工具。


效果怎样我觉得可以说件小事情。好几年前推荐一个初创医疗互联网公司用 xunit 做后台系统,大概 2 年后通过他们的研发负责人了解了一下情况,说效果不错一直在用,需求来的时候,研发就打开流程图进入代码开发,感觉使用 xunit 后研发就是一件平常的事情,很轻松,对研发很有信心,不担心事情做不出来。

总结

提高研发效能的关键是提高工作日中有效工作时长,提高单位时间产出效率。辅助研发效能提升是有限的,要突破就必须想办法提高核心研发效能。借助 x-series 这样的低代码工具能够为提高核心研发效能带来立竿见影的效果。

作者简介

资深码农,精通 OOAD,设计模式,框架以及中间件研发。在低代码概念火起来之前已经默默从事相关研发多年。在 21 年 InfoQ 举办的 ArchSummit 的低代码专场上做过分享并主持了其后的低代码沙龙。将毕生所学融会贯通于开源框架 x-series。希望为广大徘徊在效率困境中的研发人员带来帮助。

参考资料

x-series项目地址

B站技术分享视频

ArchSummit技术分享PPT


用户头像

赫杰辉

关注

开源可视化系统构建工具x-series作者 2020.06.09 加入

还未添加个人简介

评论

发布
暂无评论
提升研发效能的低代码思路