可塑软件:在封闭应用的世界中恢复用户自主权
可塑软件主张让用户以最小摩擦改造工具,而非被迫迁就封闭应用,Ink & Switch 综合数十年研究探索本地共同创造的计算系统。
中文
复制

个人计算最初的承诺是一种新的黏土——一种用户可以随意重塑的可塑材料。结果我们拿到的是家电:在远方造好、密封、不可更改。当工具不按你需要的方式工作时,你只能提交反馈,然后听天由命。你被迫调整自己的工作流程去迁就软件,而本该反过来。
本文设想一种可塑软件:用户可以以最小的摩擦重塑工具,以适应自己的独特需求。修改成为常态,而非例外。适配发生在使用现场,而不是通过遥远公司里的工程团队。
要实现这一愿景,需要面对诸多障碍。从编程语言到操作系统和应用商店,现代计算图景的每一层都建立在同一个假设之上:用户是被动的接受者,而不是主动的共同创造者。我们需要的恰恰相反——一种邀请每位用户逐步成为创造者的计算系统。工具能组合成自定义的工作流,而不是彼此孤立的单体应用。系统能支持本地群体进行共同创造。
幸运的是,可供借鉴的东西很多——过去几十年间的研究成果和商业产品都是。本文综合了这些工作中的想法,以及我们多年来测试自己原型所得到的经验。无论你是软件开发者、研究者,还是任何一种计算机用户,我们邀请你与我们一道,朝着可塑软件的世界努力。
引用本文请使用:
Geoffrey Litt, Josh Horowitz, Peter van Hardenberg, and Todd Matthews. 2025. Malleable Software: Restoring User Agency in a World of Locked-Down Apps. Ink & Switch. https://www.inkandswitch.com/essay/malleable-software/.
动机
我们想改造自己的环境
环境很重要。要做出最好的工作、过上最好的生活,我们需要能让我们各自发挥独特潜力的空间。
吉他制作者布置自己的工坊,把锯、锤、凿和锉刀摆得恰到好处。他们还能按需打造新工具以取得最佳效果——一块木头顶作支撑,或者把一把钳子打磨成合适的形状。

一个家庭厨师会年复一年地慢慢攒齐一套刀具、砧板和锅具,从中挑出最顺手的那几件。他们可以在天花板上装挂钩,挪动架子,让厨房配合自己的动线——无论是做工作日的晚饭,还是张罗周末的大餐。

这些都是再日常不过的事。在物理世界里,我们改造环境是件自然而然的事,因为物理现实是可以揉捏的。
很多小调整——往墙上贴张便利贴、重新归置几个抽屉、挪一件家具——立刻就能做,不用问任何人。我们也可以做更大的改动,那需要更多力气和手艺,比如搭一间工坊、翻新一个厨房。自己不会,还能找本地的手艺人帮忙。
当我们在自己掌控的物理空间里工作和生活时,总会把它一点点改造成适合自己需要的样子。Stewart Brand 在《How Buildings Learn》中写道:“建筑之所以被人喜爱,靠的是年头加上可改造性。建筑从使用者身上学习,使用者也从建筑身上学习。”
批量生产的软件太死板
如今,我们越来越多的时间待在由代码而非原子构成的环境里。这一转变带来了很多能力——我们可以跨越大洲即时协作,可以瞬间检索成千上万个文件。但我们也正在失去一样重要的东西:改造自己的环境、让它真正属于自己的能力。
举个例子。本书作者之一所在的软件团队,曾用贴在墙上的索引卡来跟踪工作进度。这个看板一直在变——分隔线挪了位置,清单冒了出来,主网格周围长出一片片特殊的卡片区域。工具的流动性,带动了流程的流动性。

一面索引卡墙,是管理软件开发的一种可揉捏的媒介。
后来,团队改用基于网页的问题跟踪器来支持远程协作者。这样一来,就无法再建模或显示那个特殊的卡片区域了,于是团队放弃了流程中的这一部分。后续的流程改动也一并停摆。以前试个新想法只要几分钟,现在却可能要花上几个小时去折腾配置,甚至根本做不到。把工作计算机化,反而让人失去了自主权。
软件的僵化不只是小麻烦,它会严重妨碍人们做重要的工作。医生兼作家 Atul Gawande 曾写道,医疗行业的计算机化正导致职业倦怠达到历史新高。比如,过去医生填纸质表格时可以跳过无关字段,现在软件逼着他们填,而他们无权修改软件的规则。Gawande 提到一位医生时说:“多花时间并不让她生气,让她生气的是这些时间毫无意义。”

