人物采访 / T SALON

编辑内容

曹立成谈 1688 终端架构:动态容器、低代码与开发者成长

回顾 T Chat 第 13 期两段录播,从强运营业务的动态化诉求、端侧容器与页面搭建,到架构收敛、多端一致性,以及曹立成的 Android 经历、技术表达和团队建设。

T Chat 第 13 期曹立成分享 1688 终端架构的封面
T Chat 第 13 期曹立成分享 1688 终端架构的封面

本期视频发布于 2022 年。文中的技术状态、个人经历与观点均为当时情况。

第 13 期 T Chat 的前半场,曹立成以 1688 的实际业务为例,解释一个大型客户端怎样支持频繁变化的运营活动;后半场则回到个人,从最早折腾 Android 手机,聊到进入基础架构团队、练习技术表达、建立团队氛围与选择成长方向。两段录播合计约 93 分钟。

从业务变化出发设计容器

页面增长快于人手,发版也跟不上运营节奏

强运营业务首先遇到的并不是技术选型问题,而是变化速度不匹配。活动可能今天上线、下午调整,客户端却有固定的发版窗口。促销页面持续增加,开发团队人数不会同比增加;同一个应用又可能同时存在原生、H5 和多种跨端技术,页面展示与交互逐渐变得复杂。

曹立成给出的起点是把页面拆成容器和组件:容器承接底层能力,页面中的不同区块通过组件组合。如果组件能够独立调整、上线和下线,大部分运营变化就不必等待整个应用发版。问题由此从“怎样更快写完一个页面”,转向“怎样让页面能够持续组合和调整”。

他拿 1688 首页作拆解示例:不把整张首页交给一个固定实现,而是让容器托住页面,再把活动位、推荐区等拆成可插拔组件。一个促销活动结束时,可以撤掉相应区块;新玩法上线时,在已有能力范围内换入新的组件配置。这个模型针对的是“活动上线快、页面数量多、人手有限”同时发生的情况。组件化本身并不会让新能力凭空出现,底层仍需先准备可复用的组件和容器能力。

容器承担效率、体验下限和多端复用

容器有三个目标。第一是提高研发效率,使开发者围绕组件工作。第二是保障体验和稳定性的下限:动态化增加灵活性,也可能让不同组件的性能、资源占用和异常处理参差不齐,公共底座要把这些差异控制在可接受的范围内。第三是支持多个客户端复用,1688 的业务除了主客户端,也包括面向商家和细分品类的产品。

他以常用网络库作类比:公共框架的价值之一,是让经验不同的工程师在使用相同能力时得到相对一致的结果。容器同样需要让业务开发者能够灵活做事,又不必人人重新解决资源、通信和稳定性问题。

曹立成特别把“体验下限”与灵活性一起讲。动态页面交给更多人生产之后,同样的组件可能运行在大量 Android 机型上;若缺少统一的资源处理、异常兜底和数据接入,某个活动配置就可能造成卡顿甚至无法响应。容器像团队统一使用网络库一样,把容易重复出错的部分收拢起来,让新人和老手在相同的基础上开发。至于页面是否最终达到目标,仍需要通过真实设备和线上数据观察。

先分场景,再统一底层能力

首页、搜索和商品详情直接影响浏览、下单,关注性能与体验;营销页面强调快速变化;对外开放的业务需要承接商家的扩展;收藏、足迹等工具场景则相对稳定。容器要服务这些不同需求,不能只围绕某一种页面设计。

他的架构将业务场景与公共能力分开:上层通过统一 API 接入,下层提供插件、事件、调度、交互、埋点、资源、数据和脚本运行等能力,再与数据平台衔接。比如浮层、弹窗和页面可能同时出现,需要调度优先级;资源预推和缓存影响首屏;业务效果、性能与用户分群又需要共同的数据基础。

这四类场景对应不同的约束。首页、搜索、商品详情不能轻易打断浏览和下单;营销活动可能上午发布、下午就要撤下;商家扩展需要给外部使用者留接口;收藏、足迹和直播工具之类的页面变化相对慢一些。曹立成没有把某一种渲染技术当作四类页面的统一答案。他先让团队列出业务要什么,再看容器应提供哪些共同能力,哪些差异留给上层适配。

协议和工作流程比技术栈的数量更关键

容器与业务之间主要通过插件、事件、调度和交互协议协作。不同渲染方案可以使用各自的桥接方式适配协议,但不应迫使每个业务重新理解容器内部实现。底层能力收拢、协议稳定之后,变化更多发生在上层业务。

