一、前言
低代码平台(Low-Code Development Platform, LCDP)凭借可视化开发界面、拖拽式组件编排以及自动化代码生成等强大能力,正逐渐成为企业级应用开发的选择,极大地缩短了从开发到部署的成本与周期。然而,随着平台承载的企业核心业务不断增加,其稳定性挑战也愈发凸显。例如,组件依赖可能引发级联故障,多人协作时容易陷入版本困境,低码开发的黑盒特性也可能导致无感知问题。稳定性不仅关乎用户体验,更是平台持续发展、稳定运行和数据安全的核心要素。稳定性是一个覆盖全链路、多维度的系统工程,贯穿需求分析、架构设计、开发测试、部署发布及运维监控等完整生命周期。本文仅探讨有限的几点在低代码平台建设中遇到的关于稳定性建设方面的实践与思考。
二、问题分析
问题1:变更无对比,开发黑盒无感知
传统开发以文件为载体可通过 Git 进行版本管理,Git Diff 实时了解编辑的内容。然而,低代码开发处于一个黑盒状态,难以实时了解变更,当需求内容的功能点较多或开发周期较长时,可能会遗忘或不清楚已经编辑过的内容。
问题2:多人协作发生内容覆盖或冲突
低代码开发者在研发生命周期中难免会面临多人协作或并行开发的场景,如果不对变更内容进行版本控制,多人开发同一内容时就可能会出现开发内容被覆盖或需要手动根据 JSON Schema 合并他人开发内容的情况。
问题3:变更的影响范围无法直观了解
低代码开发是一个配置化的过程,这会导致各种组件、变量和方法等内容之间存在各种依赖关系,改动一个内容可能会影响多个不同的内容,目前,开发者缺乏明确感知内容变更影响范围的手段,这容易导致线上问题的出现。
问题4:本地暂存能力不足影响使用
低代码开发是一个持续的过程,并非短时间内可以完成。在此过程中,如果缺乏对编辑内容的本地暂存或线上暂存能力,将导致编辑内容丢失。例如,如果开发过程中浏览器或电脑被误关闭,或者编辑页面关闭且忘记保存,未保存的内容就会丢失,需要重新开发,浪费人力。
问题5: 发布缺乏多环境区分和审批
低代码开发尽管可以做到“开发即上线”,但这种模式与传统软件工程的最佳实践相违背,应该在测试等环境中进行验证再发布;其次,发布流程缺乏必要的质量管控环节,包括变更 Review 和发布审批流程,不然直接影响生产环境稳定性。
三、解决方案
3.1 低代码变更对比
在传统的代码开发中,研发人员可以通过 Git 的 diff 功能清晰地掌握代码的变更情况。然而,在低代码平台中,开发的产物通常以 Schema JSON 的形式呈现,这意味着变更并不是通过逐行比较文本代码来识别的,而是需要对 Schema JSON 文件进行差异分析。。因此,如何基于 JSON 内容进行变更对比展示,使之更加清晰和直观,成为了我们面临的一项挑战。 当前业界主要通过两种方式来进行低代码变更对比:
-
全量 JSON Schema 对比:低代码变更前后的 JSON Schema 直接在 Diff 编辑器中进行对比展示,这种方法简单直接,能够迅速展示 JSON 结构的变化,但也存在明显缺陷,例如直接全量对比展示可读性非常差,尤其对于大型项目来说 JSON Schema 行数是万级别的,甚至可能会引起性能问题,同时全量对比的内容涉及多层级嵌套,理解难度也高。
-
内容预览快照对比: Schema JSON 的最终呈现是页面,因此通过 Schema 直接渲染页面内容展示进行对比,这种方法对于简单展示页面较为实用,能够直观看出变更内容是否符合预期。但是,对于需要特定交互才能展示的内容,例如弹窗和抽屉等内容,无法从预览快照中看出前后变更的区别,缺乏变更完整性。
基于上述情况,我们的解决方案是 👉 模块化对比 👈,将内容解析为 9 大模块,组件树、方法、生命周期、数据源、变量、全局样式、全局常量、组件映射和物料版本,其中方法进一步划分到具体的方法函数进行对比展示,组件树进一步划分到具体的组件粒度来进行对比展示。模块化对比的方式保证了内容的完整性,同时也通过拆分增加了可读性。
组件树是整个低代码内容核心,以组件为粒度进行对比,其中以组件在大纲树中的路径作为组件名,组件所属属性作为对比值。每个组件都由唯一的 id 进行确定,然后逐一对比,当 id 在变更动后的组件树中不存在时则视为删除,当 id 仅在变动后的组件树中存在时则视为新增,当相同 id 的组件的属性在变动前后发生变化则视为更新。但是,低代码组件树有一种特殊的变更“移动”,如果根据常规对比方法逐一对比,会存在大量无意义的变动记录,增加了对比确认的成本。例如,存在 1 个容器组件A,内部有 100 个文本展示组件,我们将容器组件 A 整体拖动到另一个层级位置,此时,如果是逐一对比的方法,会产生 101 个删除记录和 101 个新增记录,但实际上我们只需要 1 条记录,容器组件 A 标记移动即可(默认子组件同步移动的),这样大大减少了我们核对变更时的成本。因此,我们需要一种能够识别移动的对比方法并且结合大纲树来进行对比展示。
社区 microdiff 和 deep-object-diff 等热门 diff 库都是对象属性 diff,不支持树形diff 无法对比出节点移动这一种变更。 受 Vue3 DOM Diff 算法的启发,提出了 👉 Tree Object Diff 算法 👈,支持对比增删改和移动,并且不仅支持找到同层级移动还能识别跨层级移动。算法和依赖库请阅读 👉 《前端 JS 如何高效对比两个“树形对象”?增删改,还有移!》。
3.2 多人协作编辑
在低代码平台的使用过程中,难免会遇到需要多人协作或同时变更同一份资源的场景,如果不处理这会遇到非常多的问题,以下是一些真实场景:
- 场景一:用户A和用户B同时开发同一份内容,但是是两个需求,用户A先上线,用户B后上线,此时用户B会覆盖用户A的变更内容,导致线上只有用户B编辑的内容;
- 场景二:用户A和用户B同时开发同一份内容,并且是同一个需求,用户B的内容需要依赖用户A的内容,此时用户B只能等待用户A完成开发,再在此基础上去开发;
从稳定性方面考虑,编辑内容互相覆盖的严重程度远大于无法并行开发内容。为了解决多人编辑覆盖的问题,当前根据实现成本和用户体验的不同,多人协作解决方案演进路线主要有 4 个阶段和解决方案,悲观锁、乐观锁、多分支开发和多分支开发结合实时协同编辑。
📌 悲观锁
- 原则:悲观锁基于“小心为上,防范为先”的策略,做最坏的打算,假设冲突随时可能发生,因此对资源进行锁定,直到操作完成,在锁定期间其他人都无法更改。
- 优点:完全避免了内容被覆盖的问题。
- 缺点:用户体验较差,不支持并行开发,锁定期间其他人都只能等待,限制了协作效率。
📌 乐观锁
- 原则:乐观锁的原则与悲观锁正好相反,其基于“大多数操作不会冲突”的假设,允许用户自由编辑,仅在提交时检查校验或实时监听是否有其他用户提交更新。
- 优点:提高了并发性能,支持更灵活的协作。
- 缺点:在高冲突环境下可能导致频繁的重试和编辑覆盖,本质上依旧无法实现真正的协作,当然也可以设计一种合并机制,可以将其他人的最新变更合并进来。
📌 多分支开发
- 方法:允许开发者在不同分支上独立工作,每个分支代表一个独立的开发流程或功能,当开发完成时合并入主分支。
- 优点:支持并行开发,避免了直接的内容覆盖。
- 挑战:需要设计合并和冲突解决机制,特别是对于非文件类型的数据(如Schema JSON)无法借助 git 这样的工具来实现合并和冲突解决,可以尝试将 json 数据转成文件保存再来使用 git 的版本控制能力。
推荐阅读:低代码多分支协同开发的建设与实践
📌 多分支开发结合实时协同编辑
- 方法:结合多分支开发和实时协同编辑的优势,允许多个开发者在同一个分支上实时协作。
- 优点:结合了多分支开发和实时协同编辑的优势,保持高协作效率,减少并行开发导致的冲突和覆盖问题。
- 挑战:实时编辑的冲突处理较为复杂,需要采用如 OT 和 CRDT 算法等解决方案,其中 Yjs 库是当前较为流行的 CRUT 协同框架。
3.3 依赖关系图谱
在低代码开发过程中,开发者通过可视化配置(如拖拽组件、绑定变量、设置事件等)快速构建应用。然而,这些配置之间存在复杂的依赖关系,例如:
- 变量与函数的交互:一个函数可能操作多个变量;
- 组件与变量的绑定:一个组件可能依赖多个变量作为数据源;
- 事件与状态的联动:组件触发的事件可能同时修改多个变量状态。
当前的主要痛点是:缺乏依赖关系的可视化与变更影响分析。开发者难以评估修改某个配置的影响范围,容易引发意外问题。为此,我们提出构建 👉 依赖关系图谱 👈 的解决方案——通过解析 JSON Schema 并可视化配置项间的关联关系,帮助开发者理解系统结构。参考 Retool 的实现方案,我们定义以下 4 类核心依赖关系:
- A depends on B(A依赖B)
- B updates A(B更新A)
- A controls B(A控制B)
- B controlled by A(B被A控制)
典型场景示例:
- 函数A使用变量B → A depends on B + B updates A
- 组件A绑定变量B → A depends on B + B updates A
- 组件A的事件修改变量B → A controls B + B controlled by A
依赖关系图谱主要应用在两个阶段,开发时和发布时。通过依赖关系图谱,开发者能够更加自信地变更内容,同时减少因变更带来的不确定性和潜在风险,也能够帮助定位问题源头进行排障。
- 开发时:在开发过程中可以根据依赖关系图谱观察每个内容所关联的影响范围;
- 发布时:在发布阶段根据变更内容给出变更影响范围图谱,及时提供风险预警;
📣 依赖关系图谱也是一个非常好的与大模型相结合的场景,通过大模型来分析 JSON Schema 的变更内容来给出变动影响范围和风险点。
3.4 本地编辑保存
在低代码开发中,编辑和保存内容是一个频繁的操作。每次将编辑内容保存至线上版本,都相当于在 Git 版本控制系统中执行了一次 commit+push 操作。这样的流程不仅保证了我们能够轻松追溯到任何一个历史版本,还能比较不同版本之间的差异,从而快速定位问题并进行修复。版本历史中的提交信息为开发者提供了宝贵的上下文,帮助他们理解代码的演变过程和追踪问题来源。然而,虽然定期保存版本是一个好习惯,但过多的版本记录可能会造成存储空间的浪费和维护上的复杂性。因此,我们引入了编辑暂存的概念,以优化这一流程。编辑暂存允许我们在最终确定之前,临时保存编辑内容,这既保留了版本控制的优势,又避免了不必要的冗余。编辑暂存主要有两种实现方式:
- 本地存储:编辑内容保存在浏览器。该实现方式实现成本低,且无需后端配合,但是依赖本地存储,更换电脑或清除缓存都会失效。
- 后端存储:暂存内容保存在服务器。该实现方式远端存储数据持久性好,不受本地的限制,但是实现成本高,需要后端配合。
在本地存储方案实践中,通过 localForage 库可以基于 IndexedDB 实现了一个高效且可靠的本地存储解决方案。IndexedDB 不仅提供了比传统的 localStorage 更大的存储空间,还支持更复杂的数据结构,这对于处理大量数据的 Schema JSON 文件至关重要。此外,IndexedDB 的异步操作特性意味着它不会阻塞主线程,从而保证了平台的流畅运行。为了确保本地缓存的一致性和避免顺序覆盖问题,还可以实施了版本号校验等策略。这些措施确保了即使在多个会话或多个设备之间,用户的工作也能被正确地保存和恢复。
3.5 发布流程标准化
为有效平衡低代码平台的敏捷开发需求与生产环境稳定性要求,需要建立融合传统软件工程最佳实践的标准化发布流程。通过"环境分层+发布审核+灰度发布"的三层防护机制,实现发布流程的标准化。
- 环境分层:建立开发→测试→预发→生产的全链路环境隔离机制,确保变更经过充分验证再上线。在实践层面,如果请求接口也涉及多环境区分,则可以借助环境变量的配置来动态生成接口请求地址,而不使用硬编码。
- 发布审核:在发布过程中,系统会首先通过自动化规则校验变更内容,包括强制审核触发条件、是否通过测试环境验证等,只有合规的变更才能进入后续流程;此外还会借助低代码变更 DIFF 功能来可视化核对发布变更内容。
- 灰度发布:为确保变更发布的平稳性和可控性,采用渐进式灰度发布策略,通过白名单灰度、百分比流量灰度、业务泳道灰度等多元化方式,逐步验证变更稳定性。针对高风险变更,严格执行分层灰度机制,确保问题早发现、影响最小化。
四、总结
整体而言,本文从多个角度出发,详细介绍了低代码平台在稳定性建设方面的一些实践和思考,包括低代码变更对比、多人协作开发、依赖关系图谱、本地编辑保存、发布流程标准化等内容,旨在通过技术手段提升平台的稳定性和用户体验,确保平台能够高效、稳定地服务于用户,同时也实现了研发效率与系统可靠性的双重提升。
One More Thing
有效的监控和日志分析对于及时发现和解决问题至关重要。低代码平台需要具备强大的监控工具,以实时跟踪应用性能和用户活动。我们可以从平台方和使用方两个视角出发,针对组件使用情况不清晰、性能和报错数据缺失、页面渲染情况不明确等问题, 在平台和渲染 SDK 中主动上报埋点数据,建立相关的看板以监控平台的现状和情况。