译:本地优先软件 Local-first software

August 20, 2022 · 106 min read

You own your data, in spite of the cloud.

本文译自 Ink&Switch 发表于 2019 年 4 月的 《Local-first software》

This article is the translation of Local-first software written by Ink&Switch in April 2019

原文作者

Google Docs 和 Trello 这样的云应用很受欢迎,因为它们能够实现与同事的实时协作,而且它们使我们能够轻松地从所有的设备上访问我们的工作。然而,因为数据集中在服务器上存储,云应用也从用户手中夺走了所有权和代理权。如果一个服务关闭了,软件就会停止运作,用该软件创建的数据也会丢失。

在这篇文章中,我们提出了 “本地优先软件” : 它是一套兼顾了协作和用户对数据的所有权的软件原则。本地优先的愿景包括离线可用和多端协作的能力,同时也改善数据的安全性、隐私性、长期可用性和用户对数据的控制能力。

我们调查了现有的数据存储和共享方法,从电子邮件附件到 Web Apps,再到由 Firebase 驱动的移动应用。我们分析了他们各自的特性。我们研究了无冲突复制数据类型(CRDTs):这种数据结构从本质上就支持多用户,同时也是本地化的和保证私密性的。CRDTs 有可能成为实现本地优先的软件的基础技术。

本文分享了几年来我们在 Ink & Switch 开发本地优先软件原型的一些发现。这些实验检验了 CRDTs 在实践中的可行性,并探讨了这种新数据模型的 UI 上的挑战。最后,我们分别针对研究员、应用开发者和创业者提出了向本地优先软件发展的行动建议。

这篇文章还以 PDF 格式发表在 Onward! 2019 年会议。请用以下方式引用

Martin Kleppmann, Adam Wiggins, Peter van Hardenberg, and Mark McGranaghan. Local-first software: you own your data, in spite of the cloud. 2019 ACM SIGPLAN International Symposium on New Ideas, New Paradigms, and Reflections on Programming and Software (Onward!), October 2019, pages 154–178. doi:10.1145/3359591.3359737

动机:协作和所属权

如今我们可以极为轻松地进行在线协作。我们使用 Google Docs 来协作处理文档、电子表格和演示文稿;在 Figma 中,我们可以多人进行用户界面设计;我们使用 Slack 与同事沟通;我们在 Trello 中跟踪任务。我们依赖这些在线服务来完成笔记、计划项目、保存联系人,以及各类商业用途。

我们将这些服务称为 “云应用(cloud apps)”,但你也可以称它们为 “SaaS” 或 “基于网络的应用程序”。它们的共同点是,我们通常通过网络浏览器或移动应用程序访问它们,并且它们将数据存储在服务器上。

与前几代软件相比,今天的云应用提供了很大的好处:无缝协作,并能从任何设备上访问数据。随着我们的生活和工作越来越多地通过这些云应用运行,它们对我们变得越来越重要。我们在使用这些应用程序中投入的时间越多,其中的数据对我们来说就越有价值。

然而通过与很多从事创造性工作的专业人士进行了交流,我们也了解到了云应用的缺点。

当你在制作某样东西时投入了大量的创造性精力和努力,你往往会对它有很深的情感依恋。如果你从事创造性工作,这体会或许不陌生。(当我们说“创造性工作”时,我们指的不仅仅是视觉艺术、音乐、或诗歌—许多其他活动也都是创造性的,如:解释某种技术、实现复杂的算法、设计用户界面、或思考如何带领团队实现目标)。

在进行创造性工作的过程中,你通常会产生文件和数据:文档、演示文稿、电子表格、代码、笔记、图纸等等。你会希望保留这些数据:供将来参考和激发灵感,将其纳入作品集,或者仅仅是存档,因为你为它感到骄傲。这种「拥有」的感受是很重要的,因为创造性的表达是非常个人化的东西。

不幸的是,云应用在这方面是有问题的。虽然他们让你在任何地方访问你的数据,但所有的数据访问都必须通过服务器,而且你只能做服务器让你做的事情。从某种意义上说,你对这些数据没有完全的所有权—而云供应商拥有。所谓云,只是别人的电脑。

There is no cloud, it’s just someone else’s computer.

我们使用的 “所有权”一词不是指知识产权法和版权,而是指创造者对其数据的感知关系。我们在后面的章节中讨论这个概念。

当数据被存储在 “别人的电脑”上时,它就对该数据有一定的控制能力。云应用是作为一种服务提供的;如果服务不可用,你就不能使用该软件,你也不能再访问你用该软件创建的数据。如果服务关闭,即使你可能能够导出你的数据,但如果没有服务器,你通常没有办法继续运行这个软件。因此,你只能任由提供服务的公司摆布。

在网络应用出现之前,我们有一些我们可以称之为 “老式”的应用:在你的本地计算机上运行的程序,在本地磁盘上读写文件。今天我们仍然在使用这种类型的应用程序:文本编辑器和集成开发环境、Git 和其他版本控制系统,以及许多专业软件,如图形应用程序或 CAD 软件都属于这种类型。

我们在本文中谈论的软件是用于创建文档或文件(如文本、图形、电子表格、CAD 图纸或音乐),或个人数据存储库(如笔记、日历、待办事项列表或密码管理器)的应用程序。我们不是在谈论实施像银行服务、电子商务、社交网络、共享汽车或类似的服务,这些都是集中式系统能很好提供的。

在老式的应用程序中,数据存放在你的本地磁盘上的文件中,所以你对这些数据有完全的代理权和所有权:你可以做任何你喜欢的事情,包括长期存档,做备份,使用其他程序操作文件,或在你不再需要它们时删除文件。你不需要任何人的许可来访问你的文件,因为它们是你的。你不必依赖由另一家公司运营的服务器。

总而言之:云计算给了我们协作,但老式的应用程序给了我们所有权。难道我们不能同时拥有这两个世界的优点吗?

我们既希望有云应用提供的方便的跨设备访问和实时协作,也希望有 “老式”软件体现的对你自己数据的拥有权。

本地优先软件的七个愿景

我们相信,数据所有权和实时协作并不相悖。我们有可能创建具有云应用所有优点的软件,同时也允许你保留对你创建的数据、文档和文件的完全所有权。

我们把这种类型的软件称为本地优先软件,因为它优先使用本地存储(你的电脑中内置的磁盘)和本地网络(如你的家庭 WiFi)而不是远程数据中心的服务器。

在云计算应用程序中,服务器上的数据被视为数据的主要的、权威的副本;如果客户端有一份数据的副本,它只是一个从属于服务器的缓冲区。任何数据修改都必须发送到服务器上,否则就 “没有发生”。在本地优先的应用程序中,我们交换了这些角色:我们把你本地设备上的数据副本—你的笔记本电脑、平板电脑或手机—视为主要副本。服务器仍然存在,但它们持有你的数据的次要副本,以协助从多个设备访问。正如我们将看到的,这种观点的变化具有深远的影响。

以下是我们希望在本地优先的软件中实现的七个愿景。

1. 即刻加载:数据触手可及

今天的许多软件感觉比前几代软件慢。尽管 CPU 的速度越来越快,但在用户的一些输入(如点击一个按钮,或按下一个键)和相应的结果出现在显示屏上之间,往往有可察觉的延迟。在此前的工作中,我们测量了现代软件的性能并分析了这些延迟发生的原因。

全球不同地点的 AWS 数据中心之间的服务器到服务器的往返时间。 数据来源: Peter Bailis, Aaron Davidson, Alan Fekete, et al.: “Highly Available Transactions: Virtues and Limitations,” VLDB 2014.

对于云计算应用程序,由于数据的主要副本在服务器上,所有的数据修改和许多数据查询都需要往返于服务器。服务器很可能与你的居住地不在同一片大陆上,所以光速对软件的速度有限制。