四种协议在分享里各有用途:插件允许容器调用业务提供的能力;事件负责不同容器或页面之间的通信;调度决定任务和浮层何时、对谁触发;交互处理点击、滑动等动作及反馈。假如页面上既有主内容,又有可拖动的小浮标和弹窗,容器不能只是把它们画出来,还要知道展示先后、事件怎样传递。不同技术栈可经由 Bridge、Channel 或原生接口接入,面向业务的约定则尽量保持统一。

一个简化的工作流程是:启动容器,加载协议,取得组件结构和对应数据,完成渲染,再处理交互。页面、卡片、浮层虽然大小不同,仍可以沿用这一流程。曹立成也提醒,架构图列出了多种适配能力,并不代表团队必须同时采用所有方案;满足自身需求的一两种方案就可能足够。

他把协议加载后最先需要的内容归成两类:有哪些组件,以及每个组件展示什么数据。结构与数据齐备,容器才有条件渲染一个可见页面;随后还要处理用户操作以及组件之间的通信。这条流程既能用于整页,也能用于页内卡片或浮层。把流程固定下来,业务排查时才知道应该先看下发、数据、渲染还是事件,而不是在几个渲染框架里分别寻找入口。

多端复用的单位是业务模块和组件

在容器之下,需要先解耦基础库和业务能力;容器之上,搜索、推荐、商品、会员和消息等业务模块再组合成不同页面。同一个推荐模块可能出现在首页,也可能出现在工作台。不同客户端通过皮肤、资源、数据参数和交互配置形成差异。

他展示主客户端与工业品类客户端的页面,说明看起来不同的产品可以共享大量模块。这里的前提是业务结构本来就有共同部分,并且团队已把这些共同部分沉淀为组件。容器提供复用条件,组件划分与业务解耦决定能复用到什么程度。

比如推荐模块既可能出现在首页,也可能出现在工作台;面向不同客户端时,皮肤、交互或接口参数会不同,但底层返回的数据结构和大部分 UI 表达可能相近。团队先解耦基础库、商品和推荐等业务能力,再让容器组合这些模块,便能把“复制一个页面”变成“复用模块并配置差异”。曹立成也指出,他在图中画了很多种容器,只是为了展示适配范围;要在小团队落地,支持一种合适的动态方案也可以起步。

用 DSL 和搭建平台把动态化落地

一个可理解的起点:DSL 描述,原生组件渲染

为了让听众能把思路带回自己的项目,曹立成给出较小的实现模型:使用 DSL 描述布局和组件,在客户端解析为原生界面。他将这种方式称为原生增强。它能够沿用原生渲染能力,额外引入的引擎和学习成本较少,也方便通过下发描述实现动态布局。

他用 Android 的 XML 布局打比方:原先布局跟应用一起打包,改动要等下一版;如果在 Web 编辑环境里编写同类描述,客户端收到后再解析,已有原生组件就能按新布局组合。布局可采用 Flexbox 这样的跨端表达,减少两端分别写布局的工作。与引入完整的新运行时相比,这种模型在他的经验里较轻,适合作为团队理解动态化的第一步。但页面仍须请求、解析和绑定数据,实际性能要在目标场景检验。

可以把它理解为将原本写在本地的布局描述移到平台:开发者在 Web 编辑环境中编辑,平台下发描述,客户端按约定解析。分享也介绍了 Flexbox 布局与现有开源布局、容器项目作为参考,并说明 1688 当时大量使用集团的动态化能力。完整的生产方案仍需补足发布、稳定性、性能和业务定制,不能把一个演示级解析器等同于成熟平台。

这个简化模型也解释了动态化的边界:下发的 XML 或 JSON 告诉客户端该使用什么布局、组件和样式,实际绘制仍由应用中已有的原生组件完成。主持人在现场问这是否等于“热更新”;曹立成区分了两者——这里主要是更新数据与布局描述,并没有给客户端下发新的可执行代码。一个点击跳转如果属于预置事件,可由协议触发;若要新增从未内置的交互,就必须让客户端通过版本更新获得该能力。

这种方案有明确边界:如果旧版应用里根本没有某个原生组件或交互能力,下发对应标签也无法让它凭空出现。增加新的客户端能力仍然需要发版,并处理新旧版本兼容。动态布局不能消除所有版本约束。

搭建平台先有物料,再有页面

低代码搭建的前提是已有可用组件。组件发布到物料池后,业务人员创建页面,再编辑容器层级、组件位置和数据。曹立成展示的平台并不要求所有操作都表现为自由拖拽,增加和删除组件也可以完成搭建;关键在于组件和协议具备清楚的语义。