僵化的电子病历系统正把医生推向职业倦怠。
当软件满足不了你的需求时,你可能会试着向开发者反馈——但这通常不会带来立竿见影的改变。不同用户的需求各不相同,一个集中的开发团队不可能解决所有人的问题。 而且,当开发者真想把太多解决方案塞进同一个产品时,结果只会是一团臃肿的乱麻。为了避开这个陷阱,优秀的产品团队学会了拒绝大多数用户请求,留下一大堆无人问津的小众需求。
我们的具体需求得不到软件满足,这看起来似乎不可避免——但这只是因为我们想当然地认为软件就该由集中的开发团队掌控。如果把更多控制权交到那些清楚自己需求的用户手里,会怎样?
Gawande 讲过一个神经外科医生的故事:他和一位 IT 分析师合作,改造了所在科室的病历系统。“没过多久,他们就做出了一个更快、更直观的界面,专为神经外科门诊设计。”这些需求来自他们那个具体科室,而不是全国每一位医生。除了直接的生产力提升,医生们还觉得自己更能掌控手中的工具——这正是职业倦怠的一剂解药。
这个故事固然鼓舞人心,但它更像是例外而非常态,因为我们用来部署软件的工具和基础设施,把用户当作被动的接受者,而不是主动的共同创造者。软件被组织成铁板一块的单体应用,而不是灵活、可重组的工具集。想要定制,就得会编程,而大多数人并不具备这项技能——更何况,多数软件都是闭源的。软件交付到用户手里时,并不附带编辑软件的工具。应用商店是为那些向消费者分发软件的公司设计的,而不是为把工具分享给朋友的业余爱好者设计的。这是一套工业化的批量生产体系,不是小规模的手工制作。
平心而论,批量生产的软件带来了许多好处。我们能以合理的价格用上大量打磨精良的应用。软件在可靠性、可访问性和安全性上都有进步。开发者创造出了可持续的商业模式,足以养活团队,让他们持续交付不断改进的软件。
但正如这些故事以及无数其他例子所示,僵化的批量生产软件同样会碍事。你越是偏离平均用户,定制带来的好处就越压过专业打磨带来的好处。每个人都在某些方面与众不同——也许你对写作、做音乐、讨论交流或规划项目的工具有强烈的偏好。当你有具体需求时,自主权就很重要。
我们的目标:可塑软件
我们设想一种新的计算生态,让用户作为共同创造者拥有自主权。我们把这个理念称为可塑软件——一种软件生态,任何人都能以最小的摩擦让自己的工具适应自己的需求。
- 所谓“软件生态”,我们指的是围绕软件及其用户的广泛技术环境与文化环境。可塑性不是一个狭义的技术问题。
- 所谓“任何人”,我们指的是广泛的易用性才是目标。培养个人的自给自足固然有用,与本地社区协作同样有价值。
- 说到“让工具适应”,我们涵盖的是一整系列定制,从对现有软件做小修小补,到深度改造,再到创造能与现有工具良好协同的新工具。适应并不意味着从头再来。
- 最后,“最小的摩擦”是关键。编辑我们的工具应当很快,应当感觉轻巧。最好的情况是,需求一出现我们就能随手改好,然后回到手头的事情上。
现有方案
你可能会想:那设置和插件呢?确实,定制软件的手段有很多,值得称道。但它们同样有局限,无法完全实现我们上面列出的目标。
设置
设置是改变应用行为的常见方式。如果有现成的设置项,勾选一下就能继续干活。
但设置只提供应用开发者预先想到要暴露的选项。如果没有一个设置项能满足你的需求,你就卡住了。设置还容易变成一堆互不相干的复选框,背后没有一个连贯的心智模型把它们串起来。

设置让用户能做应用开发者预先选定的某些定制。
插件
要突破中心开发者的精力上限,一种办法是允许第三方插件扩展应用的行为。好的插件系统让用户上手定制的门槛很低,因为可以直接安装别人做好的插件。插件 API 还有一个关键好处:它稳定了底层应用与各种扩展之间的契约,便于长期维护。
然而,插件系统仍然只能以特定的、被授权的方式修改应用行为。 如果某个定制需求没有对应的插件接口,用户就无计可施。(事实上,大多数应用根本没有插件 API,因为设计一套好的插件 API 是件苦差事!)
还有别的问题。从安装插件到制作插件之间隔着一道难以跨越的鸿沟。而且每个应用都有自己的一套插件系统,插件通常无法在不同应用之间通用。

Obsidian Markdown 编辑器拥有丰富的社区插件生态,可用来扩展编辑器的行为。
修改(Modding)
当设置项和官方扩展 API 不够用(或者压根没提供)时,用户有时可以通过无需许可的 modding 自行接管。比如浏览器扩展可以介入网站的用户界面,注入新的客户端行为,完全不需要原应用开发者暴露任何 hook。
无需许可的 mod 适用的场景比官方支持的插件 API 宽得多。事实上,即使原开发者明确反对某类扩展,mod 照样能做——广告拦截器就是把用户利益置于网站利益之上。
Mod 也有自己的局限。做 mod 往往需要繁琐的逆向工程。底层应用一演进,mod 就难以维护。同一个应用上的不同 mod 常常没法干净地共存。
底层平台的能力边界也会限制 mod 能做的事——比如浏览器扩展改不了服务端行为,能提供的功能因此大打折扣。而且和其他插件一样,不受官方支持的 mod 在安装和制作之间存在巨大的鸿沟。比如要做一个浏览器扩展,你得离开浏览器、写代码、再分发打包好的代码产物。(后面我们会讨论一些绕过这些步骤的策略。)

一个满足小众需求的浏览器扩展示例:BoA Checklist 在 Bank of America 网站的每笔交易旁加一个常驻复选框,用来记录你是否已经核对过这笔交易。
开源
开源软件运动提倡分发源代码,让软件的用户能够真正拥有自己的软件、回馈社区,必要时还能做出更符合自身需求的版本。这对世界是股正向力量,也是可塑性的一项重要来源。
但能编辑代码,并不等于摩擦就小了。改动一个正经的开源代码库,通常需要相当的专业能力和投入,哪怕只是改个按钮颜色这样的小事。就算对熟练的程序员来说,搭好开发环境、熟悉一个代码库,这道坎也高得让人不会随手就去动它。

用户可以在 GitHub 上给开源项目提改动建议。
AI 辅助编程
最后,值得谈谈当下的一个热门话题:AI 工具会自动让软件变得可塑吗?
从历史上看,让普通人也能写程序,一直是软件创作民主化最难解的谜题之一。人们试过很多路子:语法更友好或语义更简单的编程语言;通过演示录制宏的系统;可视化实时编程环境,等等。
如今,大语言模型提供了新思路:把用自然语言表达的模糊想法自动变成代码。一股势头正在形成——任何人都能靠一段对话生成一个 Web 应用,不需要任何编程经验。这样的例子每天都在冒出来:有记者做了个生成学校午餐点子的应用,有环境研究专业的学生做了个追踪植树造林进展的网站。我们自己就觉得用 AI 搭软件工具很顺手,比如一个专门的日语翻译应用,还有一个极简的锻炼计时器,配一套自定义训练计划:

在 Claude Artifacts 里用 AI 搭一个锻炼计时器工具
我们认为这些进展潜力可观,也正是此刻去做可塑软件的一个好理由。但与此同时,单靠 AI 生成代码,并不能扫清可塑性面临的所有障碍。 就算假设每个电脑用户都能完美地编写和修改代码,仍有一些大问题悬而未决。
用户如何调整他们已安装的现有工具,而不是只能另起炉灶做新的孤立应用?AI 生成的工具之间如何相互组合,在共享数据之上搭建出更大的工作流?如何让用户对软件的调整拥有更直接、更精细的控制,而不必为了哪怕最微小的改动都去求助 AI 写代码?那些根据一句提示词生成云端托管应用的产品,一个都没有回答这些问题。
把 AI 编程工具带进今天的软件生态,就像把一位厨艺高超的副主厨带进美食广场。 如果你习惯了照着菜单点餐,再好的厨师也帮不上什么忙。同理,如果你用的是应用商店里的闭源软件,AI 编程助手作为用户也帮不了你多少。要充分利用 AI 的能力,我们得走出美食广场,走进更像厨房的地方——一个可以开放地创造东西的场所。
围绕用户自主权重塑计算的基础,是个雄心勃勃的目标。好在自个人计算诞生以来,已经有很多人提出过实现它的有力构想。
接下来几节,我们总结三种设计模式,我们认为它们是实现可塑软件的关键。这些不是解决问题的精确配方,但我们相信,它们是当今软件世界中未被充分重视的重要思路,也将构成实现可塑软件的一部分答案。
从用户到创作者,坡度要缓
可塑软件并不意味着每个人都要从零开始造自己的全部工具。那太浪费时间了,因为很多现成的工具已经相当接近我们的需求,只需稍作调整。
更合理的做法是:一开始先用别人或别的公司做好的现成软件工具,但当你发现它们满足不了自己的需求时,保留修改它们的选项。你可以从被动的用户起步,逐步成为编辑者和创作者。
1990 年的论文 User-Tailorable Systems: Pressing the Issues with Buttons 中,EuroPARC 的 Allan MacLean 及其合作者提出了一个很有力的心智模型,用来设计那种能让用户逐步变成创作者的软件系统。
看这张图,横轴代表定制的力度或深度,纵轴代表完成这种定制所需的技能水平。在这个模型里,只要用户为了达到下一层定制深度而不得不突然多学一大堆东西,图上就会出现一道垂直的“悬崖”。

从改设置直接跳到完整编程是一道陡峭的“悬崖”,很难跨越。(图来自 MacLean 等人)
为了削平这些悬崖、让进阶变得平滑,MacLean 等人提出了一个设计目标:定制能力每提升一档,所需投入的学习和技能也只应增加一小档。 遵循这条规则的系统,可以画成一条没有悬崖的“缓坡”。

一条由多种定制技术构成的定制能力缓坡。(图来自 MacLean 等人)
研究者在一个叫 Buttons 的自动化系统里验证了这个原则。最初,用户只能挪动按钮、改改文字或颜色。沿坡往上一点,用户可以编辑变量值,或者用工具包搭出简单的界面。到了坡顶,用户才能通过写 Lisp 程序来创造新的行为。关键在于,每一种定制都尽量用最简单的技术完成,完整编程只在万不得已时才动用。
许多成功的终端用户可塑环境都采用了缓坡模式。电子表格就是一例。别人发给你一张复杂的表格,你可以先只是看看,或者改一改标为输入的单元格。再进一步,你可能会调整格式、加几个标签。用得越深,你可能开始改公式,或者自己加新的单元格。电子表格一开始用起来像是一个“应用”——别人拼装好的成品,留了几个旋钮给你控制——但它和应用不同,你可以平滑地走向更深的定制。

电子表格提供了一条平缓的定制坡道,从使用通向更深入的修改。
在如何实现平缓坡道这件事上,电子表格给了我们几点有用的启示。第一,电子表格自带一套就地工具链:每个使用电子表格的人运行的都是完整的编辑器。想更深入地去修改电子表格时,不需要安装或打开一个单独的开发环境。
另外,电子表格往往有一种草莽气质。相比专业设计应用那种像素级打磨的精致感,改动一张电子表格在感觉上更安全。
实现平缓坡道的另一个办法是显式模式。HyperCard 是 1980 年代的一款 Mac 程序,用户可以在其中创建一叠叠“卡片”。它提供了若干“层级”,规定用户在任何时刻可以执行哪些操作。

在 HyperCard 中,用户可以选择某个“层级”来限制自己的编辑能力。
第 1 层是只读的;第 2 层和第 3 层支持文本与图形编辑,采用直接操作的交互方式;第 4 层增加了创建按钮和把东西连接起来的能力;第 5 层则解锁完整编程,用的是一门叫 HyperTalk 的脚本语言。这些显式模式充当护栏,让用户能安全地探索,而不必担心产生意外后果。

HyperCard 的每一层都代表着能力与技能上的一小步。
最后,我们来看看把悬崖抹平是什么样子。前面讨论过,从安装浏览器扩展到亲手做一个扩展之间,横着一道巨大的鸿沟。

从安装浏览器扩展走到制作浏览器扩展是一道陡峭的悬崖。它要求你离开浏览器,去做一套繁琐的编程。
如果我们为定制网站引入一些更轻量的做法,就能得到一条平缓得多的坡道。Stylish 和 Arc Boosts 这类工具让用户可以在浏览器内创建并分享自定义样式主题。再往曲线上面走,宏系统让用户可以录制并回放网站上执行的操作。而如果用户懂一点 JavaScript,就可以直接在浏览器里改 Tampermonkey 写的用户脚本。

Arc Boosts 让用户直接在浏览器中通过直接操作交互来重新设计网站样式。
这些技术在能力与技能门槛之间各有取舍。如果用户只是想调整网站上的某个颜色,就应该能在一个表达力较弱、专注于这项任务的系统里完成,而不必去写一整个浏览器扩展。有了这些技术,我们可以把这条坡道变得更平缓:

网页定制技术在使用浏览器扩展之前,提供了一段更平缓的坡道。
让坡道更平缓有两种方式。一种是从图的右侧出发,降低所需的技能。这一策略对应着大量工作,它们从编程的完整表达力出发,让它变得更易上手——包括更友好的语法、实时编程环境、从示例推断程序、渐进式脚手架等等。
不过还有另一种同样重要、却常被忽视的策略:从图的左侧出发,逐步提供更多能力。许多成功的可定制系统,比如电子表格、HyperCard、Flash、Notion 和 Airtable,都遵循着类似的模式:一个带有可选编程能力的媒体编辑器。当一个环境提供文档编辑和熟悉的直接操作交互时,用户不写任何代码也能完成很多事。
最后一点:不是每个人都需要爬到坡顶。到了某个阶段,许多业余用户会请别人帮忙,以节省时间和精力,或者避免去学那些必要的技能。关键在于,只要投入合理的学习和技能增量,许多定制对业余用户来说都是可及的。平缓的坡道也有助于不同技能水平的人之间更顺畅地协作。我们稍后讨论共同创作的重要性时,会再展开这一点。
工具,而非应用
到目前为止,我们关注的都是单个应用的僵化。但软件之所以难以按我们的需求调整,还有另一个原因:“应用”这个概念本身。我们对这个想法太习以为常,反而很难看清问题出在哪——不妨拿厨房打个比方。
应用就是牛油果切片器
切牛油果的一种办法是用“牛油果切片器”:一件三合一的小工具,把三样东西凑在一起——一把钝塑料刀,用来把牛油果切成两半;一个圆形挖核器,用来取出果核;还有一排塑料条,一次切出 7 片。

牛油果切片器只服务于一种用途。
谁都能上手,不用练,也没有安全风险。但正因为牛油果切片器只盯着这一件事,干别的它一概不行。要是每件事都配一件专用工具,最后你家里会堆起一座塑料山。

从剥玉米到切生菜,几乎任何一件事都能找到对应的专用工具。
另一种办法是用刀。切牛油果的每一步它都能干,而且远不止于此:切鸡胸、剁洋葱、拍蒜瓣,都不在话下。你确实得学会安全、熟练地用刀,但这功夫值得花,因为刀是通用工具。

一把刀能切牛油果,还能干更多事。
这个类比放到软件上意味着什么?很多应用就是牛油果切片器。 它们是一堆功能的打包,瞄准某个具体用途:规划行程、记录训练、整理菜谱。由于一个应用要应付某个用途下的诸多任务,有时反而哪一件都做不好。你可能遇到过这种情况:某个应用缺了你很看重的一项功能,同时又塞了一堆你根本用不上的东西。
更重要的是,用多个应用完成一项较大的任务,往往需要人工协调。我们可以把窗口并排放置、复制粘贴数据,但也就仅此而已。想要更趁手的软件工具,就得让更小的工具之间更好地协作。

规划一次旅行,需要在许多彼此隔离的应用之间手动协调。
这并不是说应用模型一无是处。在单个应用的范围内,用户能获得连贯的体验,因为开发者可以精细打磨它,而不必操心与其他工具的互操作。按应用隔离数据,也是应对某些安全与隐私问题的便捷方案。为应用付费(以及为支持付费)是一种合理的计费模式。这些都是不容忽视的重要好处。
但当你试图解决一个复杂问题,或搭建一条横跨多个应用的创造性工作流时,应用之间的隔离就会带来严重麻烦。我们能否围绕更通用、可组合的工具来重新组织软件——让它更像一把刀,而不是牛油果切片器?这里有两个子问题需要解决:工具之间如何共享数据,以及如何在用户界面中组合工具。
工具之间共享数据
如果我们打算把不同的软件工具协同起来完成一项工作,这些工具就必须能操作同一个共享的现实。
在现代云平台和移动平台上,每个应用都在自己的私有孤岛里管理数据。一次旅行的计划散落在笔记应用、Google Maps 列表和日历里。这种碎片化妨碍了可塑性。当每个应用各自管理数据时,把一个新工具引入工作流就会带来更多摩擦。人们宁可留在现有应用里把整件事做完,哪怕完成其中某一部分存在更好的替代工具。
反过来,当数据在应用之间共享时,终端用户就能以更灵活的方式组合工具。一个众所周知的例子是桌面文件系统。当信息以文件形式存储时,你可以在专注于不同子任务的不同工具中编辑同一个文件。你可以在 Lightroom 里调整照片的明暗,再用 Apple Preview 在照片上随手写个批注。不同的协作者也可以使用各自偏好的工具——一个人可以在 VSCode 里编辑代码仓库,同时与另一位用 emacs 编辑它的同事协作。

文件是不同工具之间的共同基础。
工具在共享数据上运行的另一个例子是低代码数据库应用搭建平台——包括 Microsoft Access、Filemaker Pro 这类桌面应用,以及 Airtable、Notion 这类云端应用。这些环境让最终用户能够为同一个数据库构建多种视图。比如 Airtable 自带网格表格编辑器、卡片看板和日历等通用工具。它也支持添加自定义 UI,但只有特殊场景才需要,因为很多任务在通用网格编辑器里就能完成。
在 Airtable 中用多种视图编辑同一份数据,并具备实时响应
共享数据这一思路同样可以延伸到带有行为的共享对象。 在 Smalltalk 中,用户面对的是一个 image:一个对象仓库,其中不仅包含持久化的状态,还包含与这些状态关联的代码。由于系统中的一切都被表示为对象,就可以引入新的工具来操作这些已有对象——包括调用方法来触发行为。
最后,基于共享数据的实时协作让不同的人能够在不同工具中同时工作。 Webstrates 就是一个例子,这是一个在浏览器中协作的可塑软件平台。在 Webstrates 中,不同用户运行的工具可以协同编辑存放在浏览器 DOM 中的共享同步数据集。于是,两个用户可以各自用自己喜欢的编辑器合作写同一篇论文,一个人用所见即所得编辑器,另一个人用纯文本界面。
同一篇论文在 Webstrates 中用两个不同编辑器实时编辑
组合用户界面

工具不仅需要共享底层数据,还需要共享一个能把它们放在一起使用的工作空间。
应用范式并不支持这一点。一个应用会占据一个窗口或标签页,并在其中构建一个世界。这有它的好处——应用的创造者可以在这个世界里精心打磨你的体验,确保一切都配合得恰到好处。但这也意味着,要同时使用多个应用,就得在彼此隔离的用户体验之间来回切换。 这就像每换一件工具,都得把食材从一个厨房搬到另一个厨房。即便数据可以互通,你的体验也是割裂的。
怎样才能既打破应用对数据的独占,又打破它对交互环境的控制?前面我们讨论过插件、无需许可的模改等做法,它们撬开缝隙,让应用能从内部被扩展。这些都不错,但如果想要一个“用工具、而不是用应用”的世界,我们还需要一点别的东西——一个能把各种工具汇聚起来、放在共享空间里使用的环境。
其中一类思路来自复合文档系统,比如 OpenDoc 和 OLE。它们让作者把多种媒体互相嵌入,组合成一份文档。比如一份文本文档里可以嵌一张图,图里又可以嵌一张数据表。选中其中某个部分,编辑器就地打开,不必在窗口之间来回切换,也不用翻遍文件系统去找。这类系统是开放的:开发者可以引入新的媒体类型,也可以为已有的媒体类型提供新的、可替换的编辑器。

