Java遇上JavaScript:Picnic公司現(xiàn)代頁(yè)面渲染升級(jí)之路
来自@masoud.alali.94 和 @joao.nogueira01,撰写
在之前的博客文章中,我们探讨了Picnic的Page平台如何革新了我们构建移动购物体验的方式。作为一家仅通过移动应用提供服务的在线杂货零售商,我们的成功取决于为客户提供出色的移动购物应用体验。在平台演进的过程中,我们确立了两个关键目标:
- 通过即时推送更改而无需用户更新应用来缩短上市时间。
- 让如设计师和产品负责人这样的非技术人员能够无需深入了解技术即可修改应用体验。
页面平台(Page Platform) 是我们的解决方案:一个强大的服务器驱动的 UI 框架,使我们能够即时创建和修改应用屏幕,而无需更新应用。它以统一的方式定义了视觉布局和底层数据需求,从而实现了快速开发和无缝更新。这个框架改变了我们团队的合作方式,打破了工程师、业务方和分析师之间的壁垒。然而,和所有的技术解决方案一样,我们持续寻找改进的方法。在本文中,我们将分享我们从 Handlebars 迁移到 JavaScript 的动态页面模板语言的迁移过程。在专注于需要更改的内容之前,我们首先要反思现有方法如何适应我们的使用案例,哪些是我们喜欢并打算保留的特性,以及我们想要改进的地方。
平台,对现状的一点思考页面平台的设计注重可配置性,这意味着我们希望在后端可以灵活配置页面定义。这种架构更贴近我们的目标和需求,它为我们开启了新的可能性。
- 我们的后端系统支持可配置的页面。
- 几乎实时地添加新功能和更改,而无需更新应用程序版本。
- 围绕该平台创建工具,让分析师和非技术人员可以无需深入了解技术细节即可配置页面。
要描述这种配置,最初我们选择了Handlebars作为我们的模板语言。我们知道Handlebars是一个灵活且简单的模板语言,因此我们相信它可以帮助我们构建并扩展我们的Page Platform。然而,我们也发现它有一些缺点。让我们来具体看看。
我们喜欢的 Handlebars 的地方- Handlebars既容易扩展也容易学习。
- 它很容易集成和设置起来。
- 它的语法非常僵硬。
- 引入简单逻辑时,需要通过Java实现该逻辑,并在Handlebars中以助手的形式暴露出来,这意味着即使是引入一小段逻辑,也需要一个完整的后端发布周期。
- 作为模板语言,它不适合处理复杂的逻辑需求。
- 缺乏IDE支持,导致开发体验令人沮丧,并且容易出错。
- 缺乏支持的逻辑也意味着输出验证有限,导致输出的JSON合同经常被破坏。
因此,我们得出结论,尽管这种由服务器驱动的UI方法对我们来说确实是一个明显的提升,但选择Handlebars(Handlebars)作为我们的模板语言并不是最理想的。
我们需要一个解决方案,能够与我们的后台系统无缝地集成,支持Calcite查询基础设施和其他Java逻辑,同时也能支持更复杂的用户界面逻辑而不依赖Java辅助工具。此外,开发者体验也是一个重要考量——我们希望这个系统能够使编写安全且易于维护的代码变得简单,并且支持静态代码分析、本地调试,并利用广泛可用的资源进行入职指导。
由于我们的前端已经使用了 React Native,将 JavaScript 引入服务器端显然是简化的一个自然步骤。结合 TypeScript 来提供类型安全性,这种方法成为了最佳的选择。这将赋予我们灵活性,强大的工具链支持,并且在整个 UI 层提供熟悉的 JavaScript 开发体验。为了在我们的 Java 后端实现这一点,我们需要合适的运行时环境。
探索实现的选项选择JavaScript作为我们的语言后,接下来的挑战是,我们主要探索了两种方式来在后端运行JavaScript。
独立的 JavaScript 服务在服务器上最简单地执行JavaScript的方法:
- 优点:易于搭建,与传统的JavaScript后端架构一致。
- 缺点:增加了操作的复杂性——我们需要学会如何在生产环境中规模化运行JavaScript服务。此外,它与Java之间的直接互操作性不佳,需要更多的服务和接口,这增加了发布过程中的障碍。另外,与我们的Java后端服务通信会增加额外的网络延迟。
一种更集成的方法,能够在JVM中运行JavaScript。
- 优点:与Java的无缝集成,无需管理额外的服务;
- 缺点:我们对这种方法不太熟悉,不确定如何有效地应用它;
为了利用JavaScript的无缝集成和降低运营成本的好处,我们决定在我们的Java应用程序中运行JavaScript代码。
使用GraalVMGraalVM作为一个多语言虚拟机,允许我们在同一个Java运行时环境内运行多种语言,包括但不限于JavaScript。通过采用GraalVM,我们可以在后端Java应用程序中无缝地执行JavaScript代码,从而使我们能够在JVM中评估用JavaScript编写的页面模板。
一个关键要求是确保Java和JavaScript之间的安全互操作。直接共享Java对象与JavaScript可能会无意中暴露我们不希望公开的内部方法和属性,虽然我们希望公开某个Java对象的字段,但这并不适用于像toString
这样的通用方法。我们没有允许Java类和方法的无限制访问,而是基于承诺驱动结构的自定义API (支持与异步Java方法的互操作性),每个页面都表示为一个异步函数,我们从Java订阅该承诺,只为每个页面的渲染提供所需的特定参数。为了使JavaScript能够调用某些异步Java方法(从Calcite获取数据是最明显的异步应用场景),我们扩展了该基于承诺的API,让它允许JavaScript以受控的方式异步回调Java。通过控制这种Java到JavaScript的交互,我们的设置既高效又可扩展,同时在Java后端提供的稳定性和性能中享受JavaScript的灵活性带来的好处。
我们的用例需要精心协调以确保最佳性能。每个GraalVM上下文——执行JavaScript所必需的一个必要组件——一次只能运行一个脚本。如果对于每个请求都创建一个新的实例,那么这种简单粗暴的方法将无法利用GraalVM的内部优化功能,从而严重影响执行效率。
由于创建新上下文在计算上成本较高且由于缺乏运行时优化,初始执行速度缓慢,我们实现了一个 对象池(ObjectPool) 来高效管理和重复使用这些上下文。这种池化机制使我们能够最小化频繁实例化上下文所带来的开销,而是通过回收它们来更高效地处理传入的请求。
页面渲染过程简介在深入了解我们工具之前,先退一步来看一下我们的页面平台在高层次上是如何运作的,这一点很重要。页面渲染的流程遵循以下典型流程:
- 应用程序通过其ID及相关参数请求加载一个页面。
- 该请求被发送到我们的Java后端服务器。
- 后端检索并执行请求页面对应的JavaScript代码。
- 在执行过程中,JavaScript代码根据需要与Java组件进行交互——例如,使用Calcite(我们的数据检索API)获取数据,或将来可能触发基于副作用的事件。
- 一旦获取了所有必要数据,服务器上还可能执行附加的用JavaScript表达的业务逻辑。
- 最终输出是一个符合预定义规范的JSON数据结构,该规范描述了页面应如何渲染。通过Java返回此响应给客户端,完成请求/响应循环。
基于这个基础,我们来看看如何连接JavaScript和Java之间,让这个过程变得无缝。
连接 JavaScript 和 Java为了用JavaScript定义我们的页面,我们需要在JavaScript和Java之间建立一个明确的约定。每个页面的执行必须产生一个明确的输出,这样Java才能处理,确保生成正确的JSON响应数据,这样应用就能准确地渲染页面了。
为了做到这一点,我们设计了如下的API:
- JavaScript 导出一个页面 ID 对异步函数的映射。每个函数执行后会返回一个符合预定义页面 API 的 JavaScript 对象。
- Java 提供了一个互操作 API,使 JavaScript 能在需要时调用异步 Java 方法。
这种配置确保了JavaScript和Java之间的无缝通信,使得数据检索、执行业务逻辑和页面渲染更加顺畅。
我们在系统中执行的JavaScript代码在Git仓库中进行版本控制,确保每次更改都被一个唯一的Git提交记录下来。对于每个提交,都会生成一个新的JavaScript包文件。该包文件设计为遵循特定的API,例如一个简单的主页
,仅包含标题区域:
const Pages = {
'home-page': async () => {
return {
title: "主页"
}
}
}
export default Pages;
我们也需要建立一个清晰的API,让开发人员可以直接在JavaScript框架中请求Calcite查询的数据(如我们在该系列的先前博客文章中解释的那样)。这种交互必须与我们承诺驱动的基础设施保持一致。为此,对于每个渲染出来的页面,将 query 方法绑定到GraalVM上下文中,该方法委托给Calcite查询解析处理,从而在JavaScript和数据查询层之间建立一座桥梁。
这种方法能够实现无缝的数据检索,如下所示的简单API结构:
await query('SELECT * FROM 表名', {'参数键': 'parameter_value'})
这个定义明确的 API 不仅让 JavaScript 和 Java 层在生产环境中实现无缝交互,还促进了流畅的本地开发流程。通过使用本地的 Node.js 服务器,JavaScript 开发人员和页面作者可以显著缩短他们的反馈循环。在这种设置中,查询和调用被导向内部的 REST 端点,提供页面渲染所需的必要数据,而无需每次迭代都更新部署环境中的配置——所有这些都不需要在每次迭代时在部署环境中配置新版本。我们将在本系列博客的后续文章中深入探讨这一点。
JavaScript 动态模板从 Handlebars 转向使用 JavaScript 进行模板处理已经是一个巨大的升级,这使我们能够利用编程语言的全部力量来构建服务器端页面。虽然仅使用 JavaScript 就已提供了很大的灵活性,但我们从经验中得知,类型化的语言对于确保系统稳定运行至关重要。由于我们的 React Native 应用程序已经是用 TypeScript 构建的,因此将这种类型安全的方式扩展到我们的页面平台显得顺理成章。TypeScript 还带来了许多额外的好处,比如自动补全、编译时验证以及通过格式化和代码检查工具强制执行一致的代码风格。基于以上考虑,我们为新的系统制定了一套关键要求。
我们新框架页面的关键需求- TypeScript 兼容性: 提供完整的类型检查、自动完成功能以及构建时验证。
- 语法灵活性: 简洁、直观的语法,易于记忆和维护。
- 可测试性和可维护性: 易于测试和维护的代码。
- 后端集成: 与我们的后端和数据源实现无缝集成。
- 支持复杂逻辑: 支持模板中的可复用和复杂逻辑。
- 数据查询导入: 可以直接将 SQL 查询导入页面,以实现轻松集成。
考虑到这些要求,我们开始探索不同的设置选项。以下是我们考虑的不同方法的简要概述。
使用纯 JavaScript 对象我们的第一个方法是在不同的函数中使用类型安全的JavaScript对象(TypeScript对象),并将它们层层嵌套,从而定义整个页面结构。
export const HomePage = (): IPage => {
return {
id: '首页页面',
body: {
type: '块',
...
},
...
}
}
这种方法既安全又简单。然而,随着对象越来越大,它们越来越难以维护。此外,开发人员整天和普通对象打交道并不太愉快。
将函数作为组件来使用为了改进之前的办法,我们将每个构建模块都包装成函数,使用函数代替了原来的普通对象。
export const HomePage = (): IPage => {
return Page({
id: '主页',
body: Block({
// Omitted code here
}),
// Additional properties omitted
})
}
这个解决方案有所改进,提供了更多的可重用组件,并且保持了类型安全。随着我们组件复杂性的增加,它们的定义变得更大,更难管理。随着时间的推移,我们发现组件变得越来越复杂,这使得它们的定义越来越难以管理和维护。
用 JSX受到 React 的 JSX 的启发,我们开始探索将 JSX 整合进我们的框架。使用 JSX,我们不仅可以创建可重用的组件,还可以提供一个面向开发者的友好环境,该环境具有强大的 IDE 支持,包括由 AI 提供的建议。此外,我们还可以开发一个自定义引擎来并行渲染组件,并完全掌控框架的执行流程。
为了做到这一点,我们构建了核心组件作为基础模块,并利用了TypeScript的JSX特性来开发一个自定义引擎,该引擎能够处理并解析嵌套组件。
export const HomePage = () => {
return <Page id={'home'}>
<Block>
...
</Block>
</Page>;
};
这种方法不仅被证明是类型安全的,还直观且使用起来很愉快。熟悉的语法使我们的团队更容易接受和使用,而我们自定义的引擎则让我们在组件解析方面有更多的控制,使我们能够根据需要优化性能。
在选择了TypeScript和JSX作为核心配置之后,下一步是使其与我们的生产环境GraalVM兼容。为了实现这一点,我们需要将代码转译为具有单个主入口点的纯JavaScript。遵循最佳实践的方式,我们使用打包工具来处理这一过程。
打包打包是指将多个组件或产品一起打包或捆绑销售或分发的过程。
为了优化我们的方法,我们引入了打包过程来生成生产就绪的打包,使其能与我们的后端无缝集成。打包过程确保了兼容性,使其能无缝集成到生产环境。我们开发了自定义插件以实现更为高级的功能,例如,通过导入SQL查询文件,将SQL文件转换成JavaScript函数,并支持动态参数的功能。
此外,我们为每个包的输出进行版本控制,允许我们按需加载特定版本的代码。拥有打包器还让我们能灵活地添加新依赖项,并在不影响后端系统的情况下将它们打包。这种方法让依赖项管理更加高效。
测试一下为了测试,我们使用了一个支持并行执行的基础设施,它能无缝集成到我们的打包设置中。这使我们能够创建与生产环境相似的测试环境。除了标准测试(如单元测试、功能测试、性能测试和快照测试)之外,我们还开发了针对我们页面的自定义测试,包括端到端(E2E)测试和视觉回归测试。这些测试确保了不同环境下的行为一致性,并帮助我们尽早发现问题。
开发用服务器为了进一步优化开发流程,我们设置了一个本地开发服务器,并集成了网络代理来捕获并重定向应用程序请求至本地服务器。此外,我们重用了打包和测试环境中的自定义插件,以确保开发服务器的行为模拟打包器的行为。这确保了开发、测试和生产环境中的体验一致性。
我们的设置允许我们无缝地在真实数据源和模拟的数据源之间切换,提供了灵活、准确且即时的测试能力。当不使用模拟数据之时,我们实现了本地版本的数据提供者来有效地模拟生产环境中的情况,并与REST端点通信。而在生产环境中,JavaScript和Java之间的API桥接用于直接连接到我们的后端。
我们本地环境和生产环境之间的一个关键区别在于,我们生产环境中使用的GraalVM提供了JavaScript运行时,但缺少完整的Node.js功能支持。这要求我们考虑到一些限制,并确保我们的设置在这两个环境中都能兼容。
总之,这个全面的配置——包括 TypeScript、JSX、打包工具、测试环境和本地开发服务器——使我们能够轻松地开发、测试和部署服务器端页面。借助实时反馈、兼容生产环境和适应复杂逻辑的能力,我们的方法简化了开发过程,缩短了上线时间,从而形成一个高度可维护且高效的系统。
毕竟,新版本是怎么到达生产环境的?如前所述,我们的 JavaScript 框架使用 Git 进行版本控制,每次更改都会生成一个新的 Git 提交。为了确保我们的运行环境始终与最新的更改保持同步,我们设定了一个 GitHub webhook,每当有更新时,它都会通知后端部署。收到通知后,后端会从仓库获取最新的代码变更,生成一个更新后的 JavaScript 包,并用最新提交的代码替换已部署的版本。
这个过程确保最新的修订内容能够无缝地融入环境中。整个流程如下所示:
我们的系统到底有多强的韧性呢?如果GitHub出了问题,会怎样?我们是否确保我们的系统能应对第三方故障?这时,前面提到的弹性文件系统(EFS)就发挥作用了。
但是,弹性文件系统到底是什么呢?正如亚马逊所描述的,
“Amazon 弹性文件系统(Amazon EFS)提供无服务器、完全弹性扩展的文件存储解决方案,让您轻松共享文件数据,无需担心存储容量和性能的配置与管理。” ——引自 https://docs.aws.amazon.com/efs/latest/ug/whatisefs.html
所以这有什么帮助呢?首先,我们的后端应用程序部署在多个副本上。我们不希望每个副本都依赖于与 GitHub 的直接连接来获取自己的 Git 仓库版本。为了解决这个问题,我们将更新的仓库和生成的包文件都存储在 Elastic 文件系统中,所有 Picnic Store Pods 都可以访问这些资源。
如果 GitHub 出现故障,我们不仅拥有一份最新的 Git 仓库副本(截至最后一次成功更新时),而且还存储了生成包在 EFS 中。这不仅确保我们不需要每次都重新生成这些包,还消除了对 GitHub 的直接依赖,从而提高了系统的整体韧性,使我们的系统更加可靠。
要迁移的旧模板在我们介绍完新的页面框架之后,我们面临了一个重大挑战:分别用两种不同引擎编写的模板——Handlebars(HBS)和我们新的基于JavaScript的框架。维护这两种模板不仅繁琐,而且效率低下。在两个系统之间共享逻辑变得困难,测试也成了瓶颈。
当时我们大约有1.200个HBS模板,从大型复杂的模板到小型可重用组件都有。手动迁移所有模板不太现实——这将需要团队花费大量的时间和资源。此外,每个模板迁移后的测试也相当棘手,因此,自动化变得必不可少。
将 Handlebars 模板迁移至 TypeScript 项目中我们尝试了多种策略来成功地迁移这些模板。以下是我们解决问题的方法:
探索人工智能的妙用
最初,我们尝试了AI工具。我们使用了一些简单的提示,例如:
“我有一个具有这些属性的HBS模板。将其迁移到JS,并考虑到[特定的逻辑或结构]。”
虽然AI提供了一些有趣的起点,但其结果与我们所需相差甚远。我们意识到,虽然AI可以在优化或验证模板方面提供一些帮助,但它无法单独可靠地完成迁移任务。
在 AST 层解析 HBS 模板
我们的下一个想法是解析HBS文件格式,并将其转换为JavaScript代码。理论上来说,这种方法似乎很有前景,但我们遇到了不少难题。
- 结构上的不同:HBS AST 和 JavaScript AST 的结构从根本上不同,使得 AST 的转换更加复杂。
- 模板特定语法:HBS 依赖于辅助功能和自己的语法,使得 AST 的转换更加复杂。
- 现有的解决方法:我们使用了多种技巧来从 HBS 模板生成 JSON 结果,这使得解析和理解 AST 变得更加困难。
鉴于这些复杂性,我们决定放弃基于AST的方法,转而使用一个更简单的方案。
Hacking HBS编译器
我们找到了一种非传统的但有效的解决方案方法:直接修改HBS编译器。Handlebars设计允许自定义辅助函数和逻辑的注入。利用这一特性,我们设置了编译器使其输出JavaScript代码而不是渲染模板。
例如以下情况:
- {{each …}} 被转换成了 JavaScript 的 .map() 方法。
- 我们在渲染过程中没有解析变量值,而是在输出中直接使用了实际的变量名。
这种方法虽然有效,但仍需要额外的步骤来符合我们框架的要求。这种方法让我们能够生成 HBS 模板的 JavaScript 表达。
- 语法调整:一些模板因为之前的 hacks 或者转换本身导致存在语法问题。
- JSX 转换:我们新的框架使用了 JSX 语法,所以需要一个额外的转换步骤。
TypeScript集成:我们的框架已经支持TypeScript,我们倾向于直接转换到TypeScript以获得完整的类型安全性,而不是通过VanillaJS作为中间步骤。
利用AI来改进数据迁移过程,以提升迁移效果
当我们有了基本的迁移管道后,我们重新审视了如何利用AI来改进迁移的模板。我们分别尝试了GPT和Gemini,以找到对我们用例更准确的结果。最后,我们发现使用Gemini确实得到了更好的结果。AI帮了我们大忙,具体表现在:
- 修复语法问题:AI审查了迁移的JavaScript模板,并将其与原HBS模板进行对比,以识别并解决语法错误。
- 确保语义准确性:它还确保迁移的模板保持了原有的逻辑和结构。
AI在定义TypeScript类型时也更加有用。通过分析参数的使用并推断类型,它为我们页面props生成了TypeScript接口定义,从而使模板的第三个版本更加健壮。
创建一个用于手动审核的仪表盘
每个模板都有三个版本——原始HBS、经过AI优化的JavaScript和增强JavaScript的TypeScript,最终我们需要一种高效的方式来确定最佳版本。为此,我们开发了一个基于网络的实用工具界面。此工具如下所示:
- 列出了所有模板及其相关的依赖项。
- 允许用户并排比较原始模板与迁移后的模板。
- 使开发人员能够选择质量最佳的版本作为进一步手动改进的基础。
通过使用依赖图,我们优先迁移叶子节点(即网站或系统中的最底层页面),然后逐级向上迁移直至根页面。这样渐进的方式确保了一致性,同时简化了测试。
迁移测试:迁移的主要目标是确保新的JavaScript(JS)模板能够完全复制原始Handlebars(HBS)模板的逻辑和输出。为了验证这一点,我们采用了一种全面的测试方法,包括生成测试数据、自动化比较以及进行端到端的评估。以下是我们在每个方面采取的具体措施:
创建测试数据
为了有效地测试,我们需要为每个模板准备真实的输入道具集和预期的输出结果。由于我们的模板复杂程度不同,我们采用了多种数据来源和策略,来生成所需的测试资料。
- 使用 JSON 模式 — 一些 HBS 模板预先定义了其接受属性值的 JSON 模式。这些模式定义了输入数据的结构和约束条件。我们利用 AI 工具根据这些模式生成模拟数据。例如,模式指定名称为字符串,年龄为数字,我们就可以创建不同的模拟输入(如 { name: "Alice", age: 30 } , { name: "Bob", age: 25 })。这些模拟数据被用于渲染 HBS 和 JS 模板,从而可以比较它们的输出。
- 从后端日志中提取 — 对于没有模式的模板,我们查看了后端渲染引擎的日志。这些日志包含了执行期间传递给模板的真实属性及其渲染结果。我们处理这些日志以提取结构化的测试数据,确保属性和预期结果既现实又反映了实际使用情况。
- 手动编译测试数据 — 在既没有模式也没有日志的情况下,我们手动编译测试数据。开发人员通过审查模板逻辑推断所需输入,并编写脚本来生成多种属性组合。这些脚本对多种属性组合进行渲染,并保存输出用于对比。
自动化测试
有了生成的测试数据,我们利用一些脚本来自动为模板生成JavaScript测试。这些测试是在我们页面的测试环境中进行的。具体步骤如下:
- 基线输出生成 — 我们使用HBS渲染了每个模板,并将模拟输入(预期输出)保存为基线输出。
- 迁移模板测试 — 我们把相同的输入传递给迁移后的JS模板,并将HBS和JS模板的输出进行比较。
- 识别差异 — 如果输出不同,测试将被标记为需要人工检查的项目。这些被标记的测试项目将被人工检查,以确定它们是否代表实际问题或可接受的差异。例如,输出可能存在一些细微的差异,如间距或命名约定,这些差异可能被接受,但是逻辑不匹配则需要修复。
全流程测试(E2E)
为了验证模板在整个页面上的表现,我们实现了一个端到端的测试。这一过程确保迁移后的模板能够与周围的系统无缝集成。
- 全页面渲染
我们使用了原始的HBS模板和迁移后的JS模板来渲染整个页面。
渲染后的页面进行了对比,以识别任何视觉或结构上的差异。 - 标准化差异
迁移过程中的一些更新(例如从命名颜色切换到十六进制代码)使得某些差异不可避免。
为了解决这个问题,我们开发了转换器,以统一某些属性的输出,例如标准化颜色格式、日期/时间格式和JSON属性名的大小写。 - 内部测试和部署
一旦端到端测试通过,我们将迁移后的模板部署到了开发环境中。
然后由内部团队进行进一步测试,之后再将其部署到生产环境中。
最终发布过程
为了最终确认并把迁移模板发布出去,我们遵循了一套结构化的流程:
- 运行自动化测试
执行测试用例并审查差异点。 - 进行端到端验证
渲染并对比整个页面以检查一致性。 - 部署到开发环境
在开发环境中启用新模板,并进行内部测试。 - 监控并逐步发布
监控内部反馈,在正式发布前根据内部反馈进行必要的调整。
迁移超过1,200个模板绝非易事,这是一项巨大的挑战,但通过结合自动化的工具、AI辅助以及仔细的手动检查,我们成功转换到一个统一的、基于TypeScript的框架。
自动化帮助我们减少了人为失误,并为我们提供了初步迁移的文件,而这些都是我们无法在合理的时间内通过手动迁移每个HBS模板来完成的。
这次迁移不仅简化了我们的开发流程,而且为未来更方便维护、更佳的性能和一致的开发者体验铺平了道路。
一个旅程的终点,一个新时代的起点从 Handlebars 迁移到我们自定义的 TypeScript 框架不仅仅是一个技术需求,更是一次团队的转型之旅。这次迁移不仅简化了复杂性,还统一了模板逻辑,并让我们可以利用诸如 TypeScript 的强类型和 JSX 的表达语法等现代开发实践。通过结合自动化工具、AI 改进和严格的手动监督,我们确保了平滑过渡,保留了功能的同时提高了可维护性。我们创新性地使用了 Handlebars 编译器、GraalVM 集成和结构化的测试流程,有效地连接了旧系统和新系统,使得两者能够无缝衔接。
这次行动不仅使我们的代码库现代化,还为我们在大型系统重构时应如何行事树立了先例——优先考虑自动化、协作和严格的测试。这证明了团队的创造力和适应性,展示了团队如何通过挑战激发创新解决方案的能力。在继续使用这个新框架开发的过程中,我们对其可扩展性、性能以及支持我们开发者和用户需求变化的能力充满信心。
本文是我们关于杂货购物应用程序架构系列博客中的一个帖子。还想了解更多吗?再看一篇我们其他的文章。
- 在更快功能,更快乐的顾客:介绍改变我们杂货应用的平台 中,我们分享了架构背后的原因,并进行了总体介绍。
- 在Picnic的页面平台中定义、提取和转换 提供了我们迈向一个能够查询数据并将其转换为客户端呈现指令的平台的高层次概述。
- 在利用Apache Calcite实现快速业务用例迭代 中,我们详细讲述了我们为什么选择Apache Calcite作为我们的查询引擎的技术,并说明了我们如何使用这项技术。
- 从移动应用角度看Picnic的页面平台:通过服务器驱动的UI实现快速更新 描述了我们在移动应用中实现服务器驱动的UI的方法。
- 解决配置问题:为非开发人员打造乐高式的灵活性 详细介绍了我们如何通过我们的页面平台赋予非开发人员能力。
共同學(xué)習(xí),寫下你的評(píng)論
評(píng)論加載中...
作者其他優(yōu)質(zhì)文章