数据有两条路径:一部分直接写入页面下发协议;另一部分描述动态数据源,由客户端请求接口后再与组件绑定。皮肤、实验、埋点和性能配置可以作为扩展信息逐渐加入。页面维度还可以挂载公共插件,避免每个组件重复承担同样的工作。

曹立成按搭建步骤介绍平台:先把组件放入物料池,再创建页面、选择组件和容器层级,确定各组件的数据来源,最后预览并发布。页面中哪一层放哪个组件,决定了后续数据和事件的作用范围;页面级的实验与统计则适合放在公共插件里。他提醒想自己搭平台的团队,可以先把层级、数据和组件协议做清楚,再按业务需要逐渐加入皮肤、投放等扩展字段,不必一开始照搬大平台的全部配置。

预览需要反映真实客户端条件

搭建后的静态预览往往展示所有组件的集合,真实页面却受到登录状态、客户端版本、人群条件和投放时间影响。因此平台支持扫码,在实际客户端上检查层级、样式、数据与展示条件,再发布页面。

运营可以配置不同时间段的皮肤和资源,到了相应时间自动切换。客户端拿到发布后的页面地址,交给容器完成协议请求、数据处理与渲染。这样,已具备的组件能力可以被反复组合,日常活动调整也不必总由客户端工程师重新编写整个页面。

他在演示中指出,编辑器里能看到十几个候选组件,用户真正打开页面时却可能只看见其中一部分,因为组件还受登录状态、人群、投放时段和版本条件控制。静态画布显示的是搭建结果的集合,不等于某台设备上的真实页面。扫码到当前版本客户端预览,可以检查间距、背景、瀑布流等布局,也能发现某个组件在旧版本中根本不存在。发布后,客户端入口收到页面地址,由容器完成请求、绑定和绘制;这段流程的自动化,才使运营调整不必每次重新开发整页。

下发协议如何变成可交互的页面

协议主要描述四类信息:布局结构、组件绑定的数据、组件模板和组件样式。布局决定层级与排列;数据可以内置,也可以来自动态接口;模板描述使用什么组件及其属性;样式处理背景、间距和透明度等表现。

模板与数据必须提前约定绑定关系。比如文本组件的内容不是写死的,而是指向返回数据中的某个字段,那么容器先解析占位关系,等数据到达后再填入相应位置。点击跳转等预置行为也可以在协议中描述;超出已有能力的行为仍需客户端实现。

曹立成用一个文本组件解释数据绑定:模板预留 text 属性,协议约定该属性对应接口返回数据中的某个字段;容器解析模板时先识别占位,动态请求完成后,再按字段路径把具体文案放进去。布局、数据、模板和样式看似都装在一份大协议里,运行时却是按依赖次序逐步合成页面。这也说明为什么接口返回结构和模板约定必须一起维护:字段位置一变,客户端即使仍能解析协议,也可能展示不出正确内容。

现场问题:大协议、定制入口和旧版本兜底

面对“一个很大的 JSON 会不会拖慢响应”的问题,他承认全量下发会带来成本。可以精简部分描述,也可以利用缓存、拆分请求与结果拼接;首页、搜索和详情等核心场景还可以使用标准化的定制入口干预默认流程。

这个问答没有被他用一句“做缓存就行”带过。完整协议可能包含大量结构和数据,若一次请求全部返回,会增加等待;把部分资源预先缓存、把动态数据拆到其他接口,再按需拼接,可以减少首屏必须等待的内容。对于首页、搜索、商品详情,团队还留有统一的定制入口,让业务在默认加载流程上做适合自身场景的优化。因此统一容器并不等于所有页面必须经历同样的请求顺序。

他区分了动态下发布局描述与直接下发可执行代码:这里讨论的基础模型仍然由本地组件完成渲染。某个组件在旧版应用中不存在时,不应假设它能正常展示。分享中提到的开源项目提供局部参考,所展示的整套内部平台当时并没有作为一个完整项目开源。

当基础设施成熟,架构继续往哪里走

收敛方案,让流程可以观察和度量

在一个长期演进的客户端中,同类技术方案容易越积越多。曹立成认为下一步首先需要做减法,收敛重复方案、统一核心流程。架构也应当“白盒化”:业务开发者知道关键流程在哪里、哪些环节能够扩展,排查问题时能找到明确入口。

