当前位置: 主页 > 建站知识 > 软件开发

基于构件的软件开发-大规模基于构件的软件开发

发布时间:2023-02-11 22:08   浏览次数:次   作者:佚名

进入互联网“下半场”,依靠“人力”的研发方式已经没有竞争力。 真正有效可行的方式是让系统能力可沉淀、可组合、可复用,灵活应对各种变化。 本文分享了在多格式和大规模定制需求的背景下,如何通过基于装配的开发来提高业务竞争力。

1 简介

在本地生活服务领域,面向C端的信息展示功能具有生物系统的复杂性,体现在以下三个方面:分布在尽可能多的地方 本地展示; 差异大,同样的信息,在不同的客户端、不同的页面、模块下,显示逻辑会有一些差异; 功能多变,产品逻辑经常调整。 以上三个方面的特点给R&D学生带来了极大的挑战。 比如,当我们面对上千个功能模块,几十个行业持续不断的产品需求时,如何快速响应?

进入互联网“下半场”,依靠“人力”的研发方式已经没有竞争力。 让系统能力可沉淀、可组合、可复用,灵活应对各种变化,才是真正可行有效的途径。 本文分享了在多格式和大规模定制需求的背景下,如何通过基于装配的开发来提高业务竞争力。

二、背景及问题 2.1 业务背景

首先,让我们谈谈我们的业务和产品。 美团到店是一个通过“信息”连接消费者和商家,帮助用户降低交易成本的生活服务平台。 这就是信息产品功能的商业价值。 当我们打开美团点评APP搜索“美发”时,可以看到一个搜索结果页面,显示的是根据关键词召回的美发商家(如下图左侧)。 商户下方悬挂着本店提供的团购和会员卡的概况。 我们选择一个店铺进入商户详情页,从上往下滑动可以看到商户的地址模块、商家信息模块等基本模块(如下图右侧所示)。 继续往下还可以看到商品货架模块、会员卡模块、发型师信息等,以上是信息展示商品的具体形式。

大规模基于构件的软件开发_基于lamp架构开发web应用的优势_基于构件的软件开发

图 1 产品功能形态

在上一篇文章中我们提到,本地生活服务行业信息产品的核心特征是功能多、差异大、功能多变。 为了帮助读者更好地了解相关业务背景,我们进一步补充了这些功能:

以上是生活服务行业信息产品的特点。 面对信息显示功能规模化、差异化的挑战,产品不断迭代。 R&D学生面临着什么样的问题和挑战?

2.2 研发挑战

在分享技术挑战之前,可以先看看研发同学的日常生活。 这里有两个小场景:

而我们是一群专注于信息展示的人。 这类系统行业也有一些标准术语,叫做BFF(Backend For Frontend)。 BFF的主要职责是对底层数据进行组合使用,另外处理C端的展示逻辑。 总结起来,我们研发同学的具体工作通常是:通过外部数据源找到原始数据,然后将找到的原始信息根据产品的要求加工成可以展示给用户的信息,最后发送给客户端使用。 如下图所示,这部分工作主要负责中间BFF API服务:

基于lamp架构开发web应用的优势_基于构件的软件开发_大规模基于构件的软件开发

图2 研发主要工作

看到这里,我们猜你可能会有这样的想法:这么简单的工作能有什么技术挑战? 确实,从纯编码的角度来看,代码“卷起来”就完事了,真的没什么挑战。 然而,这不是一个简单的编码问题,而是一个工程问题。 当这样的功能数以千计,产品需求不断涌入时,如何用有限的研发资源满足无限的业务需求,同时控制系统的复杂度和运维成本,还要考虑产品的问题人的成长,这是解决这个问题的必由之路。 是我们面临的主要挑战。

大规模基于构件的软件开发_基于构件的软件开发_基于lamp架构开发web应用的优势

这些问题的根源是什么? 这里想借用知乎上美团联合创始人王慧文的一句话,用理工求真。 很难定义什么是真理,但我们必须使用科学和工程方法。 下面将进一步介绍我们对这个问题的思考以及我们的解决方案。

三、问题分析及解决思路 3.1 问题思考

常规编程的基础工作总是基于一定的编程范式,如面向对象、过程式、函数式等。 人们很容易认为,如果解决问题的范式与问题不能很好地契合,那么就会产生矛盾。 因此,历史上有很多使用汇编语言难以完成大型项目,使用结构化程序设计难以应对超大型项目的故事。