OpenDoc 文档由若干“部件”组成,各部分用不同的编辑器编辑,但统一存放在一份文档里。
把 UI 拆成更小的碎片,会带来新的设计难题。用户如何为文档的每个部分选择用哪个编辑器?用户分享一份文档时,如何保证对方手上有全部所需的子编辑器?选中、焦点这类全局交互又如何跨编辑器运作?OpenDoc 这样的系统对这些问题给出了答案,值得研究,尽管现代协作应用和 Web 应用的性质也已经改变了周遭的环境。
有时候,为文档的各个部分嵌入编辑器还不够。看看 Tchernavskij 在 Designing and programming malleable software 中举的例子:用户怎样才能把一个应用里的取色器复用到另一个应用里? 取色器不像图表编辑器,它并不只对应文档的某一个部分;它是一个与文档通篇对象打交道的工具。要完成自己的工作,取色器必须以某种方式感知到其他对象,并且能够修改它们的颜色。通常,用户界面会把这类连接写死,但要想支持扩展或复用,我们需要一种更灵活、更开放的方案。

Designing and programming malleable software 中的一道挑战题:把一个应用里的取色器复用到另一个应用里。
Tchernavskij 为解决这个问题提出了 entangler:UI 系统中一个专门的层,负责动态发现并连接相关的 UI 元素。另一个研究项目 Varv 则用一组可叠加扩展的事件触发器列表来描述行为,以此解决问题。这些方案的具体做法各不相同,但共同点在于:它们都以某种方式表示 UI 各部分之间的连接,而这种表示要能支持日后的扩展。
更激进的一种 UI 组合思路,是把物理世界当作可组合性的基础,Dynamicland 就是这样做的。Dynamicland 里的程序由一组物理对象(比如纸片)构成,这些对象可以承载代码和数据,同时充当交互的把手。在 Dynamicland,你可以把若干界面摆到桌上彼此靠近,用胶带或胶水粘在一起,就完成了组合。程序还能利用空间关系——指向、邻近——在对象之间建立虚拟连接。基于屏幕的系统无法直接照搬 Dynamicland 对物理世界的运用,但至少可以从中得到一点启发:一致的物质性和物理规律本身是有价值的。

Dynamicland 中运行着程序的纸片,在桌面上拼在一起,伸出绿色的“胡须”来界定彼此的连接。
Dynamicland 的可组合性,很大程度上还来自它把程序拆成一个个协同工作的小对象。这些对象之间通过一个全局数据库通信。因此,你可以往系统里加入新工具,拦截已有对象之间的消息,从而改变已有工具的行为。当一个系统不预先假定信息的发送方和接收方之间存在某些固定连接时,用这些信息去做新用途、对系统做叠加式扩展就会更容易。
共同创造
想象每个人都能打造自己独特的计算体验,这固然令人兴奋,但用个人主义的视角看待可塑软件,走不了太远。
个人当然应该能随时对软件做点小修小补,满足自己的需求,沿着“从使用到创造的缓坡”往上爬。但想要的改动越大,做出来需要的时间和技能就越多。如果每个人都只能为自己做这些改动,一次性用完就扔,收益多半抵不上成本。不是所有人都想成为造软件的专家。
还有一个更根本的理由,让我们必须从共同体的角度看可塑性:我们是一起用电脑的。一个产品团队需要统一的项目跟踪系统,医院的一个科室需要统一的患者登记表系统。这些共同体当然不满足于自己无法掌控的通用型应用,但解决方案也不能是各人顾各人。我们应该帮助共同体构建并维护解决自身问题的共享方案。
有了合适的基础设施,我们就能协作打造自己的软件。 世界各地需求相近的人可以交换成果、协同开发,自由软件社区已经证明了这一点。而本地共同体——从公司到家庭再到民间组织——可以构建并维护适合本地需求的软件。当本地需求爬到“坡”的高处,需要特殊水平的技能和热情时,不必让共同体里的每个人都达到那个水平,只要有足够的人承担这个角色、把活干完就行。
为“本地”场景构建软件,有时比构建全球通用的软件更简单。 如果你与用户直接接触,能对他们遇到的情况及时响应,就不必做出密不透风的工业级软件;设计时也无需预判所有人的需求,只需预判自己共同体的需求。Clay Shirky 把这种模式称为“情境化软件”,他描述了学生们如何“利用社会基础设施或情境敏感信息”,为自己的共同体快速构建软件。Robin Sloan 则描述过更亲密的一层:为家人做的应用可以是一顿“家常菜”。
基于以上种种原因,我们认为,可塑软件的技术基础设施必须支撑起社会技术系统——让身处不同层级的人们协同工作,使软件服务于自己及其所在社群。
自由软件的历史为如何构建这样的社会技术系统提供了经验。尤其令我们受到启发的,是自由软件社群并不假定世界上只应存在一个由中心控制的应用程序版本。例如,由小型社群运营的 Mastodon 实例常常修改 Mastodon 的代码,以实现社群特有的功能与规则。当然,这类情况仍然需要严肃的工程投入,而且仍然是在应用程序的层面上运作。当我们走向一个通往软件修改的坡道更缓、工具多于应用的世界时,就需要弄清楚:更小的代码片段如何共享与协作开发,以及在软件多元化的世界里,互操作性如何维持。
Ink & Switch 的原型
在 Ink & Switch,我们花了数年时间构建研究原型,探索可塑软件的不同侧面。这些项目不是商业产品。每个原型的目标是加深我们对实现可塑性所需技术的理解,再从这些原型的深度使用中学习。(事实上,这篇文章正是在一个自制的可塑软件环境中写成的,我们也会分享其中的一些成功与失败。)
我们的工作覆盖了整个计算栈,大致可分为两条线索。一条线索探索的是基础基础设施——存储数据、加载代码、定义用户界面的技术,且其方式要能支撑在其之上构建可塑的体验。在更靠近栈顶的位置,另一条线索聚焦于一类特定的用户体验:动态文档,即静态媒体可以逐步被交互行为所丰富。下面依次展开。
面向可塑性的基础设施
我们已经讨论过,如今用于运行和开发软件的多数 基础设施 在设计时并未把可塑性当作优先事项。我们也展示了一些围绕可塑性重新定位的灵感来源。桌面文件系统让工具之间可以共享数据。Smalltalk、Hypercard 和电子表格允许在运行时编辑代码,从而实现实时修改。基于浏览器的应用用 DOM 描述自己的 UI,从而可以通过扩展实现无需许可的魔改。
受这些前辈启发,我们一直在为一个用于构建、运行和分享软件的基础设施栈做原型,把可塑性放在首位。 我们的方法建立在另一块研究领域之上:local-first software——这种理念优先把数据的归属和控制权交给用户,同时保留与他人协作的能力。作为这项工作的一部分,我们开发了一个名为 Automerge 的协作库,用于在用户之间持久化和同步 JSON 文档。
PushPin:可扩展的媒体画布
为了开始弄清 local-first 的基础如何支撑可塑性,我们做了一个原型:PushPin,一个基于 Web 的协作媒体画布。这个项目里我们提出的一个关键想法是“文档函数式响应式编程”(DFRP):把一个工具表示为一个用 React 编写的 UI 组件,背后是一个 JSON 文档,由 Automerge 自动持久化和同步。
相比传统应用,DFRP 让扩展界面的工作量更小,因为我们只需添加一个 UI 组件,不必操心后端数据库或 REST API。更进一步,数据与 UI 的解耦让为 已有文档 定义新界面变得直接——我们可以定义一个新组件,然后把它注册为另一种编辑器。