用户界面可能试图通过将操作显示为已经完成来隐藏这一延迟,即使请求仍在进行中—这种模式被称为乐观的 UI (Optimistic UI) —但在请求完成之前,总是有可能会失败(例如,由于不稳定的互联网连接)。因此,乐观的 UI 有时仍然会在错误发生时暴露出网络往返的延迟。

本地优先的软件则不同:因为它把数据的主要副本保存在本地设备上,所以用户永远不需要等待对服务器的请求完成。所有的操作都可以通过读写本地磁盘上的文件来处理,与其他设备的数据同步在后台悄然发生。

虽然这本身并不能保证软件的速度,但我们期望本地优先的软件有可能对用户的输入做出近乎即时的反应,在你等待的时候永远不需要向你展示一个“加载中”的状态,让你的数据触手可及。

2. 多端同步

如今用户常常使用多种电子设备来完成他们的工作,而现代应用程序必须支持这种工作流程。例如,用户可能会在旅途中使用智能手机捕捉想法,在平板电脑上组织和思考这些想法,然后在笔记本电脑上把结果打成文档。

这意味着,虽然本地优先的应用程序将其数据保存在每个设备的本地存储中,但这些数据也有必要在用户进行工作的所有设备上进行同步。目前存在多种数据同步技术,我们在后面的章节中详细讨论。

大多数跨设备同步服务也在服务器上存储数据的副本,这为数据提供了一个方便的异地备份。只要每个文件一次只被一个人编辑,这些解决方案的效果就很好。如果几个人同时编辑同一个文件,可能会产生冲突,这一点我们将在协作部分讨论。

3. 网络只是一种可选项

个人移动设备所处的网络环境经常发生变化:不可靠的咖啡馆 WiFi,在飞机上或火车上穿过隧道时,在电梯或停车场。在发展中国家或农村地区,互联网接入的基础设施有时是分布不均的。在国际旅行时,由于漫游的费用,许多移动用户禁用手机数据。总的来说,有很多对离线应用程序的需求,例如或需要在野外创作的研究人员或者记者。

“老式的”应用程序在没有互联网连接的情况下可以正常工作,但云应用通常在离线时无法工作。几年来,“离线优先”运动一直在鼓励网络和移动应用的开发者改善对离线的支持,但在实践中,很难对云应用的离线支持进行改造,因为为以服务器为中心的模式设计的工具和库不容易适应用户在离线时进行编辑的情况。

尽管有可能使 Web App 在离线状态下工作,但用户可能很难知道是否 App 所有必要的代码和数据已经下载好了

由于本地优先的应用程序将其数据的主要副本存储在每个设备的本地文件系统中,用户即使是在离线时也可以随时读取和写入这些数据。当网络连接可用时,它再与其他设备同步。数据同步不一定要通过互联网:本地优先的应用程序也可以使用蓝牙或本地 WiFi 将数据同步到附近的设备。

此外,为了提供良好的离线支持,软件最好以本地安装的可执行文件的形式在设备上运行,而不是网络浏览器中的一个标签。对于移动应用程序来说,在使用前下载并安装整个应用程序已经是一种标准。

4. 无缝协作

协作通常要求多人修改同一份文件或或文档。然而,在老式的软件中,几个人同时在同一份文件上工作常常产生冲突。在代码等文本文件中,解决冲突是枯燥且恼人的,而对于电子表格或图形文件等复杂的文件格式,这项任务很快就变得非常困难甚至不可能完成。因此,协作者可能要事先约定谁来编辑份文件,而且每次只有一个人可以进行修改。

Dropbox 上的冲突编辑,用户需要自行手动合并冲突

Evernote 中,如果笔记被同时修改,那么它就会被移动到冲突修改的笔记中,并且没有支持用户解决冲突的工具

在 Git 和其他版本控制系统上,多个人在不通过 commit 上并行修改同一份文件。合并这些改动常常导致“合并冲突”,需要通过专门的工具来解决。这些工具主要是为面向行的文本文件提供的,例如源代码。对于其他类型的文件格式的支持很弱

另一方面,Google Docs 等云计算应用大大简化了协作,允许多个用户同时编辑一份文件,而不必通过电子邮件来回发送文件,也不必担心冲突。用户已开始期待更多应用支持无缝实时协作。

在本地优先的应用程序中,我们的理想是支持与当今最好的云应用一样的实时协作,甚至更好。实现这一目标是实现本地优先软件的最大挑战之一,但我们相信这是可能的:在后面的章节中,我们将讨论在本地优先环境下实现实时协作的技术。

此外,我们期望本地优先的应用程序可以支持各种协作的工作流程。除了让几个人实时协作编辑,有时提供修改建议的模式也是很有价值的。这种模式中由一个人提出修改建议,让其他人审查并有选择性地应用修改。Google Docs 的建议模式和 GitHub 的 Pull Request 就支持这种工作流。

在 Google Docs 中,协作者可以通过直接编辑文档或者他们也可以提供修改建议,之后可以由文档拥有者来决定是否采用这些建议

GitHub 上的协作工作流程是基于 Pull Request 的。一个用户可以在多个 Commits 中修改多个源文件,并将其作为 Pull Request 提交给一个项目。其他用户可以审查和修改这个 Pull Request

5. 长期可用

数据所有权的一个重要方面是,你可以在未来很长一段时间内继续访问这些数据。当你用本地优先的软件做一些工作时,你的工作应该可以继续无限期地被访问,甚至在生产该软件的公司消失之后。

泥板上的楔形文字,约公元前 3000 年。图片来自 Wikimedia Commons

“老式”的应用程序是永久可用的,只要你有对应数据和一些运行软件的方法,即使软件作者破产了,你也可以继续运行该软件。即使操作系统和它所运行的计算机已经过时,你仍然可以在虚拟机或模拟器中运行该软件。存储介质迭代更新了,你也可以把你的文件复制到新的存储介质上,继续访问它们。

互联网档案馆收集了一些历史软件,可以在现代网络浏览器中使用模拟器运行;英国 Amiga 委员会的爱好者们分享了运行历史软件的技巧。

另一方面,云应用依赖于服务的可用性:如果服务不可用,你就不能使用该软件,你也不能再访问你用该软件创建的数据。这意味着你在打赌「软件的创建者将长期维护它」—至少在你关心数据的时候。

虽然谷歌似乎没有很快关闭谷歌文档的巨大危险,但受欢迎的产品有时确实会被关闭丢失数据,所以我们知道要小心。而且,即使是寿命长的软件,也存在价格或功能以你不喜欢的方式改变的风险,而对于云应用,继续使用旧版本不是一种选择—无论你是否喜欢,你都会被升级。

我们不可思议的旅程是一个博客,它记录了创业公司产品在被收购后被关闭的情况。

本地优先软件有更长的寿命,因为你的数据以及读取和修改你的数据所需的软件,都是在你的电脑上本地存储的。这一点很重要,不仅是为了你自己,也是为了未来的历史学家,他们会想要阅读我们今天创造的文件。如果我们的数据不能长期保存,我们就有可能创造出文特-瑟夫所说的 “数字黑暗时代”

我们以前写过关于网页的长期存档的文章。关于长期数据保存的有趣讨论,请看 Long Tien Nguyen 和 Alan Kay 在 Onward!2015 上发表的论文 《2015 年的楔形文字》

一些文件格式(如纯文本、JPEG 和 PDF)是如此的普遍,以至于它们可能在未来几个世纪内都可以被阅读。美国国会图书馆也推荐 XML、JSON 或 SQLite 作为数据集的存档格式。然而,为了读取不太常见的文件格式并保留交互,你需要能够运行原始软件(如果有必要,在虚拟机或模拟器中)。本地优先软件可以实现这一点。

6. 安全性和隐私性

