作者:子丑
内容大纲:
1、研发规范≠流程约束
2、自动化工具→研发规范载体
3、研发规范在工具上的落地示例
4、研发规范的选型方法与常见实践
研发规范≠流程约束
这个故事特别适合研发规范的场景,我们要避免成为把猫绑在柱子上的信众。而要做到这一点,我们先要了解什么是研发规范。
当我们在谈论研发规范时,我们在谈论什么?
有些团队谈起研发规范,会列一个大纲,并制定详尽的规章制度,这些规章制度对他们而言就是研发规范。
这个大纲可能是这样的:
- 需求管理规范
- 代码管理规范
- 制品管理规范
- 测试管理规范
- 生产发布规范
- 安全研发规范
还有些团队会用如下这种图来描述需求从提出到开发到交付的整个过程,准入准出要求、会涉及到哪些人等,他们以此来描述研发规范。
显然,当我们以第 2 种方式描述研发规范的时候,我们能更好地把控研发过程,能够以终为始地去看研发规范。
研发规范的目标、执行挑战
研发规范是跟随软件工程的产生而产生的。随着软件和团队的规模逐渐扩大,软件危机随之产生,为确保软件按时、按质地交付,需要利用软件工程的思路来解决软件危机,由此产生了研发规范。
因此,研发规范的目标是保障软件按时、按质地交付。
要保证软件按时按质地交付,实现研发规范的目标,我们需要从两方面入手,即优化研发团队内外协同机制和提升研发团队工程交付水平。
在制定研发规范时,有两个常见的误区。
一,只关心协作机制,乐衷于制定各种规则。例如,部署前提交发布审批单,其需经过 ABCD 四人的审批及相关人员的签字,审批单里面需要包括 XYZ 项内容等等。
二,只关心工程交付,乐衷于优化单点实践。例如,要求做单元自动化测试,并强调新增代码单元测试覆盖率要达到 100%。
如果只关心协同机制,那么研发规范很快会变成行为约束和规章制度。但是,研发规范还有另外的作用,即通过规范提升团队的工程实践能力。例如代码评审是为了提升大家对整体领域的理解和学习,自动化测试是为了提升快速验证的能力。
如果只关心工程交付,那么研发规范容易陷入一个个具体的实践,而忽视了整个交付流程,导致一眼障目不见泰山。例如测试团队强调测试自动化的实践,但却忽视了当下测试反馈的瓶颈不在测试自动化水平,而在流程中的部署和审批效率低下。
我们的目的应是既解决了协同问题,同时有提升了团队的工程水平。
自动化工具→研发规范载体
确定了研发规范的目标,我们接下来看如何将目标落地。很多时候,在制定研发规范时,我们会编写包含各类规范的详细文档。但仅靠人力去实现这些规范是不现实的。因此,我们会需求工具的帮助,即利用工具去保障文档描述的规范被执行。
但是,文档与工具的执行之间往往存在一定的差距,这个差距该如何去解决呢?
1、案例
上世纪 60 年代,阿波罗八号飞船的事故很好地阐释了这个问题。工程师汉密尔顿在加班时不慎触发了预发射程序,但好在是在测试状态,没有引发事故。这一事件促使汉密尔顿提出在系统中引入错误检查的想法,但负责人认为这种情况不可能发生。后续正如汉密尔顿所担心的,飞船飞行过程中宇航员误触发了这个程序。幸运的是,备份机制起到了关键的作用,避免了更严重的事故发生。
2、启示
仅靠文档定义规范是行不通的,当面临某个问题时,使用者需要按照文档规定的流程将各项任务串联起来解决,且中间不能出错。因此完全依赖使用者处理问题是不可靠的。正如阿波罗八号的例子,如果系统没有备份机制,要求使用者来处理这些问题将会是非常困难的。所以,工具的组合和连接(即如何正确地使用工具完成某个任务),也应该有相应工具来实现,而不是依靠使用者记住这一系列步骤并确保没有任何错误发生。
这个问题的核心是流程的自动化。
流程是工具自动化的过程,人不是流程本身和驱动者,而是参与方之一。只有这样,我们才能保证研发规范符合设计和成功落地。
3、示例:实现按特性的持续交付
通常情况下,研发规范会如何落地?我们利用名为“按特性持续交付”的案例来进行解释。
某企业有 30 左右的研发团队,架构师希望能确保团队的灵活性和快速响应能力。团队应该能够持续交付产品特性,即将产品特性拆解成各应用的开发任务,实现独立部署,以达到灵活性和快速响应业务需求的目的。
基于此,他们的愿景是,每个产品特性可以独立且快速地开发、测试、发布。每个特性对应一个开发分支,这一分支在开发验证和测试验收阶段发挥作用。只有通过开发验证的分支才能进入测试验收,通过测试验收的分支才能进入评审并合入主干,最终进入生产部署。
上面是我们定义的研发规范的概览,在这个概览下我们做了如下几层定义。
- 产品的交付粒度是一个产品特性,部署力度是一个应用,开发力度是一个 feature 分支。
- feature 分支必须经过开发、测试,最后到达部署。
- 在 feature 的整个交付过程中,每个阶段都有准入卡点。比如,如果某个 feature 分支没有经过开发阶段的验证,那么将不能进入测试验收阶段。
4、基于代码库和流水线的解决方案
有了这些规范之后,如果基于代码库和流水线,如何落地呢?
首先,代码库与应用最好一一对应,且代码库会将 master 分支配置为保护分支,其只能被合入,而不能被推送。并且被合入时具有条件限制,其必须通过前面的验证。
其次,我们应该会有两条或三条流水线。此处为开发测试流水线和生产流水线。在开发测试流水线上,开发测试都在这个 feature 分支上。开发测试流水线包含两个部分。第一个部分是开发阶段,它只做了一些构建和单元测试。第二部分是测试阶段,它只做了测试环境的部署以及测试验证。这里,我们是把两个部分是放在 1 条流水线上,即将代码推送到 feature 分支,再进入到单元测试等阶段,如果开发自测通过,确认可以提交测试验证,那么测试可以进入到部署环境阶段,然后进入到测试验证阶段,如果验证通过,则流程结束。
若开发阶段执行频率较高,测试阶段执行频率较低,此时,开发测试可以分为两条流水线。第一条流水线在代码提交后就可触发,但由于第二条流水线是独立存在,存在多种触发条件,无法设置测试必须通过前面的验证。因此,这种情况下会出现测试流水线卡不住的问题。这里为了简单,我们将开发测试合并为一条流水线。
生产流水线比较清晰。当代码合入到 master 分支后,流水线依次经过构建、发布准入和部署到生产环境等阶段。
我们以云效为例对上面的方案做一个演示。
首先,我们在代码库的分支设置中配一个保护分支,它的作用是通过关掉推送,使得任何人不能直接推送到 master 分支上,并且只能通过特定人的权限后才能合入到分支上。此时,可以给合并设置限制条件。例如,将通过代码评审以及通过开发测试流水线作为限制条件。
我们再配置两条流水线。分别为开发测试流水线和生产部署流水线。
开发测试流水线,对应 feature 分支,包含单元测试、源码的漏洞检测、构建、部署测试环境等步骤。同时,也可以按需在其中加入测试准入等步骤。
生产测试流水线,对应 master 分支,包含构建、审核、部署生产环境等步骤。
以上为流水线上的配置,同时在配置时,必须要保证两条流水线上配置的代码源、代码分支和部署环境的正确。
接下来我们看下这个解决方案是否符合前面所定义的研发规范。
我们发现,对于每一个阶段内部的规范,流水线是完全支撑的,但是阶段间的准入很难定义。例如合到 master 之后,需要保证所部署的准入已经通过前面的验证测试,那我们只能通过间接的手段,在保护分支里面设置分支合入的条件,进而达到准入的要求。但是需要注意的是,这两者并不完全对等。另一个问题是,在当前的情况下进行部署时,我们无法知道这次部署所涉及的特性是什么。
5、基于代码库和流水线的解决方案的局限
基于代码和流水线的解决方案,存在两个局限:
一、像早期的 NASA,要正确地执行研发规范,需要配合手册让使用者了解并正确执行操作步骤。包括开始一个新的特性开发时,使用者自行创建一个以 feature- 开头的分支,将代码提交后,使用者需要关注开发测试流水线是否有问题,然后按照要求创建合并请求,把 feature 分支合并到主干。然后,再去检查生产部署流水线是否运行正常。同时,在配置流水线时,使用者需要在执行的层面,保证流水线所对应的环境是正确的。类似宇航员在遇到故障的情况下,他必须遵循手册一步步去执行,如果某一步骤执行有误,那么就可能会出现问题。
二、即使我们根据手册正确地执行研发规定,还是会有信息传递不了,需要依靠线下传递的问题。最典型的就是部署所涉及的特性清单有哪些,而这类清单信息在研发规范中是很常见的。例如当我们作为验收测试的角色,这次测试的范围是什么,测试的东西包含哪些特性和改变,而这些信息需要彼此间传递。但是在传递的中间过程中,信息是否准确完整具有未知性,可能会产生信息丢失,并且后续无法再追溯,除非我们再去另找渠道补充丢失的信息。
6、研发规范承载在哪里?
造成该解决方案的两个局限的根因是什么?在之前,我也问过自己类似的问题,我们制定一个研发规范,它该定在哪里?它的承载是什么?
如果把它按模型的视角画出来,我们就会发现原来的思路明显是有问题的。
按照原来的思路,会发现代码库模板、流水线模板这些研发规范的对象对接不到真实的载体上,这显然是不对的。如果我们参考其他产品或工具的思路,会发现早已有解决方案,只是在工程交付上,没有明确把这个概念提取出来。
很早之前就有了项目的概念,例如云效的项目管理工具 projex,第一步操作往往就是创建项目。项目是研发规范的天然承载体。
进入到工程交付的层面,载体又不太一样。此时项目、代码库已不适合作为载体,因为具体的工作负载、服务、人员在很多时候并不能与代码库和项目一一对应。此外,大库模式或者一个应用涉及到多个代码库。所以在这样的情况下,需要一个名为应用的载体。
以应用承载研发规范在模型上是清晰且合理的。因此,我们引入应用这个概念,并且产生了一个新的产品:云效 AppStack。
应用对应于协作项目或者交付团队、团队空间或产品空间等。相对于项目下的需求,应用下的技术任务被定义为变更请求。
基于云效 AppStack 的研发规范落地演示
我们还是以前面的特性驱动的研发规范来举例。
1、用应用研发流程代替流水线,保证研发规范被正确执行
首先,我们用应用研发流程代替流水线。 因为流水线能定义研发阶段内部的逻辑,无法定义 2 个阶段之间的逻辑。我们引入了一个更上层的概念:研发流程。
简单来说,我们认为研发流程就是 N 条流水线的组合加上流水线的准入限制。
下面,我们进行具体的演示。首先我们会进到 demo-go-echo 这个应用里面。在这个应用里面我们可以看到其研发流程。
在这个研发流程里面,我们可以看到它包含两个阶段,一个测试阶段,一个生产阶段。这与前面两条流水线是可以对应上的。在测试阶段时,我们可以观察到它的触发方式。既可以选择一个 feature 分支去触发,也可以由代码提交自动触发。
接下来,我们看一下研发流程的配置。
如图所示的研发流程里,我们配置了两个阶段,每个阶段为一条流水线,并且阶段间存在准入卡点。例如生产阶段,其分支被固定为 master,并且以测试阶段的成功执行作为准入条件。
此时对照前面的规范,可以发现我们的研发流程与规范所描述的能够完全对应,包括阶段准入、对应分支、环境等。
这样,该研发规范就可完美的映射到 AppStack 的研发流程上,包括成员、角色、研发流程等,都可与规范描述的一一映射。
这样的好处是什么呢?让我们实际走一遍研发流程。
假设我们有个开发任务为实现一个 demo 接口。
我们创建该开发任务,并自动创建 feature 分支,同时因为开发任务的目的是为了满足某个需求,因此我们把这个任务与需求关联起来。
我们修改下代码并提交到该分支,测试阶段会被自动触发,
我们可以清晰地得知,本次触发涉及的特性是什么。同时查看变更详情时,可以看到这个变更对应的需求,点击需求全景图,我们也可展示需求的关联变更,数据都内在地连接了起来。
2、用变更请求贯穿整个应用研发生命周期,保证信息被完整连结
变更请求是一个完整的连接对象,它的作用在于让产品特性的开发任务的各个阶段被聚合起来。在流水线的研发模式里,如果想追溯某一次代码改动,在合入目标分支、打包、测试一路走到哪里,以及当前测的包来源自哪里,当我们想打通双向链路时,里面会存在两个问题。一个问题是,当拿到测试的执行记录时,我们只能拿到分支、来自哪个 commit 的信息,它是合并来的还是代码提交来的等这些问题是未知的。因此,我们在这里做了一个组件来计算这次改动涉及的源头。
这就解决了我们前面的问题:如何收集本次部署所涉及的特性或集成所涉及的特性,即依靠变更请求以及在研发流程中变更管理器的组件来实现。
通过如上的方式,整个应用的研发生命周期都可以连接起来。变更请求有两个目的,一个目的是让开发人员的工程实践被自动同步更新到需求,从而无需手动更新需求的状态;另一个目的是当需要对研发交付进行监控、跟踪和度量时,可以做到端到端的数据连通。
🔔 注: 因为文字版内容描述有限,详细演示可查看视频版:https://developer.aliyun.com/live/25398204
研发规范的选型方法与常见实践
1、研发规范的选型方法
我们在梳理云效的研发规范产品模型的时候,跟不同背景的角色在一起沟通很容易出现概念不一致的情况。为了解决这个问题,我们会以需求分析的思路去分析研发规范。
首先,明确目标。不同的企业的目标不同,有的企业是要更快地交付业务特性,也有一些企业的目标是安全或是质量,比如偏金融类的业务。在明确目标后,决定以何种流程去支持目标。接下来通过多种场景,如快速修复、迭代、项目场景去验证流程是否合理。如果存在问题,则调整流程或目标。
此外,通过流程的梳理,提取关键对象,这些对象可能是应用、系统、代码、角色、环境等。这些对象之间的关系也能通过流程的梳理而被提取出来。最后,我们利用工具落地流程,实现目标。这是一个典型的选型方法,其中研发流程是核心。当有了研发流程后,我们能够很好的做对象的提取和静态模型的建立,并作为验证的依据。
2、研发流程的梳理方法
下面我们分享研发流程梳理的一个较为实用的方法,是以技术团队的视角为基线去梳理研发流程。
例如一个十人团队承接来自各方的业务需求或产品需求,我们首先梳理两周一各迭代的输入内容,例如需求、缺陷、用户反馈、文档等。
其次,我们梳理为实现输入或解决问题,需要经过哪些个阶段。按照各阶段有不同的执行频率,且可独立运行的原则,我们可将研发流程分为若干个阶段。例如该团队每天都会提交五或十次的代码,且会在每天晚上做一次集中的验收测试,那么该验收阶段就为一个阶段。我们把代码提交后的快速检测定位第一个阶段,即开发阶段。
第三,由于频率、主负责人不同,需要明确阶段的输入输出、运行限制、负责人、准备条件、动作、环境等信息。
第四,我们明确如何进入需求,以及进入到各流程间的条件要求,以解决因需求流程的问题对团队自身各流程运行的限制。例如需求进入到最后需要进行上线审批,该审批可能为两周一次,因此该部署可能为两周一次,这就为一个限制。通过明确限制,可以把上层流程梳理出来。当梳理出了两层流程,即需求流程和技术任务流程时,整个企业的研发规范大概就可整理出来。此时,对流程进行调整和优化,逐步形成一个较为合理的研发规范。
综上所述,整体四步可简述为:
一、站在技术团队视角,找到开发任务的源头。二、按照开发任务的交付过程,识别流程所包含的阶段。三、明确每阶段的准入条件。四、往上追溯开发任务的源头,识别其流程,并找出其限制。
3、研发流程的梳理示例
当定研发规范和研发流程时,首先团队需要定目标,例如每个产品每周至少发布一次、每天都有可发布的 build、每次发布前置时长 1 小时。本质上其核心目标是更加关注发布的频率,要求产品能够快速出去,而对于质量并没有明确的提示。
其次是梳理技术团队的工程交付流程。该流程是通过需求发布流程中的开发中阶段进入,从 feature 分支开始运行,之后经过多个阶段到最后的上线,在各个阶段中会有各自的任务内容。
之后工程交付流程梳理结束后,会进入需求发布流程。需求发布流程以单个需求为来源,之后通过限制或相关性与之后的阶段相串联,最后确定了需求发布流程以及整体研发流程的全框架。需求发布流程和工程交付流程共同对应了一个粗略的研发规范概览。再者,对工程交付流程实践的部分做细化要求。
4、常见的研发规范举例
第一种是特性持续交付。 其特点是各特性彼此独立,尽量快速、不耦合的进入开发、测试和发布。在这种情况下,分支模式要求简单,并且各分支在最后发布时再合到主干。由于结构简单,在整个流程中,其运维和开发、测试往往为同一人,因此拥有对业务特性的快速响应的特点,此外,对业务的时效性具有高要求。
第二种是批量集中的交付。 其特点是在部署时,一定是多需求或多特性一起进行,并且会进入到一个集成的阶段,之后再进入生产。该集成可能有一个固定的集成分支或临时的集成分支。在这种情况下,一般开发测试和集成验证是由不同的人负责,但是人员需要依据具体的情形来定。
例如,大多数集中批量交付的场景,部署和开发测试的人员是不同的,在该情况下,分支模式也会不同。其发布选择的是集中式发布。因此,会有一个集成的过程,即多个需求或多个特性集成到一起,再做一次集成验证,最后再上线。在实践上与第一种模式相比,其交付频率不高,但是批量集中交付更多关注平衡质量,以牺牲一些需求的灵活性来保证发布上线的安全性。除此之外,由于系统本身的框架限制或特点,要求适用范围是批量集中交付模式。
第三种是主干开发。 主干开发是被很多人所推崇的一种模式。其特点是开发过程都是在主干上,集成非常频繁。当要发布时它会拉出发布分支上进行问题修复和生产发布。
主干开发的前提是保证主干具有高稳定性。为了保证主干的高稳定性,需要有快速的验证和修复能力和较高的工程素养。主干开发更多关注快速开发以及快速交付。
第四种是制品晋级。 制品晋级与前面的分支模式无关。其特点是到某个阶段之后,分支或代码不变,用构建出来的制品作为后续阶段的输入。例如,在测试验证通过之后,会有一个以 pre- 为开头名称的包,如果该包验证通过,那么就会将其打成以 release- 为开头名称的包。在后续部署时会用 release- 包(此处 release- 包和 pre- 包相同)。在这种情况下,无需再重新做镜像构建或者代码。所有后续验证均以制品来做。
该场景的好处在于所测即所发。例如,同一份代码重复构建时,可能会产生不一样的包,或者所依赖的某项小版本发生变化时会带来 bug 等问题。但是通过制品传递,可以杜绝上述等方式,做到所测即所发。
5、研发规范的实践建议
最后,我们给出各种研发规范的建议使用场景,当然,更好的实践方式还是按照前面我们提到的方法,梳理和定义符合自己团队特点和诉求的研发规范。
本文整理自直播分享,点击此处查看视频内容。