一个 PushPin 看板,内嵌的卡片各自实现为 React 组件,背后是各自同步的 Automerge 文档
PushPin 也暴露出几个难题。首先:在特定场景下,该用哪种 UI 来展示文档?我们试过一个简单的模型,基于硬编码的场景——“在画板上把文档渲染成卡片时显示紧凑视图;作为主内容渲染时显示展开视图”——但这种方式无法满足我们的全部需求。比如,它不允许用户根据手头的任务在多种视图之间选择。
我们还发现,嵌入模型需要在隔离与协同之间取得平衡。我们采用的是一种限制性模型:组件之间可以相互嵌入,但不允许进一步通信。这种强隔离让组件内部的行为容易推理,但 UI 组件之间无法方便地共享悬停状态之类的上下文,某些丰富的用户体验因此做不出来。无许可的魔改同样得不到很好的支持,因为没有任何机制能触及已有组件的内部。如何分发视图、如何在组件之间协同,这些问题对我们来说仍处于探索阶段。
Cambria:在工具之间调解 schema 兼容性
使用 PushPin 时,schema 兼容性是个令人头疼的问题。升级工具经常会破坏与现有数据的兼容性。而如果不同工具无法就底层 JSON 数据的格式达成完全一致,它们之间就很难互操作。这属于一类更广泛的问题——当工具无法就数据的形态达成一致时,它们该如何协作?在一个可塑的环境中,我们不能指望由某个中心权威来规定 schema。
我们做了一个名为 Cambria 的原型系统,它通过将写 schema 与读 schema 解耦来解决这一难题。每次更新由各工具按自己偏好的 schema 写入,然后在需要时按其他 schema 解释——这些 schema 可以是同一 schema 的新旧版本,也可以是其他工具使用的完全不同的 schema。这不是一次性的数据迁移,而是持续进行的实时转换,从而保留了同时按任意 schema 写入和读取的能力。
在对 Cambria 原型做了有限的测试之后,我们认为实时数据翻译是支持多元 schema 的一条有前景的路径。不过,我们还没有做出能与 Automerge 集成的生产可用版本;一个难点在于,它可能需要对底层数据引擎做深度集成。

Cambria 通过一张名为 lens 的转换图,按需在工具之间实时翻译数据。
Farm:代码即数据
PushPin 降低了向环境中贡献新工具的门槛,但系统在运行时不可编辑——代码存放在 GitHub 仓库里,通过标准的 Web 应用流水线部署。于是在下一个项目中,我们尝试让修改变得流畅:用本地优先的数据层不仅承载用户数据,也承载系统自身的代码。 我们构建了一个叫 Farm 的系统,用户可以用 Elm 语言编写工具,源代码则存放在 Automerge 文档中并保持同步。
在最基本的层面上,把代码当作数据简化了工具的分享。但协作的许多方面依然棘手。你编辑一个工具时,怎么才能不把它改坏、影响其他人使用?多个协作者又怎么能并行地处理同一个工具的不同部分?这一系列问题引出了我们的下一个项目。
Patchwork:版本控制 + 自举
可塑性(malleability)的核心,是让人能够探索和分叉。不同用户需要自由地尝试改动某个软件,或者做出多个变体,而不必担心丢失工作成果或搞乱协作者的状态。在传统的软件工程项目里,这些问题靠 Git 分支之类的版本控制概念来解决。
版本控制系统对软件工程师是有用的工具,但它们并不是为支持可塑软件而设计的。它们难以上手,面向的是正式的协作流程。我们认为,可塑软件需要新的方案:把普通用户能否用得起来放在首位,并且更好地支持随意、非正式的探索。我们把这一设想称为通用版本控制。
为了进一步探索这些版本控制理念,我们开发了 Patchwork——一个面向可塑软件的基于 Web 的协作环境。Patchwork 建立在 PushPin 和 Farm 的核心思想之上,将用户数据和软件代码都存储在 Automerge 文档中。在此基础上,它加入了历史视图、简单分支等版本控制工具。这些工具适用于系统中的任何文档——无论是一篇文章,还是某个软件工具的代码。

Patchwork 可以显示文本文档分支上的 diff,也支持其他媒体类型。
Patchwork 与我们此前的基础设施项目有一个流程上的不同:我们现在正在“自举”——我们实验室的大部分内部知识工作,从写作到白板讨论再到项目规划,都在 Patchwork 里完成。 事实上,这些文字本身,就是在一个托管于 Patchwork 的协作编辑 Markdown 文档中敲出来的。