云应用架构的一个问题是,它们将所有用户的所有数据存储在一个集中的数据库中。这种大量的数据收集对攻击者来说是充满吸引力的目标:一个流氓雇员,或一个获得公司服务器访问权的黑客,可以阅读和篡改你的所有数据。令人恐惧的是这样的安全漏洞很常见。我们不幸地任由云应用供应商的摆布。

虽然谷歌有世界级的安全团队,但可悲的现实是,大多数公司都没有。虽然谷歌善于保护你的数据免受外部攻击,但公司内部却可以自由地以各种方式使用你的数据,例如将你的数据输入其机器学习系统。

引用 Google Drive 服务条款的内容。“我们的自动化系统分析你的内容,为你提供个人相关的产品功能,如定制的搜索结果,以及垃圾邮件和恶意软件检测。”

也许你觉得你的数据不会引起任何攻击者的兴趣。然而,对于许多职业来说,处理敏感数据是他们工作的重要部分。例如,医疗专业人士处理敏感的病人数据,调查记者处理消息来源的机密信息,政府和外交代表进行敏感的谈判,等等。由于法规遵从和保密义务,这些专业人士中的许多人不能使用云应用。

另一方面,本地优先的应用程序,在核心部分内置了更好的隐私和安全。你的本地设备只存储你自己的数据,避免了集中式的云数据库保存着所有人的数据。本地优先的应用程序可以使用端对端加密,这样,任何存储你的文件副本的服务器都只持有他们无法读取的加密数据。

iMessageWhatsAppSignal 这样的现代信息应用已经使用了端对端加密,Keybase 提供了加密的文件共享和信息传递,Tarsnap 也采取了这种方法进行备份。我们希望看到这种趋势也能扩展到其他类型的软件。

7. 你保留最终的所有权和控制权

对于云应用,服务提供商有权力限制用户的访问:在 2017 年 10 月,几个谷歌文档的用户被锁定在他们的文件之外,因为某个自动系统错误地将这些文件标记为违规。在本地优先的应用程序中,数据的所有权归属于用户。

在本文中提及的“所有权”并不是指知识产权的法律意义上的所有权。例如,文字处理工具无法知晓谁拥有所编辑文本的版权。而我们指的是对数据的控制的意义上的所有权:你应该能够以任何方式复制和修改数据,写下任何想法,没有公司应该限制你的行为。

根据《欧洲人权公约》,你的思想和意见自由是无条件的—国家永远不得干涉,因为它只属于你自己—而言论自由(包括言论自由)则可以在某些方面受到限制,因为它影响到其他人。像社交网络这样的通信服务传达的是表达,但创作人的原始笔记和未发表的作品是发展思想和意见的一种方式,因此值得无条件地保护

在云应用中,访问和修改你自己的数据的方式受到服务提供商的 API、用户界面和服务条款的限制。在本地优先的软件中,构成你的数据的所有字节都存储在你自己的设备上,所以你可以自由地以任意的方式处理这些数据。

伴随数据所有权而来的是相对应的责任:采用备份或者其他防止数据丢失的措施,防止勒索软件,以及组织和管理文件。对专业用户来说,我们认为用更多的责任来换取更多的所有权是可取的(如本文开头中所介绍的)。例如重要的个人创作,如博士论文或电影的原始镜头。对于这些数据,你可能愿意承担存储和备份的责任,以确保你的数据是安全的,完全在你的控制之下。

在我们看来,保持对数据的控制和所有权并不意味着软件必须是开放源代码的。尽管修改软件的自由有价值,但只要不人为地限制用户对其文件的操作,商业和闭源软件是可以满足本地优先的理想的。这种人为限制的例子有:禁用打印等操作的 PDF 文件,干扰复制粘贴的电子书阅读器,以及媒体文件的 DRM。

现有的数据存储和共享模式

我们相信专业和创造性的用户应该得到能够实现本地优先目标的软件,帮助他们进行无缝协作,同时也允许他们保留对工作成果的完全所有权。如果我们能在用户用来做最重要工作的软件中提供这些品质,我们就能帮助他们更好地完成工作,并有可能为许多人的职业生活带来重大改变。

然而,虽然本地优先软件的理想可能会引起你的共鸣,但你可能仍想知道它们的可行性。它们是否只是乌托邦式的幻想?

在下文中,我们将讨论在实践中实现本地优先的软件意味着什么。我们考察了大量的现有技术,并对它们满足本地优先理想的程度进行了分解。在下面的表格中,✓ 表示技术符合理想,-表示部分符合理想,而 ✗ 表示不符合理想。

正如我们将看到的,许多技术满足了部分目标,但没有一项技术能够满足所有目标。最后,我们将研究一种来自计算机科学研究前沿的技术,它可能是未来实现本地优先软件的重要基础设施。

应用程序架构如何影响用户体验

让我们先从最终用户的角度来审视软件,并分解不同的软件架构在多大程度上满足了本地优先软件的七个目标。在下一节中,我们将比较软件工程师用来构建应用程序的存储技术和 API。

文件和邮件附件

1. 快2. 多设备3. 离线4. 协作5. 长期可用性6. 隐私7. 控制权
文件和邮件附件--

从我们的七个目标来看,传统的文件有许多理想的特性:它们可以被离线查看和编辑,它们给了用户完全的控制权,它们可以随时被备份和长期保存。依靠本地文件的软件也有可能变得非常快。

然而,从多个设备访问文件是比较棘手的。有多种方式在设备间传输文件:

  • 通过电子邮件来回发送
  • 来回传递 U 盘
  • 通过分布式文件系统,如 NAS 服务器、NFS、FTP 或 rsync
  • 使用云文件存储服务,如 Dropbox、Google Drive 或 OneDrive(见后面章节)
  • 使用版本控制系统,如 Git(见后面的章节)

其中,电子邮件附件可能是最常见的共享机制,特别是在那些不是技术专家的用户中。附件很容易理解和信任。一旦你有了一份文件的副本,除非用户主动修改,否则它就不会变:如果你在六个月后查看一封电子邮件,附件仍然以其原始形式存在。与网络应用不同,附件不需要任何额外的登录过程就可以打开。

电子邮件附件最薄弱的地方是协作。一般来说,一次只能有一个人对文件进行修改,否则就需要进行困难的手动合并。文件的版本很快就会变得混乱,往返发送的协作文件常导致文件名变成奇怪的样子: 预算草案 (小明版)绝对最终版(2).xls

尽管如此,对于那些想要融入本地优先理念的应用程序来说,一个好的起点是提供导出功能,生成被广泛支持的文件格式(如纯文本、PDF、PNG 或 JPEG),并允许它被分享,例如通过电子邮件附件、Slack 或 WhatsApp。

网页应用:Google Docs, Trello, Figma, Pinterest, etc.

1. 快2. 多设备3. 离线4. 协作5. 长期可用性6. 隐私7. 控制权
Google Docs----
Trello---
Pinterest

在光谱的另一端是纯 Web App,此时用户的本地软件(浏览器或移动应用)是瘦客户端,数据存储在服务器上。服务器通常使用一个大规模的数据库,其中数百万用户的数据都混合在一个巨大的集合中。

Web 应用程序已经设定了实时协作的标准。作为用户,你可以相信,当你在任何设备上打开文件时,你看到的是最新的内容,用的是最新的版本。这对团队工作来说是非常有用的,以至于这些应用程序已经成为主导。甚至像微软 Office 这样传统的纯本地软件也在向云服务过渡,截至 2017 年,Office 365 已经让本地安装的 Office 黯然失色

随着远程工作和分布式团队的兴起,实时协作的生产力工具正变得更加重要。团队视频通话中的十个用户可以调出同一个 Trello 板,每个人在自己的电脑上进行编辑,同时看到其他用户正在做什么。

