查看原文
其他

营收活动: 高频高压倒逼出的积木式开发

赵伟&沈新华 哔哩哔哩技术 2024-04-01

本期作者



赵伟

哔哩哔哩资深开发工程师


沈新华

哔哩哔哩资深开发工程师



前言


你会如何总结你的工作内容?

“CURD”,这是不少业务开发者对自己工作内容,做出的“哲学级别”总结,不乏调侃和乏味之意。实际上,过来人都清楚,这和业务开发面临的复杂性和挑战不在一个层次。

一般情况下,互联网核心业务可以大体分为两类,其一是基础功能性业务,其二是围绕基础功能,刺激增长类业务。以直播为例,可以分为:

  1. 核心功能类:如送礼打赏、PK连麦等互动消费业务。

  2. 增长类:通过精细化运营等方式,以刺激核心业务增长(消费)为核心目的,如营收活动。

业务开发的第一挑战在于决策与实施的割裂,决策端和实施端是完全不同的两拨人,在关注点、信息量、职业技能、工作方式等方面差异较大,在软件旺盛的迭代协作之下,保障边际生产力是个难点。

在此基础上,核心功能类业务很考验“第一个程序员”,业务迭代就像为飞行中的火箭更换零件,更难的在于下次要怎么换,目前还不一定清楚。

增长类业务不以“换发动机”这种艰巨场景为主,但基于刺激增长的目的,其在迭代节奏和开发量方面让人望而生畏,基本上是:上线时间已定、套路又变了、时间不多了、不能出问题、下一个需求已经来了···

就规模和节奏而言,有人奔溃有人跑路,也正是这样,当越过山丘后,才发现这段路更接近工程的本质。

本文,我们基于团队在营收活动业务中的实践经验,探讨如何在快节奏、复杂灵活、工作量大的增长类业务中,保证交付生产力和交付质量。


营收活动简介


在开始之前有必要介绍下营收活动业务。作为刺激消费为最终目的的业务,基本上有两个特点:

  1. 联动各方资源(如公会),绑定节日或时间节点,如春节活动、七月活动等。频率高,倒排期上线

  2. 业务套路花样百变,联动平台几乎所有业务,巨量的细节和逻辑,高负荷开发量,历史上单活动MR普遍在6000+行

以Bilibili Live Star活动为例,记得我第一次打开需求文档想了解下,一直翻页翻了好久没到底,后面放弃了,当时想这是什么需求。



后来我真的负责这块业务,逐渐发现这是个长链条协作项目,流程走到研发时的诉求是,通过技术手段将策划方案真正落地。概括一下:

  • 对于平台来讲,是一次基于天时地利人和的周密策划

  • 对于主播来讲,是一场一展身手有机会扬名立万的竞赛

  • 对于用户来讲,是一次有新意能得到好处的社交游戏

用一个简单的模型表达可以类似下图:



痛苦的锤炼


活动业务发展经历了几个阶段,业务特点在于逻辑量大、细节多、节奏快、稳定性要求高。总结下来就是整体复杂度较高,且需要赶排期节奏,仅依靠横向加人效果有限,所以第一个阶段 Superman 模式起到主要作用。

到了第二阶段,由于一阶段积累的基建历史债务比较重,已经无法继续承接当前阶段的业务,再加上新同学不理解老的基建逻辑,导致线上变更评估不足造成生产事故。同时活动属于上层业务,对主播端、用户端、数据端、账号、长连接等其他端存在重度依赖,每次开发功能都需要跟各类系统打交道,基本上属于活动联动一切,依赖链条既复杂又多,一旦出了问题,解决起来也是相当痛苦。

快节奏和高复杂度,让二代 Superman 的负重前行持续加码,线上bug不时光顾,问题反馈一波未平一波又起:

  • 有些问题只影响显示,略有庆幸;

  • 发错奖励,得趁用户/主播半夜熟睡悄悄回收

  • 分数排名错误,连夜修复,可能在睡梦中继续被电话叫醒

  • 平台资损,老板也不能次次兜底···

随着营收压力增大节奏进一步提速,规模效应逐步放大问题。在不断解决历史问题同时,仍然是小问题不断、大问题随机。逐渐影响用户体验,拖慢平台运营节奏,压力像水一样全方位渗透了过来。“xxx又出了一个bug,导致了xxx问题”,会变成瞩目的话题。

客观来看,活动几十个赛道,叠加多个阶段晋级,数十个榜单和任务,各种花样玩法,一大堆异步脚本,显示和触达细节等等,case成千上万,确实难以做到完全覆盖。究竟有没有一种可能,让我们摆脱这种困境?