那么,上面提到的问题是不是开发方式和业务问题不匹配导致的呢? 举个通俗的例子,我们现在要做的是一个西红柿炒鸡蛋,但是我们拿出来的工具是一个电烤箱,后果可想而知。 当然,或许有人会说“真正的高手还好”,但毕竟绝世高手少之又少。 如果我们拿出一个平底锅,那肯定会更容易。 因此,我们不应一味追求成为解决问题的“绝世高手”,降低解决问题的门槛才是真正有效的途径。

静静反思,我们认为真正的原因是我们使用的开发范式与业务问题不匹配。 也就是说,我们缺乏有针对性的问题建模,缺乏有针对性的方法论。 比如在业务层面,我们的诉求是能够快速交付,满足需求的多样性,能够快速响应产品功能的灵活变化。 在技​​术层面,我们的诉求是在人力有限的背景下,让系统的复杂度可控,代码的复杂度不会在与业务逻辑的“乘法”关系中增加。 此外,还要保证运维成本可控,系统数量不会与功能数量呈“线性关系”增长。

但是,我们现在的开发方式是“过程化”的,这种“过程化”体现在面向产品需求文档的直译编程。 但是这种开发模式不符合我们的需求。 因此,我们需要找到适合自己的开发范式。

3.2 标准化和装配思想

John Ousterhout 曾经说过:复杂性是由歧义和依赖造成的。 歧义主要源于对事物缺乏明确的概念描述,因此复杂性通常通过应用许多关键概念来解决,这些概念通过抽象、分解、迭代和细化等方法来表达。 建立清晰的概念是消除歧义的关键,也是我们解决复杂问题的常规思维方式。

在这个过程中,分解是指将一个较大的问题分解成较小的、易于管理的单元,每个单元都可以单独处理,这些单元称为模块、包或组件。 这个思想可以追溯到哲学上的“还原论”,它已经成为许多软件工程方法论的核心。 依赖性是指模块之间依赖关系的数量和强度,比如一个模块是否依赖于另一个模块的实现,模块之间的依赖关系是否遵循统一的契约,这些都会影响系统的复杂度。

装配式开发是指将系统分解为标准组件,然后由标准组件组装系统。 这样形成的建筑称为组装式建筑。 与传统代码重用技术的主要区别在于组件的含义。 组件是高度标准化的单元,具有可重用性、标准化、可替换性、封装性和独立性等特点。 装配式开发背后的核心逻辑是基于标准化思想的代码复用。

关于标准化,历史上有这样一个故事:18世纪末,美国刚刚建国,由于国内外战火尚未停息,政府担心与法国开战基于构件的软件开发,并且急需准备大量的军火。 然而,当时传统的枪支制作方式是依靠熟练的工匠,通过打磨、削片、锤击等工序制作出非标准的枪栓零件,然后再组装成枪支。 ,生产效率也很低。 于是,在政府的催促下,Eli Whitney(美国发明家、机械工程师和企业家,发明了轧棉机和轧棉机)将整个过程分成了几个过程,每个部分都与标准步枪的样品进行了对比纺纱成通用零件,从而成功地将零件可更换原理引入军火生产,是一个划时代的工业标准化的开端。 此后,标准化的互换性原则推动了工业的快速发展,惠特尼也被誉为现代工业的“标准化之父”。

大规模基于构件的软件开发_基于lamp架构开发web应用的优势_基于构件的软件开发

图3 “标准化原则”的应用

再举个例子,我们都知道乐高积木。 玩具制造商生产了一批标准的积木。 孩子们可以根据这些积木拼出不同风格的玩具。 如果他们喜欢飞机,他们可以组装飞机。 如果他们喜欢坦克,他们可以组装坦克。 利用了标准化互换性的原则。 回到软件行业基于构件的软件开发,基于组件的大规模软件复用的概念起源于1968年Doug McIlroy在软件工程学会的一次演讲,演讲名称为《Mass-produced Software Components》,后来被称为Recognized作为软件重用的起源。 基于这种思想,如果我们能够引入组装开发的思想,将业务代码分解成标准化的组件,然后基于这些组件组装成不同的功能,以满足不同场景下的业务需求,将会不能满足我们的需求。 开发方法?

但说起来容易做起来难,因为它忽略了很多实际的细节,我们在实际应用中总是面临各种实际问题的挑战。 柏拉图曾定义人生的终极问题:我是谁? 我从哪里来? 我要去哪里? 这些问题一直持续到今天,一直困扰着人们和人类。 而软件工程也向工程师提出了软件设计的终极问题:什么是抽象层次? 什么粒度? 以及如何应对变化?