在此基础上,通过日志、链路与指标建立可观测能力。崩溃告警往往说明问题已经发生,而更细的观测可以帮助团队按页面、人群和设备分析达标情况,追踪依赖、定位问题。分享强调的是将这些能力落实到客户端具体场景,并没有把“建立平台”本身当成最终收益。

曹立成用 Android 自定义 View 的 measure、layout、draw 说明“白盒化”:开发者知道关键步骤,就知道问题发生时从哪里入手。容器也应有少数清晰的关键流程和扩展点。监控只能在崩溃等显性问题出现后报警,细分的日志、调用链和指标则有机会显示某类页面、某组用户或某档设备的体验正在偏离目标。团队可以据此追踪依赖和定位瓶颈,再决定是否需要改协议、资源或具体组件。

多端一致性需要对应的工程手段

业务足够复杂之后,Android 和 iOS 对同一条件产生不同行为,会让排查和兜底越来越困难。他提出两种思路:保留各端实现,但加强逻辑对齐和交叉审查;或者将适合共享的逻辑放到同一份代码中,通过跨平台编译生成各端产物。

前一种路径需要流程和语言可读性支持,后一种则是他在当时关注 Kotlin 跨平台能力的原因之一。具体共享哪些逻辑,仍要与现有架构和业务边界相适应。

他把两条路称作不同程度的一致性。弱一致保留 Android 与 iOS 各自实现,但通过相同的业务规则和交叉评审减少分歧;他提到 Kotlin 与 Swift 写法较接近,有助于让两端工程师读懂彼此的逻辑。强一致则考虑将适合共享的业务逻辑写成一份 Kotlin 跨平台代码,再编译成各端产物。前者考验评审和测试,后者还要判断哪些代码值得共享、跨端工具链如何接入,不能因为语言相同就跳过平台差异。

从单个 SDK 走向场景解决方案

技术成熟之后,收益往往来自多个能力的组合。用户运营可能需要识别浏览行为、判断触发条件、展示挽留内容并统计结果;性能优化也需要针对具体页面和用户路径安排资源、请求与渲染。

他认为仅靠发现某个孤立函数的低效,很难持续推动整个应用的显著进步。架构团队需要理解具体场景,把基础能力组合起来,最终用体验或业务指标检验效果。分享末尾还谈到当时受到关注的 AR、VR、数字形象与端侧智能,作为客户端工程师可探索的新交互方向;这些是 2022 年的探索判断。

曹大的成长路:兴趣、反馈与能力边界

从折腾手机到完成真实项目

曹立成最初接触 Android,来自大学时期对手机界面和系统的兴趣:刷机、调整主题、替换资源,让自己使用的设备变得不同。随后,他开始写简单应用。按钮能够点击、界面立即响应,这种可见的反馈带来了持续学习的动力。

他记得自己买到第一部 Android 手机后,嫌系统和应用界面不好看,先从论坛里学习刷机、替换主题、修改图片和文字资源。那时还谈不上完整应用开发,但为了重新打包一个自己想用的界面,他开始知道 APK 里大致有什么。后来做第一个小程序,只是按钮点下去弹出自己的名字,也让他感到“写的东西立刻有反馈”。主持人顺势指出,兴趣、当时移动应用的热潮和后来遇到的项目机会,三者一起推动了这个方向。

应用比赛让他尝试更多小作品,而一个与灾害搜救有关的研究项目,把兴趣推向了更复杂的问题:如何利用移动设备、自组织通信和数据传递辅助定位。项目延续到毕业设计,让他在实际需求中接触网络、通信和系统协作,也逐渐确定了 Android 开发方向。

项目设想的是地震后手机仍在被困者附近,而常规网络可能中断,搜救人员能否利用这些设备先建立联系并辅助初步定位。要回答它,他从单机界面走向设备连接、数据传递和搜索路线等问题。这个项目并不等于已经交付一套救援系统;在曹立成的经历里,它是一个持续到毕业设计的研究实践,让他第一次为了具体目标把多个技术环节接起来。

从“能跑就会了”到认识自己的边界

谈到从新人到专家的阶段变化,他并没有把过程描述成始终自信的上升线。早期带着项目经历进入工作,对自己的能力很有信心;在饿了么实习时,也敢于尝试把当时尚不成熟的 React Native 引入真实工程,为此学习前端技术。

后来进入美团基础架构团队,看到更复杂的工程与同事的实现,他开始意识到能写出演示、让代码运行,与真正理解问题之间还有距离。接下来的阶段是通过请教、阅读和项目逐步积累,知道自己能解决什么、行业里有哪些成熟方案,以及遇到新问题时该如何选择。