技术视角的思考


自从开始用golang简洁的并发功能,我每隔一段时间就会犯闭包问题,每次发现后的捶胸顿足没有让我成为superman,直到代码Linter检测的出现,这个问题才得到了阶段性杜绝。实际上好多问题核心并不一定在人的能力或态度上。

回到活动面临的困境,最根本问题实际上有三个。

其一,从宏观角度上看,是依赖了人,去对抗大规模的本质复杂度。

活动增长类业务根本特点是:1.规模大,2. 时间资源有限。当开发量大细节多,而难有充足的时间开发测试时,出现各式各样的问题是必然的。即使是经验丰富,犯过各式各样问题的supereman,一旦量变引起质变,或者困了乏了,出问题也是不可避免的。从基本规律上看,人是不可靠的,写bug是大概率事件,特别是叠加上规模这个纬度。

其二,从技术建设的角度上看,是技术系统在标准化和数字化程度不够,未能有效收敛边际成本。

系统复用的思路大家都能想到,以往技术上也做了不少的尝试,而面对套路不定花样灵活的业务策略时,不少抽象出的系统很快就用不上了,在一段时间里,技术的认知是活动不可抽象,因为未来不可预知。实际上大量的需求肯定有一定程度的相似度,当某个功能无法复用时,大概率也只是部分不能用。

其三,从研发模式角度看, 是技术生产力模式未能领先业务发展周期。

当上游业务开始提升节奏时,基本上是将认为成熟的套路开始大量套用,相似度开始逐步增加。此时业务阶段开始从发展期开始向平台期过渡,沿着验证期-> 发展期-> 平台期->衰退期链条发展,技术Leader的重要职责之一就是提前识别并做出调整,不能以验证期的打法应对成熟期的业务节奏。

不管是在什么周期,增长类业务要保障效率和质量,都需要让系统能具备一定的复用性,让以往的历史沉淀能降低边际成本,面对规模问题的解决方案就是控制规模效应。

问题的挑战在于,复用和灵活性or变化之间有一定的矛盾,有时不好兼顾。这个问题在制造业也有解决思路,当某类产品要做迭代换新,而生产线已经物理级别成型了怎么办呢?打碎了全部重建成本极高,而且整个过程还会不断反复。

为了最大成本提升复用,制造业中提出了“柔性生产线”的思路,简单讲就是讲基础能力模块化到最细粒度,然后基于特定需求做生产线模块化组装,当需求变化时,只变更对应的模块即可。

这种方法在软件工程领域不用多说,实际上业务迭代也会潜移默化的往这个方向尝试。但由于抽象粒度不够细,组合灵活度不高,导致灵活性较差深陷重复开发的泥潭。我们要做的是及时识别到这个问题,并大力改进即可。


积木式开发


对于比较灵活多变的业务来讲,要降低本质复杂度,需要关注不变的,并识别变化的部分,积木式开发主要从四个方面入手:

  1. 抽象基础不变的能力,逐层构建业务,最大可能提升复用

  2. 流程SOP,对特定流程做标准化表达,屏蔽细节

  3. 自动化。通过工具解放人力

  4. 模块SDK化。提升最后一公里的复用


积木式架构


熟悉动态规划算法的人都知道,有一种解法叫:自底向上,识别最基础模式,从底部往上构建。

回顾营收活动花样百出的套路和玩法虽然非常多,但这里面依赖的底层不变的能力肯定是有限集,柔性生产的过程分为三个层次:

  • 沉淀不变的能力(基于活动业务场景)

  • 搭积木组合常用的功能(沉淀常用的模式-直接复用)

  • 自定义组合&定制,应对变化

简单示意图如下:



基础层:以数值为例,uid, score_id, extend, num, expire ,通过这几个字段,可以存放任意维度的积分,并提供强一致性发放、扣减、流水记录、余额读取等能力,为抽奖、兑换、玩法等复用。

积木层:以晋级为例,从榜单、任务等拉取符合条件的对象,自动报名,实现满足条件的自动晋级。这一层会逐步沉淀常用的模式到后台,可直接创建勾选复用。

定制组合层:以自动加分为例,这里套路多变,常用模式一般满足不了时,可以在此实现定制逻辑。当需要干预特定逻辑时,填写特定path的接口地址即可,可按需实现钩子。样例如下:



积木式架构达成了以下几个结果:

  1. 基础层:沉淀基础能力,屏蔽了底层系统的复杂性,并提供灵活组合的空间

  2. 积木层:最大程度实现复用,常用的模式逐步沉淀在后台,勾选即可使用。原来写大量代码并测试,现在只需创建配置。并以此不断向业务上游推销,以效率成绩倒推更进一步的逻辑简化和标准化

  3. 定制层:最大情况应对变化。当沉淀的能力无法完整满足时,可以实现定制接口干预逻辑,以最小的开发量实现整体复用。当新花样出来时,可以先在定制层实现,验证后沉淀到积木层


流程SOP


在解决复用之后,如何利用积木体系实现需求,并在生产流程中保证质量变成第二块硬骨头。基于积木式系统实现活动时,代码开发量变少了,但配置量迅速上升,中大型活动配置量可在400+以上,在这个过程中保证研发质量并不是一件容易的事情。

基于此,我们基于SOP的思想定义了配置生产流程,保证线上线下配置一样,消灭环境差异。即先在线上创建配置,并推动各个业务后台实现配置导出能力,将线上配置一模一样copy到测试环境,交付给测试跑通。

预演是交付的最后一公里,基于活动广泛的依赖,在线上做冒烟式的预演必不可少,这个过程很必要,但也导致不少问题。预演过程是在线上提前将活动开启,这个过程有几个痛点:

  • 调整线上配置,这可能涉及大量配置修改,完了然后回滚,很容易漏

  • 不能影响线上,也不能被线上流量影响,需要稳定的黑白名单能力

  • 数据污染,经典的例子是预演时爆了一个一等奖,真到了开奖时刻却开不出来了。每次需要很小心清理数据

  • 线上配置检查review,因为量大也担心出问题,这个过程效率较低

历史上因为预演出过不少问题,而且很多是重复发生,很依赖人的细心和运气。基于以前预演过程粗犷的操作,引导SOP的预演系统项目应运而生,基本目标是:提前在线上开启活动,但从理论上避免对线上造成影响,解放体力和心智。

预演系统定义了标准,强制了各个模块配置关联活动,借此实现统一时间Hack、预演黑白名单、流量标记、配置聚集,通过预演后台管控操作功能自动生效,针对性解决了预演过程中的痛点,将预演过程退化成简单无顾虑的简单操作。


自动化


在消灭细节的过程中,通过自动化工具解放了大量人肉操作。最典型的就是奖励配置自动检查和代码自动生成。

每个活动奖励数量一般几十上百,靠人肉检查奖励是否正常非常繁琐且时有纰漏,自动检测发奖可实现一键检测,显示失败原因,效率和质量有了革命性提升。



为了进一步解放开发心智,活动标准化了一套模板方法,每个活动实际上都是一个新的Handler,实现了模板的所有接口,通过活动ID映射。

以往新增活动时,依靠人肉去老代码中拷贝过来再改,一个文件几千行笨重又繁琐,找代码、模块管理都不方便,有时需要为预演注入的代码经常忘记或者写错,依赖人的细心。



对此我们利用golang text/template 开发了一套代码生成工具,可以一键按照模块创建文件,并自动填充相关代码。便捷实现模块化、代码自动生成、自动挂载、自动注入代码等等。很丝滑地将开发从繁琐细节中解放了出来。


代码模块化


有不少功能很难抽象到后台,例如一些非标的脚本,或者变动较大的玩法代码,这种较难实现系统级复用,一般在定制组合层通过函数封装起来,重复开发较为明显。

代码生成工具间接引导了最后一公里的代码复用,有时候某个功能已经封装过了,但不知道的人可能会重复开发。这些定制性强很难标准化的代码模块,工具可以把封装过的代码自动填在对应的方法里,复用的时候只需要简单改改即可,不需要找半天然后复制过来。


实践案例


以BLS全站赛活动案例,看看如何应用积木式开发,这个过程大致分为以下几步:

1.技术方案构思,基于当前需求,思考如何使用已有的业务基建能力实现,并识别变化和需要定制开发的部分。

2.配置创建(线上)。基于当前能复用的部分,在基础层和积木层的服务后台创建配置,按方案串联模块。这个过程在落地技术方案,复用已有能力。(原来重复开发,现在去各个系统后台创建配置&串联)



3.定制逻辑开发。这部分要写代码,主要是两部分:

其一是,一次性定制逻辑钩子(上文提到的path)。如有榜单特殊加分逻辑。例如xx主播在xx条件下实现xx方式的积分加倍,并有xx限制。提供给积木系统自动调用,会带上相关ID及上下文。
其二是,新业务逻辑 or (新花样)只能复用部分模块。新写代码并串联能复用的模块。复用的模块需要创建配置,并持有配置ID调用。