因此,装配发展的历史是坎坷的,而这些问题的答案在各个技术领域也不尽相同,更是难上加难。 比如在粒度的问题上,组件的粒度有多大? 粒度越大,被修改的风险越大。 粒度太小虽然可以使组件更稳定,但也会给装配带来复杂性。 . 再比如在处理变化的时候,很难区分这个逻辑是通用的还是个别的,但是我们的系统设计非常依赖这个判断。 如果最初的判断错误,可能会导致系统最终失败。 . 我们以前遇到过这种情况。 一旦我们信心十足地封装了一个组件,觉得它应该能够满足各种复杂的情况。 就在下一个需求来的时候,我们发现不匹配。

大规模基于构件的软件开发_基于构件的软件开发_基于lamp架构开发web应用的优势

3.3 我们的解决思路

如前所述,组装开发似乎正是我们需要的开发模式,然后我们也讨论了组装开发面临的关键挑战。 那么,在我们的实际业务中,组装式开发真的可行吗? 如果是这样,我们如何应对随之而来的挑战? 尤其是本地生活产业,细分领域多,功能多。 当然,商业在给技术带来挑战的同时,也蕴含着巨大的机遇。 因为涉及的行业越多,系统功能越多,代码重用的几率就越高(如下左图所示)。 虽然不同的功能给人的感官印象截然不同,但是很多功能的底层却有着太多的共性。 我们认为解决问题的关键在于粒度的控制和变异性的处理。

大规模基于构件的软件开发_基于构件的软件开发_基于lamp架构开发web应用的优势

图4 组装式开发的可行性及关键思路

那么,我们主要从粒度和可变性两个方面入手。 在粒度问题上,我们引入了多层次、多粒度的组件系统设计; 在可变性问题上,我们通过可变性建模,让整个功能具备灵活应对变化的能力,最终形成如上右图所示的概念模型。 此外,我们还通过视觉组装简化了组装过程。 总体思路如下:

通过以上策略,我们将信息展示场景的研发模式打造为多系列产品生产线,每条生产线支持一系列定制化功能的组装生产。 我们将在下一章介绍更多技术细节。

4. 后端BFF标准化思维和组装式架构的实践 4.1 产品功能分类系列化 1)产品系列化

在官方语言中,序列化是指“对同类型产品的结构形式和主要参数规格进行科学规划的标准化形式”。 在我们的案例中,序列化是指对信息产品的功能进行分类。 目的包括两个方面。 一方面是为了降低系统的整体复杂度,另一方面也是为建设一条组装这些功能的“生产线”做准备。 ,一系列的功能被一条“生产线”组装起来,组件可以在不同的范围内复用。

信息产品展示的内容通常来自多个领域。 例如,产品展示模块可以聚合商店信息。 直接按领域划分很难,那么系列怎么划分呢? 我们可以直接看到明显的区别,主要包括两个方面。 首先是展示内容。 我们不难发现,每个显示模块在显示内容上的主要区别。 例如,主要内容是店铺信息和评价信息。 、内容信息、产品信息等内容,其他信息往往附加在主要内容之后。 其次,在展示风格上,有些展示风格明显不同,比如商品详情页和商品货架模块; 有些陈列风格差别不大,比如同一个产品货架模块,只是个别领域有区别。 隐式差异主要是内部实现,因为这些实现不是直观可见的。

基于构件的软件开发_基于lamp架构开发web应用的优势_大规模基于构件的软件开发

图5 信息产品功能分类思路

我们主要从展示内容、展示风格、展示逻辑实现等几个方面对功能进行分类合并:

以上维度不是绝对的优先级关系,但是可以解决大部分问题。 也有例外。 比如有些功能可能会同时显示多个信息,但是找不到主要的显示对象,我们可以根据这个因素来选择。 经过以上一波操作,我们基本可以将产品功能的系列化情况尽收眼底。 几千个函数序列化后,就只有几个系列了。 以上只是业务层面的划分,那么相应的系统设计是怎样的呢?

2)接口标准化

产品功能系列化分类后,同一系列产品功能之间仍然存在逻辑差异,这些差异主要体现在展示模型和内部实现上。 显示模型是后端吐给前端的数据结构。 它的主要职责是承载显示数据。 不同的功能有领域的差异,所以模型也会不同。 举个简单的例子,有的函数有label字段,有的没有,所以label字段就有区别。 内部实现主要包括数据查询逻辑、处理逻辑等。针对这两个差异,我们的核心思想是标准化接口模型,统一业务标识,连接差异化逻辑。