这样做的负面是所有权和控制权的完全丧失:服务器上的数据才是最重要的,而你的客户端设备上的任何数据都是不重要的,它只是缓存。大多数网络应用程序对离线支持很差或者根本不支持:如果你的网络出现哪怕是片刻的故障,你的工作就会被锁定。

如果 Google Docs 检测到它是离线的,它就禁止文档编辑

一些最好的网络应用程序使用 JavaScript 隐藏了服务器通信的延迟,并试图提供有限的离线支持(例如,Google Docs 的离线插件)。然而,这些努力似乎都是为了适应从根本上以与服务器同步互动为中心的应用架构。用户在尝试离线工作时报告了不同的结果。

Google Docs 离线插件的负面评价

一些网络应用,例如 Milanote 和 Figma,提供了可安装的桌面客户端,基本上是重新包装的网络浏览器。如果你试图使用这些客户端访问你的工作,而你的网络是断断续续的,当供应商的服务器遇到故障时,或者在供应商被收购并关闭后,很明显,你的工作从来就不是真正属于你的。

Figma 桌面端离线时

网盘:Dropbox, Google Drive, Box, OneDrive, etc.

1. 快2. 多设备3. 离线4. 协作5. 长期可用性6. 隐私7. 控制权
Dropbox---

基于云的文件同步产品,如 Dropbox、Google Drive、Box 或 OneDrive,使文件在多个设备上可用。在桌面操作系统(Windows、Linux、Mac OS)上,这些工具通过观察本地文件系统上的指定文件夹来工作。你的电脑上的任何软件都可以读写这个文件夹中的文件,每当一台电脑上的文件被改变,它就会自动复制到你所有的其他电脑上。

由于这些工具使用本地文件系统,它们有许多吸引人的特性:对本地文件的访问是快速的,离线工作也没有问题(离线编辑的文件在下次有互联网连接的时候会被同步)。如果同步服务被关闭,你的文件仍然会在你的本地磁盘上保持无损,而且很容易切换到另一个同步服务。如果你的电脑硬盘出现故障,你只需安装应用程序并等待它同步,就可以恢复你的工作。这提供了良好的长期可用性和对数据的控制能力。

然而,在移动平台(iOS 和安卓)上,Dropbox 及其表亲使用了完全不同的模式。移动应用程序不同步整个文件夹—相反,它们是瘦客户端,每次从服务器上获取你的数据一个文件,而且默认情况下,它们不能脱机工作。有个 “离线可用”的选项,但你需要记住在离线之前调用它,它是笨拙的,而且只有在应用程序打开时才有效。Dropbox 的 API 也是非常以服务器为中心的。

Dropbox 移动端的用户的很多时间花在“加载中”的页面上,和桌面端即刻加载的体验大相径庭

文件同步产品的最弱点是缺乏实时协作:如果同一个文件在两个不同的设备上被编辑,其结果是需要手动合并的冲突,如前所述。这些工具可以同步任何格式的文件,这既是一个优点(与任何应用程序兼容),也是一个缺点(无法进行特定格式的合并)。

Git 和 GitHub

1. 快2. 多设备3. 离线4. 协作5. 长期可用性6. 隐私7. 控制权
Git+GitHub---