我们在 Patchwork 里用过各种各样的工具——从文本编辑器、白板,到模拟程序、音频音序器和游戏。
用 Patchwork 做工作的过程中,我们观察到一件事:在可塑系统里,改进软件的机会会自然而然地浮现出来。 写这篇文章的过程就有一个例子。我们主要使用的工具是一个协作 Markdown 编辑器:

在 Patchwork 中编辑这篇文章
写到某个阶段,我们发现文章太长了,但光看 Markdown 编辑器很难判断各节的篇幅。于是我们做了一个极简的 Section Word Counter 工具,用来统计每一节的长度:

在 Patchwork 中,这个新工具可以直接融入现有工作流。它在一个桌面 IDE 中编写完成,然后同步进一个 Automerge 文档,无需重新构建或重新部署 Patchwork 本身。安装之后,Section Word Counter 立刻就能用来查看现有的文章文档,并且随着文章修改实时更新。

两个 Patchwork 工具可以编辑/查看同一个底层 Automerge 文档。
把代码当作数据,也让分享变得更简单。字数统计工具最初是其中一位作者为自己日常使用而开发的,只加载在他自己的 Patchwork 实例里。但后来另一位作者提出想用这个工具,它就可以像其他文档一样,通过一个链接分享出去。每个协作者都能按自己的需要,选择安装哪些工具。

通过 URL 把工具分享给协作者。(聊天是我们至今还没能迁移进 Patchwork 的一个用例。)
需求出现时,我们用类似的方式又开发了好几个工具。一个「Zoomout View」在讨论文章整体结构时很有用:

有些情况下,新工具并没有那么有用。我们做过一个「Request Tracker」,用来管理把反馈请求分配给共同作者,但最后没怎么被用起来。这倒算不上多大损失——做这个工具没花多少工夫,忽略它、卸载它也很容易。
在定制 Patchwork 的过程中,我们还发现:AI 是可塑环境的有益补充。 我们前面说过,仅靠 AI 辅助写代码并不能保证可塑性。但与可塑环境结合之后,AI 辅助开发能让你编辑自己的工具快得多。
我们借助 AI 在 Patchwork 里快速搭出了许多工具。上面提到的 Section Word Counter 就是靠 AI 辅助,在写作过程中花几分钟写出来的,不需要专门腾出时间。
可塑环境还能提供一些平台能力,让 AI 生成的软件更有用。比如:我们有一个界面,可以直接从 AI 对话中做出小型软件工具。这个 UI 表面上和 Claude Artifacts 之类的现有产品相似,但生成的工具因为身处 Patchwork 内部而获得了额外能力。它们自动支持持久化和多用户协作,还能与现有工具组合,用来编辑已有数据。

用 AI 对话在 Patchwork 里搭出的演示计时器,自带持久化和多用户同步。
Patchwork 的可塑性我们已经窥见一斑,但仍有几个大问题没解决。
一个悬而未决的问题,是如何把轻量级通用版本控制的想法完整用到代码上。 我们已经有了一套基本机制,可以为一个软件工具创建分支,但围绕分支的完整能力还没做出来——比如同时查看并运行一个工具的不同分支。往大了说,我们还注意到,以不那么中心化的方式协作开发软件,存在一些棘手的社会性难题。一个工具,是大家共用同一个版本,还是各自分叉、各走各路,人们怎么谈拢?某个工具的产品决策由谁来做——是最初的创建者继续说了算,还是允许任何人自由贡献?这些问题没法靠魔法解决,但我们认为版本控制工具能帮上忙。
与之相关的一个挑战,是管理对质量的预期。我们做出来的大多数工具,离商业产品的打磨程度差得远;它们只是些粗糙的个人工具。当一个人分享工具时,怎么把它的质量水平讲清楚?「原样分享」和承诺持续维护,是两回事。
最后,Patchwork 目前的嵌入模型比较有限,我们想改进它。工具可以嵌入其他工具,但被嵌入工具的界面会被挤得很局促,用户也需要更好的方式在层级结构的各个层面挑选工具。我们还逐渐发现,UI 组件之间需要更丰富的沟通方式,原因有很多。比如在做那个通用评论工具(它能配合任何工具使用)时,我们意识到,工具把用户当前的选区作为一个语义区域上报出来是很有用的。为了实现这类工具组合,我们加了一些硬编码的变通做法,但我们想找到一种更通用、耦合更少的方案,或许可以从前面提到的 entanglers 和 Varv 这类思路中汲取灵感。