4.交付测试,利用“测试即线上”的配置思想,将线上配置原样复制到测试环境完全跑通,一方面节约了配置时间,同时也杜绝了配置不一致带来的风险,活动团队提供了一键同步配置的工具(ID及配置内容一致)。



5.预演 & 上线,基于预演系统及预演SOP,通过注入预演时间达到灵活控制时态的效果,同时也支持了预演账号隔离,防止预演过程中对线上造成影响,在线上试用后交付。



以榜单特殊加分定制逻辑为切入点,整个系统调用链路示意图如下:



新增的开发工作量仅有两部分,灰色部分表示创建配置,并在后台进行逻辑关联,只有配置工作量,高度复用;深色部分表示定制的开发代码,新写逻辑、部分复用代码、复用sdk,仅需关注增量业务逻辑。


效果


积木式开发这套模式,在实现一定程度的系统复用情况下,也保留了灵活的自定义空间。复杂通用的能力,通过服务或SDK沉淀,每次新增的代码更多的是模块串联组合,或者某个小流程的逻辑定制。开发能更专注于需求差异化的地方,系统串联的代码都相对简单,整体上大幅度降低了需求实现的难度,降低了开发量。开发量少测试量也自然变少,人力资源能投入到更重要的部分。

同时,在常用模式后台化后,大量细节被封装,原来开发要不断关注细节,例如 if xx == yy 写成 if xx !=yy 一级分区和二级分区搞反 等是轻而易举的事,现在关注的更多是逻辑组合、模式和选项。叠加流程SOP,本质复杂度大幅降低。

中小型活动一般一个人就可以并发一个,大型活动一个半人也能搞定,这在以前是两三个人加班加点还可能出一堆问题。如今整个过程相对轻松,且线上问题逐渐消失。在从superMan模式往积木式架构切换的过程中,由于人力紧张,每次都掐点跟着活动上线系统,过程紧张辛苦。但回头看,整个过程中效率和质量呈螺旋上升的态势,加班和心理压力呈螺旋下降态势。

通过交付数据来看:2021年H2累计交付21场定制活动,到2022年H2提升至41场,人力没变的情况下整体交付量提升一倍,效率显著提高;同时质量数据也大幅提升,单活动的平均bug数较原先降低了33%,线上问题也明显下降,原来经常一边开发新需求,一边处理线上问题,现在精力基本都在新需求上。

时至今日,虽然大幅改观,实际上仍然有一些地方可以进一步标准化数字化,靠后的骨头虽然难啃,也在不断进行中。


总结


纵观互联网业务开发,基本上是在做以下几件事,并持续优化:

  1. 能不能做(实现),技术能力

  2. 做的快不快,交付效率

  3. 做的质量好不好,交付质量

  4. 资源消耗是否足够低,成本

本文从增长类业务出发,结合营收活动团队的实战经验,探讨如何在大规模、快节奏情况下实现优质的交付效率和交付质量。

效率

从工程的角度来讲,要实现高交付效率,只有提升复用率这一条路。但面对灵活多变的业务场景,复用会变得困难。如何结合实际业务场景,在灵活和复用之间找到恰当的平衡,则是问题的关键。

在业务高频高压的不断毒打中,我们主动而又冥冥之中走上了积木式开发这条路,从不变的东西出发,逐层构建应用,通过开放钩子接口,定制变化部分实现最大灵活性。

这背后实际上在对业务概念持续不断地做数字化、标准化,最终实现可配置、可组合、可串联管控。

质量

在解决复用之后,我们基于SOP的思想定义了配置生产流程、抽象预演流程、代码创建、配置检查等等,进一步屏蔽人需要关注的细节。

一切重复的流程,在不断变得成熟后都会演变成SOP,基础能力的黑盒化对人屏蔽了细节,并在标准化和规范化中引导高质量的结果,这在各行各业是基本规律,互联网也不例外。

回头看这一路,很庆幸在艰巨的时刻加入营收活动,并有幸引领了团队越过山丘,和伙计们一起历经毒打并最终站上了更高的山峰。

谨以此文献给为营收活动付出汗水的所有伙计,也希望技术人都超越“CURD”的视野,在技术创造价值的过程中不断攻城略地。


参考链接


[1] https://live.bilibili.com/activity/live-activity-full/full-next/index.html?is_live_full_webview=1&app_name=bls_winter_2022&is_live_webview=1#/popularity


以上是今天的分享内容,如果你有什么想法或疑问,欢迎大家在留言区与我们互动,如果喜欢本期内容的话,欢迎点个“在看”吧!


往期精彩指路

继续滑动看下一个
向上滑动看下一个

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存