GitGitHub 主要被软件工程师用来进行源代码的协作。它们也许是我们最接近真正的本地优先的软件包:与以服务器为中心的版本控制系统(如 [Subversion](https://subversion.apache.org/)相比,Git 可以完全脱机工作,它速度快,可以给用户完全的控制权,而且适合长期保存数据。之所以如此,是因为本地文件系统上的 Git 仓库是数据的主要副本,不从属于任何服务器。

我们在此着重介绍 Git/GitHub,作为最成功的例子,但这些经验也适用于其他分布式版本控制器,如 Mercurial 或 Darcs,以及其他仓库托管服务,如 GitLab 或 Bitbucket。原则上,没有仓库服务也可以进行协作,例如通过电子邮件发送补丁文件,但大多数 Git 用户都依赖 GitHub。

像 GitHub 这样的仓库托管服务可以实现围绕 Git 仓库的协作,从多个设备上访问数据,并提供一个备份和存档位置。目前对移动设备的支持还很薄弱,尽管 Working Copy 是一个很有前途的 iOS 版 Git 客户端。GitHub 存储的仓库是不加密的;如果需要更强的隐私保护,你可以运行自己的仓库服务器。

我们认为 Git 的模式为本地优先的软件指明了方向。然而,就目前而言,Git 有两个主要的弱点。

  1. Git 在异步协作方面非常出色,尤其是使用 Pull Requests,它可以获取一组粗略的修改,并在合并到共享的主干分支之前对其进行讨论和修改。但 Git 并不具备实时、细粒度协作的能力,比如 Google Docs、Trello 和 Figma 等工具中出现的自动、即时合并。
  2. Git 对代码和类似的基于行的文本文件进行了高度优化;其他文件格式则被视为二进制文件,无法进行有意义的编辑或合并。尽管 GitHub 努力显示和比较图像、散文和 CAD 文件,但非文本文件格式在 Git 中仍然是次要的。

有趣的是,大多数软件工程师都不愿意接受云版本的编辑器、IDE、运行时环境和构建工具。从理论上讲,我们一般会预期这个用户群体比其他类型的用户更早地接受新技术。但是,如果你问工程师为什么不使用 Cloud9Repl.it 这样的云端编辑器,或 Colaboratory 这样的运行环境,答案通常包括“它太慢了”或“我不信任它”或“我希望我的代码在我的本地系统”。这些情绪似乎反映了一些与本地优先的软件相同的动机。如果我们作为开发者为自己和我们的工作想要这些东西,也许我们可以想象,其他类型的创造性的专业人士也会为他们自己的工作想要这些相同的特性。

构建 Apps 的开发者基础设施

现在我们已经通过本地优先的理念考察了一系列应用程序的用户体验,让我们把思维转换到应用程序开发者的角度。如果你正在创建应用程序,并希望为用户提供部分或全部本地优先的体验,你在数据存储和同步基础设施方面有哪些选择?

Web app (瘦客户端)

1. 快2. 多设备3. 离线4. 协作5. 长期可用性6. 隐私7. 控制权
Web apps

最纯粹的网络应用通常是在服务器上运行的 Rails、Django、PHP 或 Node.js 程序,将其数据存储在 SQL 或 NoSQL 数据库中,并通过 HTTPS 提供网页。所有的数据都在服务器上,而用户的网络浏览器只是一个瘦客户端。

这种架构有很多好处:零安装(只需访问一个 URL),用户无需管理,因为所有数据都由部署应用程序的工程和 DevOps 专业人员存储和管理在同一个地方。用户可以从他们所有的设备上访问该应用程序,同事们可以通过登录同一个应用程序轻松地进行合作。

Meteor 和 ShareDB 等 JavaScript 框架,以及 Pusher 和 Ably 等服务,使得在 WebSocket 等低级协议的基础上,更容易向 Web 应用添加实时协作功能。

另一方面,一个网络应用如果需要为每个用户的行动向服务器执行请求,那么速度就会很慢。在某些情况下,可以通过使用客户端的 JavaScript 来隐藏通讯时间,但如果用户的互联网连接不稳定,这些方法很快就会失效。

尽管有许多努力使网络浏览器对离线更友好(minifestslocalStorageservice workersProgressive Web Apps 等),但网络应用程序的架构从根本上说仍然是以服务器为中心的。离线支持在大多数网络应用中都是后续增加的能力,因而往往很脆弱。在许多网络浏览器中,如果用户清除了他们的 cookies,本地存储中的所有数据也会被删除;虽然这对缓存来说不是一个问题,但它使得浏览器的本地存储不适合存储任何长期的重要数据。

新闻网站《卫报》记录了他们如何使用 Service Worker 为用户建立离线体验。

依靠第三方网络应用在长期可用性、隐私和用户控制方面的得分也很低。如果网络应用是开源的,并且用户愿意自我托管他们自己的服务器实例,就有可能改善这些属性。然而,我们认为对于绝大多数用户来说自我托管并不是一个可行的选择;此外,大多数网络应用是闭源的,完全排除了这种选择。

总而言之,我们推测,由于平台的瘦客户端性质,网络应用将永远无法提供我们所寻找的所有本地优先的属性。选择建立一个 Web App,你就选择了属于你和你的公司,而不是属于你的用户的数据之路。

带本地存储的移动端应用(胖客户端)

1. 快2. 多设备3. 离线4. 协作5. 长期可用性6. 隐私7. 控制权
Thick client--

iOS 和安卓的应用程序是本地安装的软件,在运行应用程序之前下载并安装整个应用程序二进制。然而,许多应用程序是瘦客户端,类似于 Web 应用程序,需要服务器才能运行(例如,Twitter、Yelp 或 Facebook)。如果没有可靠的互联网连接,这些应用程序会给你带来“加载中”、“出错了”和意外行为。

然而,还有一类移动应用程序更符合本地优先的理想。这些应用首先在本地设备上存储数据,使用 SQLite、Core Data 等持久层,或者只是普通文件。其中一些(如 ClueThings)开始时是一个没有任何服务器的单用户应用程序,后来增加了云后端,作为一种在设备间同步或与其他用户共享数据的方式。

这些胖客户端应用程序的优点是快速和离线工作,因为服务器同步发生在后台。如果服务器被关闭,它们通常会继续工作。它们提供隐私和用户对数据控制的程度取决于相关的应用程序。

如果数据可能在多个设备上被修改或被多个协作用户修改,事情就会变得更加困难。移动应用的开发者通常是终端用户应用开发方面的专家,而不是分布式系统方面的专家。我们已经看到多个应用开发团队在编写他们自己的临时差异、合并和冲突解决算法,而由此产生的数据同步解决方案往往是不可靠的和脆弱的。下一节中讨论的更专业的存储后端可以提供帮助。

后端即服务 BaaS: Firebase, CloudKit, Realm

1. 快2. 多设备3. 离线4. 协作5. 长期可用性6. 隐私7. 控制权
Firebase, CloudKit, Realm--

Firebase 是最成功的移动后盾即服务选项。它本质上是一个本地设备上的数据库与云数据库服务和两者之间的数据同步相结合。Firebase 允许在多个设备上共享数据,并支持离线使用。然而,作为一个专有的托管服务,我们在隐私和长期可用性上给它打了低分。

另一个受欢迎的后台即服务是 Parse,但它在 2017 年被 Facebook 收购,然后被关闭。依赖于它的应用程序被迫转移到其他后台服务,这强调了长期可用性的重要性。

Firebase 开发者提供了很好的体验:你可以在 Firebase 控制台以自由的方式查看、编辑和删除数据。但是,用户却没有类似的方式来访问、操作和管理他们的数据,使得用户几乎没有所有权和控制权。

Firebase 控制台: 对开发者非常友好,终端用户则无法使用

苹果的 CloudKit 为愿意将自己限制在 iOS 和 Mac 平台上的应用提供了类似 Firebase 的体验。它是一个具有同步功能的键值存储,具有良好的离线能力,而且它还有一个额外的好处,那就是内置于平台中(从而避免了用户必须创建一个账户并登录的笨拙)。对于独立的 iOS 开发者来说,这是一个很好的选择,像 UlyssesBearOvercast 等工具都使用了它,效果不错。

通过一个复选框,Ulysses在用户的所有连接设备上同步工作,这得益于它对CloudKit的使用

这方面的另一个项目是 Realm。与 Core Data 相比,这个用于 iOS 的持久化库因其更简洁的 API 而获得欢迎。用于本地持久化的客户端库被称为 Realm 数据库,而相关的类似 Firebase 的后端服务被称为 Realm 对象服务器。值得注意的是,对象服务器是开源的,可自行托管,这减少了被锁定在一个可能有一天会消失的服务上的风险。

移动应用程序将设备上的数据作为主要副本(或至少比一次性缓存多),并使用 Firebase 或 iCloud 等同步服务,使我们在实现本地优先软件上有了很大的进展。

CouchDB

1. 快2. 多设备3. 离线4. 协作5. 长期可用性6. 隐私7. 控制权
CouchDB-----

CouchDB 是一个数据库,因开创了多主机复制而备受关注:每台机器都有一个成熟的数据库副本,每个副本都可以独立地对数据进行修改,任何一对副本都可以相互同步,以交换最新的修改。CouchDB 是为在服务器上使用而设计的;Cloudant 提供了一个托管版本;PouchDBHoodie 是兄弟项目,使用相同的同步协议,但被设计为在终端用户设备上运行。

从哲学上讲,CouchDB 与本地优先原则密切相关,这一点在 CouchDB 的书中尤其明显,该书对分布式一致性复制变更通知多版本并发控制等相关主题做了很好的介绍。

虽然 CouchDB/PouchDB 允许多个设备同时对数据库进行修改,但这些修改会导致冲突,需要由应用程序代码显式解决。这种冲突解决代码很难正确编写,这使得 CouchDB 对于具有非常精细的协作的应用来说是不切实际的,比如在 Google Docs 中,每个按键都有可能是一个单独的修改。

在实践中,CouchDB 模型还没有被广泛采用。这其中有各种原因:当每个用户需要一个单独的数据库时,存在可扩展性问题;在 iOS 和 Android 的本地应用程序中嵌入 JavaScript 客户端存在困难;冲突解决的问题;使用不常见的 MapReduce 模型来查询,等等。总而言之,虽然我们同意 CouchDB 背后的大部分理念,但我们认为在实践中,其实现并没有能够实现本地优先的愿景。

迈向更美好的未来

正如我们所表明的,现有的用于应用开发的数据层都不能完全满足本地优先的理想。因此,三年前,我们的实验室着手寻找一个能给出七个绿色复选标记的解决方案。

1. 快2. 多设备3. 离线4. 协作5. 长期可用性6. 隐私7. 控制权
???

我们已经发现了一些有前景的能满足本地优先理念的基础技术。最值得注意的是被称为无冲突复制数据类型(CRDTs)的分布式系统算法系列。

以 CRDTs 为基础性技术

CRDTs 是在 2011 年从学术计算机科学研究中出现的。它们是通用的数据结构,就像哈希图和列表一样,但它们的特殊之处在于,它们从一开始就支持多用户。

每个应用程序都需要一些数据结构来存储其文档状态。例如,如果你的应用程序是文本编辑器,其核心数据结构是构成文档的字符数组。如果你的应用程序是电子表格,数据结构是一个包含文本、数字或引用其他单元格的公式的单元格矩阵。如果它是矢量图形应用程序,数据结构是一个图形对象的树,如文本对象、矩形、线和其他形状。

如果你正在建立单用户应用程序,你将使用模型对象、哈希图、列表、记录/结构等在内存中维护这些数据结构。如果你正在建立协作的多用户应用程序,你可以把这些数据结构换成 CRDTs。

两个设备最初拥有相同的待办事项列表。在设备1上,使用 .push() 方法将一个新的项目添加到列表中,该方法将新的项目附加到列表的末尾。同时,第一个项目在设备2上被标记为完成。在两个设备进行通信后,CRDT自动合并状态,使两个变化都生效。

上图显示了一个由具有 JSON 数据模型的 CRDT 支持的待办事项列表应用程序的例子。即使是在离线状态下用户也可以在他们的本地设备上查看和修改应用程序的状态。CRDT 会跟踪所做的任何修改,并在有网络连接时在后台与其他设备同步这些修改。

如果状态在不同的设备上被同时修改,CRDT 会合并这些变更。例如,如果用户在不同的设备上同时向待办事项列表添加新的项目,合并后的状态会以一致的顺序包含所有添加的项目。对不同对象的并发变化也很容易被合并。CRDT 不能自动解决的唯一变化类型是当多个用户同时更新同一对象的同一属性时;在这种情况下,CRDT 会跟踪冲突的值,并让它由应用程序或用户来解决。

因此,CRDTs 与 Git 这样的版本控制系统有一些相似之处,只是它们操作的数据类型比文本更丰富。CRDTs 可以通过任何通信渠道(例如,通过服务器,通过点对点连接,通过本地设备之间的蓝牙,甚至在 U 盘上)同步其状态。CRDT 可以追踪每一次按键,从而实现 Google Docs 式的实时协作。你也可以汇集更多改动,并把它们作为一个批次发送给合作者,这就像是 Git 中的 Pull Request。因为数据结构是通用的,所以我们可以开发通用的工具来存储、通信和管理 CRDTs,使我们不必在每个应用中重新实现这些东西。

对于 CRDTs 的更多技术介绍,我们建议以下内容:

Ink & Switch 已经开发了一个开源的 JavaScript CRDT 实现,名为 Automerge。它是基于我们早期对 JSON CRDTs 的研究。然后,我们将 Automerge 与 Dat 网络栈相结合,形成了 Hypermerge。我们并不声称这些库完全实现了本地优先的理想—仍然需要更多的工作。

然而,基于我们对它们的经验,我们相信 CRDTs 有可能成为新一代软件的基础。就像分组交换是互联网和网络的促成技术,或者电容式触摸屏是智能手机的促成技术一样,我们认为 CRDTs 可能是本地优先软件的基础技术。

Ink & Switch 原型

虽然学术研究在设计 CRDT 的算法和验证其理论正确性方面取得了很好的进展,但到目前为止,这些技术的工业应用相对较少。此外,大多数工业 CRDT 的使用都是在以服务器为中心的计算中,但我们相信这项技术在客户端的创造性工作的应用中有着巨大的潜力。

使用 CRDTs 的以服务器为中心的系统包括 Azure Cosmos DB、Redis、Riak、Weave Mesh、SoundCloud 的 Roshi 和 Facebook 的 OpenR。然而,我们对 CRDTs 在终端用户设备上的使用最感兴趣。

这也是我们的实验室开始进行一系列实验原型的动机,这些实验原型是建立在 CRDTs 上的具有协作性的本地优先应用。每个原型都以现有的创造性工作的应用程序(如 Trello、Figma 或 Milanote)为模型,提供终端用户体验。

这些实验探索了三个方面的问题。

  • 技术的可行性。CRDTs 在多大程度上可以用于工作软件?我们需要什么来进行网络通信,或安装软件的开始?
  • 用户体验。本地优先的软件使用起来感觉如何?我们能否在没有权威的集中式服务器的情况下获得类似 Google Docs 的无缝实时协作体验?对于源代码以外的数据类型,类似 Git 的、离线的、异步的协作体验如何?一般来说,没有集中式服务器,用户界面会有什么不同?
  • 开发者体验。对于一个应用程序的开发者来说,使用基于 CRDT 的数据层与现有的存储层(如 SQL 数据库、文件系统或 Core Data)相比如何?分布式系统是否更难编写软件?我们是否需要模式和类型检查?开发人员将用什么来调试和反省他们应用程序的数据层?

我们使用 Electron、JavaScript 和 React 建立了三个原型。这给了我们快速开发能力,同时也给了我们的用户一个可以下载和安装的软件,我们发现这是本地优先的一个重要部分。

看板

Trellis 是一个以流行的 Trello 项目管理软件为模型的看板

Trellis 提供了一个类似于 Trello 的本地优先软件的体验。右边的修改历史反映了所有在该文件中活跃的用户所做的修改

在这个项目中,我们尝试使用 WebRTC 作为网络通信层。

在用户体验方面,我们设计了一个初级的 “变更历史”,其灵感来自于 Git 和 Google Docs 的“查看新的变更”,允许用户查看他们看板上的操作。这包括回溯时间,查看文件的早期状态。

通过演示视频观看 Trellis 的操作,或者下载亲自尝试。

协作绘画

PixelPusher 是一个协作绘图程序,为 Javier Valencia 的 Pixel Art to CSS 带来类似 Figma 的实时体验。

多人实时同步地绘制。顶部的 URL 提供了与其他用户分享该文件的快速方法。右边的 “版本”面板显示了当前文档的所有分支。箭头按钮提供了分支之间的即时合并

在这个项目中,我们通过 Dat 项目的点对点库进行了网络通信的实验。

用户体验实验包括用于文档共享的 URL,受 Git 启发的可视化分支/合并工具,用红色突出冲突像素的冲突解决机制,以及通过用户绘制的头像的基本用户身份。

阅读完整的项目报告下载亲自尝试。

多媒体画布

PushPin 是一个类似于 MiroMilanote 的混合媒体画布工作空间。作为我们在 Automerge 上建立的第三个项目,它是这三个项目中实现得最充分的一个。我们的团队和外部测试用户的实际使用对底层数据层造成了更大的压力。

PushPin的画布混合了文本、图像、讨论线程和网络链接。用户通过工具栏上的头像看到对方,并使用URL栏在他们自己的文件之间导航

PushPin 探索了嵌套和连接的共享文档、CRDT 文档的各种渲染器、更先进的身份系统(包括用于共享的 “发件箱”模型),以及对共享短暂数据的支持,如选择高亮。

观看 PushPin 的演示视频下载亲自尝试。

发现

我们开发 Trellis、PixelPusher 和 PushPin 三个原型的目的是为了评估技术可行性、用户体验以及本地优先软件和 CRDTs 的开发者经验。我们通过在开发团队(由五名成员组成)中定期使用这些原型,对我们开发软件的经验进行批判性的反思,以及对大约十名外部用户进行单独的可用性测试来测试这些原型。外部用户包括专业设计师、产品经理和软件工程师。我们没有遵正式的评估方法,而是采取了一种探索性的方法来发现我们原型的优点和缺点。

在这一节中,我们概述了我们从建立和使用这些原型中得到的教训。虽然这些发现有些主观,但我们相信它们包含了有价值的见解,因为我们比其他项目在基于 CRDT 的本地优先的成熟产品道路上走得更远。

CRDT 技术管用

从一开始,我们就对 Automerge 的可靠性感到惊喜。我们团队中的应用程序开发人员能够相对容易地整合该库,而且数据的自动合并几乎总是直接和无缝的。

离线工作的用户体验非常出色

下线,继续工作,然后重新连接,与同事同步,这一切都运行良好。当系统上的其他应用程序抛出错误(“离线!警告!“)并阻止用户工作时,本地优先的原型仍正常工作,不受网络状态的影响。与基于浏览器的系统不同,它永远不需要对应用程序是否能工作或数据是否在那里感到焦虑。这让用户对他们的工具和他们的工作有一种「拥有」的感受,正如我们所希望的那样。

结合 CRDT 与 Functional Reactive Programming (FRP) 后的开发体验不错

React 的 FRP 模型与 CRDTs 很适合。基于 CRDTs 的数据层意味着用户的文档同时获得来自本地用户的更新(例如,当他们在文本文档中输入时),但也来自网络(当其他用户和其他设备对文档进行更改时)。

因为 FRP 模型能可靠地同步「应用程序的可见状态」与「共享文档的底层状态」,开发者可以从同步视图状态和文档状态的繁琐工作中解脱出来。另外,通过确保对底层状态的所有改变都是通过一个单一的函数(一个 “reducer”)进行的,很容易确保所有相关的本地改变都被发送给其他用户。

这使应用开发者毫不费力地让所有原型都实现了实时协作和离线使用能力。这是显著的优势,因为它使应用程序开发人员能够专注于他们的应用程序,而不是解决数据分发的挑战。

冲突并不像我们担心的那样是一个重大问题

我们经常被问及自动合并的有效性,许多人认为需要特定的应用冲突解决机制。然而我们出乎意料地发现用户在与他人协作时,很少遇到编辑冲突,而通用的冲突解决机制则运作良好。这方面的原因是:

  1. Automerge 在一个细粒度的层面上跟踪变化,并考虑到数据类型语义。例如,如果两个用户同时在一个数组的相同位置插入项目,Automerge 将这些变化结合起来,以确定的顺序定位这两个新项目。相比之下,像 Git 这样的文本版本控制系统会将这种情况视为需要手动解决的冲突。
  2. 用户对人类的协作有一种直观的感觉,并避免与他们的合作者产生冲突。例如,当用户合作编辑一篇文章时,他们可能会事先约定在一段时间内由谁来处理哪个部分,并避免同时修改同一个部分。
  3. 当不同的用户同时修改文档状态的不同部分时,Automerge 会干净利落地合并这些修改,没有任何困难。以看板应用为例,一个用户可以在一张卡片上发表评论,另一个用户可以将其移动到另一列,合并后的结果将反映这两个变化。只有当用户同时修改同一对象的相同属性时才会产生冲突:例如,如果两个用户同时改变画布上同一图像对象的位置。在这种情况下,如何解决它们往往是任意的,无论哪种方式都令人满意。

Automerge 的数据结构带有针对并发变化的默认解决策略。原则上,人们可能期望不同的应用需要不同的合并语义。然而,在我们开发的所有原型中,我们发现默认的合并语义已经足够了,而且到目前为止我们还没有发现任何需要定制语义的情况。我们假设情况普遍如此,我们希望未来的研究能够进一步检验这一假设。

将文件历史可视化是很重要的

在分布式协作系统中,另一个用户可以在任何时候向你传递任何数量的变化。与集中式系统不同的是,服务器对变化进行调解,本地优先的应用程序需要为这些问题找到自己的解决方案。如果没有合适的工具,就很难了解一个文件是如何形成的,文件有哪些版本,或者各部分都是由谁编写的。

在 Trellis 项目中,我们尝试使用了一个 “时间旅行”的界面,允许用户回到过去,查看合并后文件的早期状态,并在收到其他用户的修改时自动突出最近的修改内容。以线性方式穿越潜在的复杂的合并文件历史的能力有助于提供上下文,并可能成为理解协作的通用工具。

URL 是很好的分享机制

我们试验了许多与其他用户共享文件的机制,发现受网络启发的 URL 模型对用户和开发者来说最有意义。URL 可以被复制和粘贴,并通过电子邮件或聊天等交流渠道进行分享。秘密 URL 之外的文件的访问权限仍然是开放的研究问题。

Peer-to-peer 系统从未完全 “在线” 或 “离线”,并且很难推测数据如何流动

传统的集中式系统中通常有「在线」或「离线」的状态,这些状态由每个客户端根据与服务器保持稳定网络连接的能力来定义。服务器决定了真实数据是什么。

在去中心化的系统中,我们的数据可以有万花筒般的复杂性。任何用户都可能对他们所拥有、选择分享或接受的数据有不同的看法。例如,用户对一份文件的编辑可能在飞机上的笔记本电脑上;当飞机降落,电脑重新连接时,这些修改就会分发给其他用户。其他用户可能会选择接受所有、部分或不接受这些对他们版本的文件的修改。

文件的不同版本会导致混乱。就像 Git 仓库一样,特定的用户在 “主”分支中看到的东西是他们最后一次与其他用户交流时的功能。新来的修改可能会意外地修改你正在工作的文档的一部分,但手动合并每个用户的每一个修改是很乏味的。去中心化的文档使用户能够控制他们自己的数据,但需要进一步研究以了解它对实际应用中 UI 的影响。

CRDTs 积累了大量的变化历史,产生了性能问题

我们的团队将 PushPin 用于类似 Sprint 计划的真实文档中。性能和内存/磁盘的使用很快就成了问题,因为 CRDTs 存储了所有的历史,包括逐个字符的文本编辑。这些数据会堆积起来,不能轻易地被截断,因为不可能知道什么时候有人会在离开 6 个月后重新连接到你的共享文档,并需要从那一刻开始合并修改。

注:与文章发表时相比 CRDT 的性能和内存/磁盘占用已经有了明显改善,详情可参考 CRDT benchmarks

网络通信仍然是未解决的问题

CRDT 算法只规定了数据的合并,但没有说不同用户的编辑如何到达同一台物理计算机上。

在这些实验中,我们尝试了通过 WebRTC 进行网络通信;用 Dropbox 和 USB 钥匙复制文件的 “sneakernet”实现;可能使用 IPFS 协议;并最终确定使用 DatHypercore 点对点库。

CRDTs 不需要点对点网络层;使用服务器进行通信对 CRDTs 来说是很好的。然而,为了充分实现本地优先软件的长期有效的目标,我们希望应用程序的寿命超过其供应商管理的任何后端服务,所以去中心化的解决方案是合理的最终目标。

对于原型中使用的 P2P 技术我们发现:一方面,这些技术还远未达到可以投入生产的程度。特别是 NAT 穿透,是不可靠的,这取决于用户当前连接的特定路由器或网络拓扑结构。但在另一方面,P2P 协议和去中心化网络社区所带来的可能性是巨大的。在这个已经开始依赖中心化 API 的世界里,没有互联网接入的计算机之间的实时协作感觉就像魔术一样。

云服务器在发现、备份和突发计算方面仍有其地位

像 PushPin 这样的实时协作原型可以让用户在没有中间服务器的情况下与其他用户分享他们的文件。这对于隐私和所有权来说是非常好的,但是可能会出现这样的情况:一个用户分享了一个文件,然后在其他用户连接之前关闭了他们的笔记本盖子。如果用户没有同时在线,他们就不能互相连接。

因此,服务器在本地优先的世界中可以发挥作用—不是作为中央机构,而是作为 “cloud peer” 来支持客户端应用程序(它将不在关键路径上)。例如,一个存储文件副本的 cloud peer,在其他 peer 上线时将其转发给他们,可以解决上述封闭的笔记本电脑问题。

Hashbase 是 Cloud Peer 的例子,也是 DatBeaker 浏览器的桥梁。

同样地,Cloud Peer 可以是

  • 存档/备份位置(特别是对于手机或其他存储空间有限的设备)
  • 通往传统服务器 API 的桥梁(如天气预报或股票行情)
  • 突发计算资源的提供者(如使用强大的 GPU 渲染视频)

传统系统和本地优先系统的关键区别不是没有服务器,而是服务器的职责发生了变化:它们属于支持性角色,而不是真理的来源。

你可以如何提供帮助

这些实验表明,本地优先的软件是可能的。协作和所有权并不相悖—我们可以得到两个世界的最好结果,用户也可以从中受益。

然而,基础技术仍在进行。它们对开发原型很有好处,我们希望它们在未来几年内能够发展和稳定下来,但从现实的角度来看,今天在生产环境中用 Automerge 这样的实验项目来取代 Firebase 这样的成熟产品还不可取。

如果你和我们一样相信本地优先的未来,你(和我们所有技术领域的人)可以做什么来推动我们走向这个未来?这里有一些建议。

对于分布式系统和编程语言研究者来说

本地优先的软件从最近对分布式系统的研究中受益匪浅,包括 CRDTs 和 P2P 技术。目前的研究界在提高 CRDTs 的性能和功率方面取得了卓越的进展,我们热切地等待着这项工作的进一步成果。尽管如此,仍有一些有趣的机会可以进一步开展工作。

大多数 CRDT 研究的运作模式是,所有合作者立即将他们的编辑应用于某个文件的单一版本。然而,实际的本地优先应用需要更多的灵活性:用户必须有拒绝其他合作者编辑的自由,或者对不与他人共享的文件版本进行私人修改。用户可能想决定应用哪些修改,或调整修改历史。这些概念在分布式源码控制领域被很好地理解为 “分支”、“分叉”、“重定位”等等。迄今为止,只有很少的算法和编程模型的工作来解决在多个文档版本和分支并存的情况下的问题。

还有围绕类型、模式迁移和兼容性的更深入的有意思的问题。不同的合作者可能使用不同版本的应用程序,可能具有不同的功能。由于没有中央数据库服务器,所以没有权威的“当前”数据 Schema。我们如何编写软件,使不同的应用程序版本能够安全地交互,即使是在数据格式变化的时候?这个问题在基于云的 API 设计中也有类似之处,但在本地优先中有额外的挑战。

对于人机交互(HCI)研究者来说

对于集中式系统,应用程序与服务器的“同步”状态很容易提供,也有大量案例可以参考。而在去中心化系统有很多新机会来探索 UI 上的挑战。

我们希望研究人员能够考虑如何提供在线和离线状态,或者在任何其他用户可能持有不同数据副本的系统中的可用和不可用状态。当每个人都是 peer 的时候,我们应该如何考虑网络连接的问题?当我们可以直接与其他节点同步而不需要访问互联网时,“在线”意味着什么?

“铁轨”模型。它在 GitX 中被用于可视化 Git 仓库中的源代码历史结构

当每个文件都能形成复杂的版本历史时,仅仅通过日常操作,就会出现一个尖锐的问题:我们如何将这个版本历史传达给用户?在没有中心化的真理源的情况下,用户应该如何思考版本问题,分享和接受修改,并理解他们的文档是如何变成某种样子的?今天,有两种主流的变更管理模式:一种是差异和补丁的源代码模式,另一种是建议和评论的 Google Docs 模式。这些就是我们能做的最好的了吗?我们如何将这些想法推广到非文本的数据格式?我们急切地想知道能发现什么。

虽然集中式系统在很大程度上依赖于访问控制和权限,但同样的概念并不直接适用于本地优先的环境。例如,不能阻止任何拥有某些数据副本的用户在本地修改这些数据;但是,其他用户可以选择是否订阅这些修改。用户应该如何看待共享、权限和反馈?如果我们不能从别人的电脑上删除文件,那么与人 “停止共享”是什么意思?

我们相信,集中化的假设在我们今天的用户体验中根深蒂固,而我们才开始发现改变这种假设的后果。我们希望这些开放性的问题能够激发研究人员去探索我们认为是一个未开发的领域。

对于从业者

如果你是一名软件工程师、设计师、产品经理或独立的应用程序开发人员,你能提供什么帮助?我们建议采取循序渐进的步骤来实现本地优先的未来。从给你的应用程序打分开始:

1. 快2. 多设备3. 离线4. 协作5. 长期可用性6. 隐私7. 控制权
你的应用???????

这里是改善以上特性的一些策略:

  • :激进的缓存和提前下载资源,可以防止用户在打开你的应用或之前打开的文档时看到”加载中”的状态。默认情况下信任本地缓存,而不是让用户等待网络获取。
  • 多设备:像 Firebase 和 iCloud 这样的同步基础设施使多设备支持变得相对简单,尽管它们确实引入了长期可用性和隐私问题。像 Realm Object Server 这样的自我托管的基础设施提供了替代品。
  • 离线:在网络世界中,Progressive Web Apps 提供的功能,如 Service Worker 和 App Manifests,可以提供帮助。在移动世界中,要注意 WebKit 框架和其他依赖网络的组件。通过关闭 WiFi 来测试你的应用程序,或使用 traffic shapers,如 Chrome Dev Tools 网络状况模拟器iOS 网络链接调节器
  • 协作:除了 CRDTs,更成熟的实时协作技术是 Operational Transformation(OT),例如在 ShareDB 中实现。
  • 长期可用性:确保你的软件可以很容易地导出到扁平化的标准格式,如 JSON 或 PDF。例如:大量导出,如 Google Takeout;持续备份成稳定的文件格式,如 GoodNotes;以及 JSON 下载文件,如 Trello
  • 隐私:云计算应用程序从根本上说是非私有的,公司的员工和政府可以随时窥视用户数据。但对于移动或桌面应用,要尽量向用户说明数据何时只存储在他们的设备上,而不是传输到后端。
  • 控制权:用户能否在你的应用程序中轻松地备份、复制或删除他们的部分或全部文件?这通常需要重新实现所有基本的文件系统操作,就像 Google Docs 对 Google Drive 所做的那样。

对初创企业的呼吁

如果你是一个对建立开发者基础设施感兴趣的企业家,上述所有情况表明了一个有趣的市场机会: “Firebase for CRDTs”。

这样的创业公司需要提供优异的开发者体验和在本地持久化的库(类似于 SQLite 或 Realm)。它需要适用于移动平台(iOS、Android)、本地桌面(Windows、Mac、Linux)和 Web(Electron、Progressive Web Apps)。

用户控制、隐私、多设备支持和协作都将被纳入其中。应用程序开发人员可以专注于构建他们的应用程序,因为他们知道最简单的实施路径也会给他们在本地优先的记分卡上打上最高分。作为检验你是否成功的试金石,我们建议:即使所有的服务器都关闭了,你的所有客户的应用程序是否能继续永久地工作?

我们相信,随着 CRDTs 时代的到来,“Firebase for CRDTs” 的机会将是巨大的。如果你正在做这方面的工作,我们想听听你的意见。

总结

计算机是人类有史以来最重要的创造性工具之一。软件已经成为我们完成工作的方式,也是工作的储存库。

为了追求更好的工具,我们将许多应用程序转移到云端。云计算软件在许多方面都优于“老式”软件:它提供了可协作的、永远最新的应用程序,可以从世界任何地方访问。我们不再担心我们正在运行的软件是什么版本,或某个文件在哪台机器上。

然而,在云中,数据的所有权属于服务器,而不是用户,因此我们成了自己数据的借用者。在云应用中创建的文件注定会在这些服务的创建者不再维护它们时消失。云服务是无法长期保存的。没有网站时光机可以恢复一个没落的网络应用。互联网档案馆不能保存你的谷歌文档。

在这篇文章中,我们为未来的软件探索了一条新的发展道路。我们展示了用户有可能保留对其数据的所有权和控制权,同时也受益于云相关的功能:无缝协作和从任何地方访问。鱼和熊掌可以兼得。

但还需要更多的工作才能将本地优先应用到实践中。应用程序开发人员可以采取渐进的步骤,例如改进离线支持和更好地利用设备上的存储。研究人员可以继续改进本地优先软件的算法、编程模型和用户界面。企业家们可以开发基础技术,如 CRDTs 和点对点网络,以驱动下一代应用。

今天,创建一个服务器掌控所有所有数据的网络应用是很容易的。但要建立尊重用户所有权和代理权的协作软件却太难了。为了改变这种平衡,我们需要改进开发本地优先软件的工具。我们希望你能加入我们。

我们欢迎你的想法,问题,或批评:@inkandswitchhello@inkandswitch.com

致谢

Martin Kleppmann 得到了波音公司的资助。感谢我们在 Ink & Switch 的合作者,他们参与了上面讨论的原型。Julia Roggatz, Orion Henry, Roshan Choxi, Jeff Peterson, Jim Pick, 和 Ignatius Gilfedder。也感谢 Heidi Howard、Roly Perera,以及 Onward! 的匿名审稿人对本文草稿的反馈。

本翻译也被发表至知乎见相关讨论


Profile picture

Written by  Zixuan Chen