近年来,“低代码”这一概念在资本市场上愈发火热,各种低代码平台项目也纷纷上马。
然而,此类平台的设计者与维护者们,或早或晚,终将会面对“低代码”与生俱来的一组底层矛盾,而是否能合理地处理这组矛盾,最终决定了此平台的发展前景。
背景
我们团队正在维护一套基于DSL(领域特定语言)的表单管理平台。该平台由客户方团队自建,其中包含了前端UI、数据管理、表单规则管理等模块。设计目标是:让业务人员以书写DSL的方式定义表单模板,此后可以直接基于模板生成UI并管理表单数据,从而达到低代码甚至零代码创建表单的效果。
该平台自建立以来,已经支持了数百个大型表单的成功上线,支持了公司业务的快速成长。
然而随着业务的持续发展,该平台的疲态也日益凸显:
- 难以支持复杂的表单需求(可用的表单组件、数据结构、DSL语法不足,且新增成本过高)
- 平台代码极度难以维护
- 大型表单的性能表现糟糕
不可能三角
以上案例,生动展现了一款低代码平台的典型生命周期:
- 前期:平台能力弱,但业务也简单。平台开始起步。
- 中期:平台能力趋于成熟,业务逐渐变得复杂但仍在平台能力范围之内。平台支撑着业务快速发展。
- 后期:平台触达能力上限,部分业务需求开始超出平台能力。
而此类平台之所以会有如此表现,根本症结在于其发展受到了“低代码”天然自带的一组矛盾的掣肘,该矛盾可以用不可能三角的形式加以描述:
如图,该三角的三个顶点分别代表了低代码平台的三个核心设计目标:
- Easy to Use - 易于使用
- Powerful - (功能)强大
- Low Complexity - 低(系统)复杂度
而这三个目标形成了如此一个三角形的结构,意味着在同一低代码平台上,他们永远不可能同时被满足:
a.若一平台在易于使用的同时功能强大,则必然拥有较高的系统复杂度。
其中,系统复杂度与维护成本成正比,与系统性能成反比。也即,较高的系统复杂度最终会导致平台拥有较高的维护成本以及较低的系统性能。
例子:人工智能编程。用户只需使用自然语言描述需求(易于使用),系统就能生成代码(功能强大),但该技术至今仍未完全成熟(高系统复杂度)。
b.若一平台易于使用的同时保持低系统复杂度,则必然功能受限
例子:Scratch少儿编程。使用图形化界面编程(易于使用),指令只需经过简单翻译即可执行(低系统复杂度),但功能只能满足教学目的(功能受限)。
c.若一平台功能强大的同时保持低系统复杂度,则必然拥有较高的使用成本
例子:通用编程语言。图灵完备理论上可以实现任何功能(功能强大),编译或解释为机器指令执行(低系统复杂度),但存在较高的专业门槛(高使用成本)。
出路
正所谓:
软件工程就是trade-off的艺术
对于低代码平台上述的三个目标来说,“我全都要”的结果只能是“全都得不到”,最终势必会陷入到按下葫芦浮起瓢的窘境当中。所以,对于任何一款低代码平台的设计者来说,做出合理的trade-off都将是所有工作中的重中之重。
那么如何trade-off来破解“不可能三角”呢?需要基于以下2个事实:
事实1:易用性与性能/可维护性不可放弃
通常,一款低代码产品的宣传会向我们许诺至少以下两个承诺:
- 低代码平台一定会比传统开发方式更方便/更快速/更易上手
- 低代码平台的最终产物一定是可运行的商业应用
对应到我们的三个核心设计目标上去,很容易看出:
- 第1条直接要求低代码平台必须是易于使用的,尤其是相对于传统方式要有显著优势,这是此类平台的核心竞争力。否则面对同样需求,客户为何不选择更成熟可靠的传统方案,而要使用低代码平台呢?
- 第2条则要求低代码平台以及其产物必须是生产可用的。这要求平台的产物要具有可接受的性能与可维护性,从而要求平台的复杂度不宜过高。而低性能或不可维护的产品则是断然无法应用在生产中的。
与之相对的,虽然低代码平台也会将“功能强大”作为其一大卖点,但又往往会特意强调其功能具有特定的应用范围。常见的有针对企业工作流、报表、ERP等场景设计的低代码平台。
综上,三个设计目标可以被简短的总结为:
- 易于使用是魂,是平台的意义所在。
- 低复杂度是骨,是平台在生产中可用的基石。
- 功能强大是肉,是平台价值的组成部分。
事实2:单一方案至多只能满足80%场景
我们可以将一款低代码平台所面向的问题域,划分为封闭问题域与开放问题域两类,它们之间的区别在于问题空间是否存在确定的边界。
- 对于那些专注于解决封闭问题域问题的低代码方案,经过精心设计,是有机会做到使用一个通用方案覆盖100%域内场景的。此类方案中的佼佼者有SQL之于数据查询领域,可以实现几乎所有查询需求。
- 然而对于一个开放问题域来说,由于不可能穷尽域内所有可能的问题场景,则可以说完全不可能存在有某个单一的“终极方案”,能够凭一己之力解决域内所有问题。
例如说对于一个To C的低代码开发平台,在UI交互方面,它的问题域就是开放的。
因为终端用户的交互需求无法穷尽:同样是展示数据,既可以用文本、表格,亦可以用图表、3D模型,甚至可以用AR、VR……
也因此,平台不可能提供一个满足100%应用场景的UI构建方案。
所以,我们可以化用“二八定律”对这个问题做个略微粗暴的总结(当然这里的2与8都是虚数):
对于一开放问题域,任何单一方案至多只能满足其80%场景。
逃生舱
注:笔者非常喜欢React新版文档中所使用的“逃生舱”(Escape Hatches)这一比喻,因此在此处也借鉴一番。但需要注意的是,这篇文章中的逃生舱与React中的概念并不完全相同,还需读者自行鉴别。
聊到这里,其实我们的最佳策略已经不言而喻了。如上所述,已知:
- 事实1:易用性与性能/可维护性不可放弃
- 事实2:单一方案至多只能满足80%场景
显而易见,对于平台设计者来说,他们应该将绝大部分精力投入到服务好平台最擅长的那80%业务场景当中去,竭尽全力在优势领域中做到尽善尽美。
至于剩余的20%边缘场景,在某些时候,直接放弃或许也是一种可以接受的选项。
当然,对于那些富有野心的平台来说,这恐怕难以接受。那么此时的最佳策略便是本节的标题所指:在平台设计之初,设计者就应该考虑到为将来某些高级使用者提供一个完善的“逃生舱”机制。
这里的“逃生舱”指的是一种向平台使用者暴露底层能力的机制。它可以是一组低级API,也可以是一批可供二开扩展的集成点,甚至也可以是一套进行定制开发的解决方案。总之,为了满足从过去至将来100%的业务需求,平台必须允许用户在有必要的时候进行“抽象降级”,有机会乘坐逃生舱从平台规定的条条框框之中逃离。
这样的设计思想其实在软件工程领域相当常见,譬如plugin API之于webpack、unsafe之于rust、指针之于C#等等……只不过由于低代码平台本身业务的复杂性,设计出一套优雅的“逃生舱”机制并不容易,因此设计者在平台设计的早期就必须考虑这个问题。
当前我们团队遇到的问题,就是由于平台早期缺乏良好的“逃生舱”设计,导致了面对超出平台能力的新需求时无从下手:
- 若在原有架构上修修补补——复杂度爆炸
- 把架构推倒重来——工期爆炸
- 若拒绝支持新需求——PM爆炸
可以用一个词来形容——积重难返。
小结
如上,笔者将本人在低代码平台上遇到的困难与思考进行了些浅显总结,斗胆试图借此来发现一点架构设计的普遍规律,惭愧惭愧……
文章最后,再试着简单总结一些由本文观点得出的建议。
对于平台使用者
对于使用者来说,最大的启示是在技术选型的阶段最好留个心眼,切勿轻信了平台销售对其产品功能的吹嘘。尤其是如果有人胆敢宣扬他的平台无所不能,今天开发明天上线后天解雇程序员云云,那你可得小心了。
正如前文所言,任何低代码产品的出发点都是为了降低用户的使用成本,所以如果一个平台即承诺了易用性,又承诺了功能性,那么代价是什么?
经验来说,反倒是专注于一个特定领域的平台供应商往往更加务实,也有更大概率做出更成熟的产品。
对于平台设计者
对于设计者来说,最大的挑战是在如上不可能三角中取得平衡。对于不同类型的系统设计取向自然也不同,但对于低代码平台这一品类来说,大部分情况下,易用性和系统复杂度还是比功能性重要的多。
因此如果你的产品也具有类似的价值取向的话,那么作为架构设计者,尽早地规划“逃生舱”的设计和落实才是负责任的做法。