在 Patchwork 中,将一份文本文档和一份电子表格嵌入白板
Patchwork 仍是一个活跃的项目。我们已经发布了一些版本控制工作的早期成果,收录在实验笔记中,随着进展,我们计划分享更多关于整个系统的内容。最终我们还计划将 Patchwork 作为开源工具发布。
动态文档
在基础设施方面的工作中,我们对软件的托管方式做了创新,但软件本身的行为仍然是用传统代码来定义的。然而我们知道,写代码——即便有 AI 辅助——在使用与修改之间依然是一道陡峭的“悬崖”。
因此,我们的另一条工作主线聚焦于让用户无需编程就能创建自定义工具。上文我们展示了一些先例,比如电子表格和 Hypercard,它们都体现了同一种思路:从可直接操作的信息出发,再选择性地叠加编程能力。
我们在动态文档的工作中延续了这一传统。目标是探索新技术,让用户能够为已有的媒体文档叠加行为。我们想弄清楚的是:用户应该如何表示信息,才能让这些信息有可能被赋予交互?工具之间又该如何在同一份文档中协作共存,成为一块共享的底布?
Potluck:用于食谱规划的动态纯文本
在 Potluck 中,我们探索了用户如何为文本笔记赋予动态行为,比如在食谱中按比例调整食材用量、设置计时器。用户可以用任何自己觉得自然的文本语法来表示信息,然后编写检测器,从文本中解析出有意义的结构。
我们发现 Potluck 能够支持创建许多有用的工具,而且用任何顺手的格式随手记下信息,体验很舒服。
在 Potluck 文档中按比例调整食谱。缩放滑块的行为由用户在 Potluck 编辑器内用一种公式语言指定。
AI 辅助也能很好地集成到 Potluck 中。AI 可以起草一组检测器和计算规则,同时用户仍然可以在实时编程环境中查看并亲自编辑生成的逻辑。
在 AI 辅助下将食谱中的用量翻倍。生成的检测器和公式可以直接查看和编辑。
不过,Potluck 的一个问题是从任意纯文本中解析结构相当麻烦。我们很难在不借助复杂检测器规则的情况下表示相关信息之间的关联(比如包含关系或顺序关系)。
Embark:用于旅行规划的动态大纲
在下一个项目中,我们决定尝试一种更有结构的方法。我们构建了一个用于旅行规划文档的编辑器,叫做 Embark,它以层级大纲作为基础格式。大纲提供了一些底层结构,解决了 Potluck 中检测复杂模式的部分问题。
我们还加入了结构化信息这一一等概念。像 Google Maps 位置这样的对象可以被“提及”并存储在大纲中。这些对象随后可以作为路径规划和天气预报等计算的输入。
最后,我们允许将地图和日历这类丰富视图嵌入大纲中,用来可视化和交互旅行相关信息。
在 Embark 文档中计算驾车路线。路线计算会利用周围文档提供的上下文。
Embark 的一个关键结论是:拥有丰富共享上下文的嵌入模型能带来强大的交互。Embark 中嵌入的视图深度感知周围信息——地图视图可以读取并可视化大纲中的位置。可用信息取决于局部上下文——你可以通过把地图移到大纲的其他位置来限制它显示哪些信息。交互也是同步的——鼠标悬停在地图上的某个地点会在大纲中高亮它,反之亦然。这些机制共同带来的结果是一种连贯的用户体验,而这是更受限的嵌入方案所无法提供的,比如我们目前在 Patchwork 中的方案,或者一个网站以 `` 嵌入另一个网站的做法。
在 Embark 中,地图可以与大纲文档中的结构化数据进行交互。
我们在 Potluck 和 Embark 中都遇到的一个主要挑战,是如何在信息的正式与非正式表示之间取得平衡。用非结构化的方式表达想法很有价值,但计算工具通常更适合处理结构化程度更高的表示。此外,数据 schema 并不局限于用户输入——Google Maps 的位置有自己的结构化格式,而且这种格式需要能在各种工具之间通用。
最后,Potluck 和 Embark 的这些想法能否进一步推广,以处理更广泛的任务,目前还没有定论。我们把这两个项目分别落地在食谱和旅行这两个具体用例上,是为了限制范围、鼓励真实使用,但我们计划进一步尝试推广这些想法,并把它们集成到像 Patchwork 这样可塑的基础设施之上。
走向更好的未来

在这篇文章中,我们勾勒了一种不同形态的计算愿景——它可以让人们打造自己的工具,把权力转向本地社区,并帮助人们做出最好的工作。我们也分享了许多关于这种不同形态的计算究竟如何实现的想法,既有前人的实践,也有我们自己的工作。
与此同时,我们也不想低估前方的挑战。几十年来,大量精力都投入到完善以应用为中心的计算,开辟一条新路需要面对许多棘手的问题。我们已经讨论了一些我们认为根本性的问题:如何从被动使用平滑过渡到主动创造?如何把应用拆解成可组合的工具?如何始终把协作群体的需求放在核心位置?
这仍然只是一份不完整的清单,还有一些重大挑战我们在这篇文章中基本没有涉及。
隐私与安全:我们如何在对可扩展软件和互操作性的追求与恶意行为者的现实之间取得平衡?当不受信任的陌生人分享对现有软件的修改,而这些软件又能访问敏感数据时,危险的事情就可能发生。
商业模式:如果开发者交付的是可组合的工具,而非单体应用,他们靠什么盈利?支持和维护的成本又由谁承担?
文化:如何培育一场走向个人自主的运动,让人们愿意去改造自己的环境——无论是数字环境,还是其他环境?
这些都是棘手的难题。仅靠技术能力不足以给出完整答案,还需要经济与文化的转变。但改变是可能的——计算仍然年轻,过去几十年里它已经发生了巨大变化,前方必然还有大量结构性的变革。
许多不同的人都能发挥作用。如果你是计算领域的研究者,现在正是重新构想计算的基础隐喻的好时机,把更多权力交到用户手中。如果你是平台开发者,不妨试着让天平倾斜一点:把终端用户看作有能力的参与者,而不是需要不惜一切代价加以保护的被动使用者。安全性和开箱即用的易用性是优点,可扩展性和强大同样也是。如果你做的是软件产品,想一想如何让用户自己掌握更多主动权,而不是替他们决定每一件事。我们相信,要充分实现可塑软件需要激进的变革,但渐进式的改进同样有帮助。
当在一个空间里生活或工作的人逐渐改造自己的工具来满足需求,由此产生的品质是特别的。可塑软件或许缺少那种在帕洛阿尔托闭门打磨出来的设计一致性,但我们发现,随着时间推移,它会生出一种老房子的韵味。它见证了过去的使用方式,带着以往决策留下的痕迹,同时也在不断演变以适应当下的需要。
每个人都应当有权改造自己的数字环境。在这个越来越由代码定义的世界里,这是发挥我们的创造潜力、保持自主感的重要途径。我们希望你能加入我们,一起让可塑软件成为现实。
想跟进我们正在进行的研究,请订阅我们的邮件通讯。
致谢:
- 感谢 Roshan Choxi、Orion Henry、Ignatius Gilfedder、Mark McGranaghan、Alexander Obenauer、Jeff Peterson、Max Schoening、Paul Shen、Paul Sonnnentag、Matt Tognetti 和 Adam Wiggins 对本文所提及研究项目做出的贡献。
- 感谢 Mary Rose Cook、Marcel Goethals、Alex Komoroske、Clemens Klokmose、Steve Krouse、Jess Martin、Andy Matuschak、Paul Sonnentag、Alex Warth 和 Adam Wiggins 对本文提出的宝贵反馈。
- 感谢 Thomas Mitchell Clark(吉他制作师)和 Nathan Peterson(Food Practitioner)允许我们拍摄他们的创作空间。