这个落差在他的讲述里很鲜明。刚毕业时,他拿着大学项目找工作,对自己的能力颇有信心;在饿了么实习时,还敢于接早期 React Native 的工程落地,自己补前端知识,向社区提交改进建议,也到别的团队分享经验。到了美团的基础架构团队,读同事的代码却常常是单词都懂、组合起来的设计意图看不明白。他没有把前一阶段说成毫无价值,而是意识到“做成一个项目”和“理解成熟系统为何这样组织”是两层能力。之后靠提问、比较与解决更难的真实问题,判断才逐渐稳下来。

这种成长最终表现为更客观的自我判断和更丰富的方案储备。经历过动态化技术快速发展的阶段,也让他能够把后来出现的方案放到更长的演进脉络中理解。

Kotlin 的价值来自生态与跨平台可能性

在当时的访谈中,他把 Kotlin 看作 Android 开发者值得持续投入的语言:平台与工具生态对它的支持、与 Java 的协作、语言表达能力,以及逐渐扩展的跨平台用途,共同推动了它的普及。

讨论从 Android 延伸到后端与原生跨平台代码。他关注的重点是,同一项语言能力可以连接不同领域,使开发者有机会复用已有积累。访谈中提及的生态状态属于当年的观察,不能直接替代今天对具体库版本与平台能力的核查。

把技术讲清楚,也让团队里的贡献被看见

分享技巧先从组织思路开始

曹立成认为,技术分享要让听众获得接近讲者的理解。自己做出了东西,并不代表已经能把它讲清楚。写文章是一种成本较低的训练:有意识地整理问题、设计理念、核心模块、落地过程与最终效果,避免把文章写成流水账。

演讲还需要另外练习。幻灯片应呈现经过抽象的模型,讲者再补足解释,页面之间要形成从问题到解决方案的连续叙述。紧张、音量和表达节奏,也需要在实际分享中改善。

严谨体现在内容之外的细节。他回忆团队技术文章审稿时对语句、错字与标点的要求:这些细节会影响读者对专业程度的判断。分享能力因此包括思考、组织材料、口头表达与反复校订。

个人技术品牌建立在可持续的优质内容上

谈到工作繁忙时怎样建立技术品牌,他先强调内容积累。文章是可以反复使用的基础材料,但需要把工作里的具体问题抽象成别人也能理解和借鉴的模型。相较单纯增加数量,他更倾向于投入时间写出值得细读的文章。

在有内容之后,再通过社群交流、投稿和技术会议扩大触达。读者持续从文章中获得启发,才会逐渐建立对作者的稳定认识。他也提到表达时的自信:在认真准备之后,应当相信自己产出的内容有价值,愿意让别人看到并参与讨论。

团队氛围需要传承,也需要具体激励

团队中的分享、沙龙、新人培训和经验传递,可以帮助知识流动。但技术氛围也需要让那些不容易被看见的工作获得正向反馈,比如维护稳定性、解决用户体验问题、提高代码质量,以及长期帮助同事。

他介绍了团队通过项目与奖励认可不同类型贡献的做法。主持人进一步把对内的技术氛围与对外的技术品牌联系起来:内部积累产生有价值的分享,外部认知又可能吸引志同道合的人加入,再反过来改善团队。这个过程需要长期持续。

招聘看基础,也看协作与持续学习

在他看来,技术基础、工程素养和学习能力是重要前提。架构和技术方案不断变化,开发者需要跟着问题继续学习。与此同时,一个人的产出存在边界,更大的目标需要拆解任务、协调协作并将结果重新组合。

因此,沟通与合作能力并非技术之外的附属项。能让多人共同完成复杂项目的人,也更有可能承担更大范围的责任。

成长方向可以不同,转型应尽量承接已有积累

访谈最后讨论客户端工程师的后续发展。他给出了几种不同方向:深入某个技术领域、解决困难问题;围绕架构演进和研发效率工作;贴近业务,运用技术组合改善增长、留存等目标。不同方向需要不同能力,也对应不同的岗位空间。

他特别指出,理解业务并能把技术转化为结果,同样需要复合能力。做业务并不天然意味着技术浅。对于想探索新方向的人,他更建议从已有经验延伸,比如以客户端能力连接新的交互和图形场景,减少完全抛弃过去积累的成本。这些建议应结合个人兴趣、能力与实际机会来判断。

完整录播

查看本期原视频与分段信息:我在 1688 做终端架构 →