大规模基于构件的软件开发_基于构件的软件开发_基于lamp架构开发web应用的优势

大规模基于构件的软件开发_基于构件的软件开发_基于lamp架构开发web应用的优势

图6 标准接口+多租户模式

序列化本身更有利于接口模型的统一抽象,因为同一系列的内部函数在结构上不会有太大差异,我们只需要做一点抽象,大部分领域都可以收敛。 对于极少数的个别情况,比如某个字段只对个别功能可用,我们使用KV结构来处理。

模型设计的具体细节这里不再赘述。 重点是接口统一标准化后,有明显的好处。 前后端协同效率得到提升,前后端不再需要每次需求变更时都面对面沟通。 不仅如此,系统级接口的标准化还可以让前端代码与后端接口的集成关系更加稳定。

拿到接口后,内部差异怎么办? 下面将介绍这部分我们的组装思路。 在这种思路下,内部实现的差异最终会表现为一系列不同粒度组件集合的差异,因此这里要解决的核心问题是如何识别功能差异化的组装组件。 为了解决这个问题,我们的思路是引入目前广泛使用的“业务标识”的概念。 我们使用业务标识将不同业务场景的数据组装组件串联起来,实现差异化的逻辑处理。

4.2 功能组件提取及预组装 1)功能组件提取

领域驱动方法(DDD)在业界广泛用于系统分区。 基于领域驱动的方法,我们一般根据实体或聚合根划分子系统或模块,但很难应用于信息显示系统。 领域驱动的方法。 因为我们开发的不是一个单一领域的小系统,而是一个跨越多个领域,由几千人共同开发的复杂分布式系统的子系统。 该系统负责查询、组装底层系统提供的数据,然后加工、裁剪、组装展示模型给前端。 此类系统远离业务实体,因此对于此类系统的组件分解,无法应用传统的领域驱动方法,而需要采用特殊的方法。 我们的基本思路是将现有的流程步骤进行梳理,将现有的功能按照相关性进行分类,将同一类型的功能封装成一个功能组件,然后将多个组件组合成一系列的功能。 这是一个例子:

基于lamp架构开发web应用的优势_大规模基于构件的软件开发_基于构件的软件开发

图 7 组件提取案例

左图为商品货架场景,右图为制作该货架所需展示数据所需要经历的流程步骤。 从上图我们可以看出,货架陈列数据的制作过程主要包括以下几个步骤:

根据查询条件查询商品ID,比如查询店铺下可销售的团单ID; 聚合产品维度信息,包括产品名称、价格、服务流程、折扣促销、标签等; 查询货架筛选数据和商品信息摆放规则,如推荐、热销、新品等标签的数量,哪些商品放在哪些标签下的摆放规则; 组装结果展示模型涉及到聚合数据的再加工,比如标题和标签的拼接。

以上流程步骤比较清晰,可以适用于一类场景,但不同场景的步骤会有一些差异。 所以我们可以把这些步骤抽离出来,将每一个步骤封装成一个单独的组件负责解决一类问题,组件可以组合复用。 值得强调的是,这些组件的封装都是基于标准接口实现的。

2)功能部件的预组装

传统业务流程编排适用于流程业务场景,如OA办公审阅系统,基于业务流程引擎的好处,一方面易于实现能力复用,复用现有能力编排新流程. 另一方面更容易应对流程变更,因此特别适用于流程容易变更的场景。 组件类似于业务流程编排系统中的“能力”,通过业务流程引擎可以达到类似的效果。 但实际上,信息展示场景的业务流程更像是一个图,姑且称之为“活动图”,而不是一个长长的流水线流程,如下图所示:

基于lamp架构开发web应用的优势_大规模基于构件的软件开发_基于构件的软件开发

图8 筛选与未筛选的区别

流程引擎适合响应流程的变化,而在我们的业务场景中,变化不在流程本身,而是在这个活动图上执行的步骤集合。 如上例所示,左边是一个带过滤的货架,需要查询过滤标签数据,所以执行了整个活动图。 右边是没有过滤的情况,不需要检查和过滤标签数据,所以执行了活动图的子图。 还有更复杂的实际业务场景的活动图。 这里只是一个简单的例子。

大规模基于构件的软件开发_基于lamp架构开发web应用的优势_基于构件的软件开发

我们会发现,不同情况的区别在于,遍历活动图的节点集合不同,这大体上类似于在完全图中选择一个子图。 因此,我们选择用图表来组织我们的组件,而不是传统的流程​​,这样更适合我们的实际情况,也更容易理解。

此外,为了提高组件组装效率,我们提前将一系列功能中用到的组件全部组装起来,以求全景。 然后在需要的时候,我们只需要针对这个完整的图选择一个子图,然后根据子图组装定制的组件即可。 预组装不仅可以提高组装效率,还可以避免错误,使系统更加稳定。 在传统的业务流程编排中,能力的应用场景其实是有限的。 虽然流程引擎已经足够灵活,但实际上在编排能力时还是需要手动测试能力,以确认是否能够满足当前的流程。 预组装可以从根本上避免这个问题,因为只要存在路径就可以执行。

4.3 变化响应的可变性建模

通过将功能组件组织成活动图,每个活动图负责解决一系列的产品功能展示信息组装问题。 这个时候组件粒度还是比较大的。 组件粒度大的问题是容易出现不稳定。 从软件设计的角度来说,是因为变化的因素太多了。 例如,在组装展示模型的过程中,展示模型组装组件负责将数据组装成展示模型发送给前端。 在实际业务场景中,同一个显示领域的拼接有不同的拼接策略。 下面以开头的例子来说明:

基于lamp架构开发web应用的优势_大规模基于构件的软件开发_基于构件的软件开发

图9 不同情况下的组装逻辑不同

左侧美容行业产品名称的拼装规则为“服务类型+产品名称”,右侧汽车维修/汽车行业产品名称的拼装规则为“服务特色+产品名称”。 其实这个组件的粒度刚刚好,因为它让我们的活动图看起来不会太复杂,但是如何处理这个变化呢? 作为一个有经验的程序员,你可能很自然地想到条件分支语句if...else...,而在实践中,很多项目都是用if...else...来处理这个问题的。

对于简单的情况,使用这种方法没有错。 我们在这里讨论更复杂的情况。 过度使用条件分支至少有两个问题。 一方面,代码会变得非常复杂,就像密密麻麻的电线一样,这样的代码很难理解和维护。 另一方面,这种模式本身使共享组件变得极不稳定。 如果我们的系统建立在频繁变化的基础上,我们就很难保证系统的稳定性。 共享组件的每次更改都面临失败的风险。 为了使变更可控,我们需要对变更进行建模。

1)变异性建模

近年来,软件工程在如何处理“变化”方面最具革命性的创新是将共性与变化分离,分离的变化是通过使用扩展点代码或配置变量来实现的。 这些经典的想法真是太棒了。 对我们也很有启发。 如果把易变的逻辑和变的逻辑分开,同时引入配置能力,那么我们的组件就很容易响应变化。 因此,对于可变性的管理,我们引入了通过标准功能组件进行可变性分离的思想来解决。 如下图所示,组件本身有两种适配:变化点和配置项:

大规模基于构件的软件开发_基于lamp架构开发web应用的优势_基于构件的软件开发

图10 变函数建模变点-可选项-配置项

变更点的概念是我们对扩展点代码的具体化,暗指容易变更的地方; 配置项用于承担变量的提取。 变化点和配置项都是处理变化的方式,那么在实际应用中如何选择呢? 对于可枚举的变体,可以提取变量配置。 对于复杂度可变性,可以通过变化点进行扩展。 变化点的具体形式是接口。 变点的具体实现放在组件外部,组件内部的公共逻辑部分只调用变点接口。 这样,无论变化点如何变化,即使有一百个变体,也不会影响到组件的公共部分。

具体的情况,比如查询产品ID这个组件,我们其实有多个查询指标,比如产品推荐指标,产品筛选指标,还有一些实时性强的指标,但是总体来说还是比较稳定和可枚举的,所以我们会不同的是,查询索引建模为查询通道配置项,使用时只需要填写查询通道配置项即可。 再比如显示模型组装组件,因为标题的拼接规则会发生变化,而且是不可枚举的,而且产品规则变化很大,所以我们在标题中设置一个变化点,不同标题组装的逻辑是变化点。 不同的实现,更能适应变化。

不同变化点的实现可以认为是组件提供的多种功能特性,因此我们抽象出选项的概念来描述变化点的实现。 一个变化点有多个选项,选项可重复使用,贴合现实世界,更容易理解。 组装时,只需“选择所需的功能选项”即可满足定制要求。 The above set of concepts of change points-optional items-configuration items solves the problem of component flexibility, and at the same time makes the component itself more stable.

2) Optional option explosion problem

大规模基于构件的软件开发_基于构件的软件开发_基于lamp架构开发web应用的优势

We found that if each option is hard-coded, some change points may have a lot of options. For example, in the label field, the label is often derived from extended attributes. The models and extended attributes of different products are different, resulting in great differences in the source of this label. If we all implement options through hard coding, then the problem of option proliferation caused by differences in label sources will be obvious. There are as many options as there are possible commodities. Options are essentially a finer-grained component, and the component itself also needs to have the ability to respond to changes. Therefore, our solution is to add configurability to the fine-grained component of the option. Each option can design its own configuration items, and realize the variable rules through configuration, so that the options have the ability to respond to changes to a certain extent. thus ended.

4.4 Visual assembly and configuration filling

In the past, when we needed to develop a display function, we needed to do the following things:

Write the code that glues external data together, including remote RPC access code and data assembly code; write the display processing logic according to PRD; fill the processed data fields on the display model defined with the front-end classmates; Build and integrate code.

The basic requirement of assembly development is to encapsulate system functions into component units of different granularities based on standard interfaces. This requirement has brought about a revolutionary change in the production process of product functions. Functional assembly no longer requires handwritten "glue code", but automated assembly can be completed through the system. Most of the functions are to reuse existing components instead of rewriting them. In fact, through the continuous precipitation of functional feature components, we have realized that more than 80% of the product logic is to reuse existing components.

At this point, our R&D process as a whole can be divided into two stages: component development and assembly integration. The component development phase focuses on the abstraction and encapsulation of functions. Based on the existing components, what the assembly and integration phase does is to select the required components and fill in the configuration. Once the assembly results are saved, the system integration is completed, and components and deployment are no longer needed. The diagram below shows the process of selecting and integrating components:

大规模基于构件的软件开发_基于lamp架构开发web应用的优势_基于构件的软件开发

Figure 11 Select function-select feature-fill configuration

The process of component selection and assembly revolves around the activity diagram. It generally goes through three steps of selecting functions-selecting features-filling configuration. The specific work of each step is as follows:

After the configuration is filled, it can be released. When the system is running, multiple product functions share a set of component instances at runtime. The difference lies in the combination and configuration of the execution components. This is achieved through the business identities mentioned above. Different business identities are associated with different component assembly DSLs and component user configurations, and finally realize differentiated function assembly and execution.

5.总结

Did assembly-style development practices solve the original problem? Components are divided into large-grained functional components and small-grained feature components. Components of different granularities have reusability and adaptability, so the efficiency of display function construction has been significantly improved. Internal data shows that the development efficiency of our group has increased by at least 50%.

Secondly, each component unit is a well-designed logical unit, and the scale of a single component is controlled, so the complexity of the code is significantly reduced. The practice results show that the code cyclomatic complexity of the business components naturally developed by R&D students does not exceed 10. In addition, through the serialization of information functions, the entire information display system has also been closed, and the number of interfaces has been reduced from hundreds to single digits, greatly reducing the maintenance cost of interfaces.

Finally, in the past, R&D personnel translated business requirements "procedurally", but now they need to consider how to design components. Because the architecture itself provides such conditions and also has such requirements, R&D students need to consider encapsulation and abstraction issues in the process of "adding bricks and tiles" to the system to integrate into the system. Encapsulation and abstraction are the basic software engineering thinking, which turns "physical activity" into "mental activity". Now R&D students are more like software engineers, and they feel more fulfilled at work. So, overall we've had pretty good results.

Each domain has its own complexity. For example, in some domains, the problem lies in the complexity of calculation, and in some domains, the storage and maintenance of models is complex. Since software development is an engineering problem, we cannot only consider technical complexity, but also business and personnel issues. Scientific thinking tells us that we must pay attention to paradigms in solving problems. When a paradigm is not satisfied, we need the courage to dare to break through. This article mainly introduces how we use the new development paradigm to solve the problems we face in the information display scenario. 希望对大家有所帮助。 You are also welcome to leave a message at the end of the article to communicate with us.

6. References Authors of this article

Lu Chen, Zhiyuan, Chen Qi, etc. are all from the comprehensive technical team of Meituan Daodian.