告别 React,拥抱 Svelte:21 天重写应用,速度翻倍代码量减半!
作者 | Dusty Phillips
译者 | 刘志勇
策划 | Tina
导读 :在软件开发的大潮中,重写项目常常被视为一项既常见又充满挑战的任务。本文作者结合自身多年的实战经验,深入剖析了前端与后端重写之间的异同,并特别分享了从 React 向 Svelte 迁移的历程,其中遇到的种种难题与收获均一一呈现。通过对比 Svelte 与 React 在性能、开发速度及开发者满意度等方面的表现,作者认为 Svelte 具有成为新项目首选框架的潜力,并分享了自己对 Svelte 的独特见解与热切期待。此外,文章还着重强调了项目重写的必要性及其所面临的挑战,同时列举了一些成功的重写案例与失败的教训。若你对软件重写、前端框架的选择以及 Svelte 的优势抱有浓厚兴趣,那么本文定能为你带来深刻的见解与启发。
我和妻子一起倾注心血于 Fablehenge,这款专为小说作家打造的写作应用已经陪伴我们好几个年头了。最初,这不过是个业余爱好,我们会用几个周末的时间全力以赴,然后让它暂时休息几个月,偶尔在晚上的空闲时间里稍作调整。
今年年初,我本打算好好休个长假,去开辟新的徒步小径、做做木工活。虽然这些活动确实进行了,但我发现自己还是基本上把主要时间都投入到了 Fablehenge 的工作中。
Fablehenge 是用 React 编写的,我和 Jen 都对 React 了如指掌。我早在它还未开源时就断断续续地开始使用,而 Jen 则多年来一直只用它一个。
一月份,我大部分时间都在努力让一个相对(但并不算特别)复杂的拖放系统正常运作。接着,Jen 又在二月份花了三周时间来微调和改进我的实现。
我们的代码是基于 dndkit 构建的,就像我找到的每一个 React 拖放库一样(我为此花了几个小时搜索),它最近都鲜有维护更新。尽管我很喜欢这个库,但经过我们共同花费大约六个星期的调整后,我们发现应用存在性能问题,而自己却束手无策。
在探索解决方案的道路上,我意外地发现了 svelte-dnd-action 这个库。当时,它最新的提交竟然就在 18 小时前!天哪,我真是羡慕不已!
虽然之前听说过很多人对 Svelte 的热爱,但我总觉得那更多是炒作而非实质。毕竟,Javascript 社区向来以追求 “新奇特” 而著称。
然而,为了从 React 的挫败感中解脱出来,我还是决定花一天时间学习 Svelte 的教程,并尝试创建了一些简单的应用。当我测试 svelte-dnd-action 时,真的被它深深吸引了。我立马给 Jen 分享了教程链接,没过多久她就告诉我:“我觉得我再也不想回到 React 了。”
于是,抱着玩玩看的心态,我开始将几年的工作成果迁移到 Svelte 上。这个过程竟然异常顺利,我原本还担心会出现什么问题。在我的职业生涯中,遇到过许多看似很好的库,但最后却带来了更多麻烦而非解决方案。抽象层总是会出问题,这是难免的。但随着对 Svelte 的深入了解,我渐渐意识到它真的很特别。
我记得不知道什么时候曾听人说过,新技术要想克服用户社区转换的惰性,必须比现有技术好上 10 倍才行。React 出现时,其开发体验显然比当时的竞争对手(如 jQuery 和早期的 Angular,当时的 Angular 与今天的 Angular 不同)要好得多,远远超过了 10 倍的标准。因此,整个社区几乎一夜之间都转向了 React。
虽然之前我对 Svelte 的炒作持保留态度,认为它不太可能成为改变整个社区的 10 倍改进技术。但现在我的想法已经变了。
1 迁移之旅:React 到 Svelte 的开发速度革新
我估计我们在 React 应用上投入了大约一年的总人时。我们的代码库写得既规范又易于维护,规模适中,不存在任何未知问题让我们担忧。因此,相较于最初的投入,从头开始重写它花费的时间肯定会少很多。我们深知复杂性的根源所在,于是优先解决这一问题。同时,我们也对应用程序进行了结构化调整,以便能够复制粘贴大约百分之二十的与查询和修改数据相关的代码,只需稍作修改即可。
整个重写过程仅花了三个星期的时间。这里所说的 “重写”,并非指 “大部分重写”,而是指 “完全重写”。2 月 26 日完成了初始提交,到 3 月 15 日,Svelte 应用已经部署并投入生产。我们已经开始开发新功能,并且进展迅速。
请回想一下,之前 Jen 和我在 React 中耗费了三周时间才勉强搞定拖放系统。鉴于这是当时面临的主要问题,我们自然在 Svelte 中也优先解决这一问题。而我在 不到一天 的时间内就彻底解决了它。虽然我在 svelte-dnd-action中发现了一个错误或缺失的功能,但维护者在我发布了可靠的重现后两天内就迅速修复了。
若将每六周的 React 开发时间与一天的 Svelte 开发时间相对应,那么我们将在半天内就赚回重写应用程序所花费的时间。当然,这种说法可能稍显(更为)乐观!但不可否认的是,我们的 Svelte 开发速度确实比 React 开发快两倍以上。
2 轻装上阵:Svelte 带来的代码精简与效率提升
虽然 React 相比很多前辈技术有所进步,但它仍然需要编写相当多的样板代码(哪怕是不使用 Redux 的情况下,我们也知道要避开它)。我们已经使用 React 很长时间了,以至于对样板代码已经习以为常;在编写 React 代码时,往往会忽略每个组件中重复的部分。
我们的 Svelte 应用程序只用了 React 应用程序所需代码的 60%。这里我要再次强调,我们的 React 应用程序编写得非常规范,没有多余的代码或未使用的功能。我们几乎把所有东西都迁移到了 Svelte 上。当然,在迁移过程中我们也对一些功能进行了重新设计,但这只是因为这样做起来很容易。要说的话,我怀疑我们的 Svelte 代码是写多了,不是写少了。
我应该承认,代码量减少的部分原因是我们现在使用了 tailwind CSS,而之前我们使用的是 Chakra。tailwind 在减少代码行数方面确实非常出色。关于 tailwind 的更多细节,我会稍后详细介绍(提前透露一下:我们其实不太喜欢它)。
3 愉悦探索:感受 Svelte 的独特魅力
Fablehenge 是一个业余项目,我们的初衷是享受在上面的工作时光(当然也希望作家们能喜欢使用它)。我们本以为自己很喜欢 React,但事实是,Jen 和我经常在午餐时抱怨 React 生态系统的各个方面。并不是说 React 不好,它真的很棒!只是相较于它的潜力,使用它的乐趣似乎没有那么大。
相比 React,Svelte 有趣得多 ,而且我认为等到 Svelte 5 发布(即将)时,它会更有趣。当然,享受编码的感觉是非常主观的,但 Svelte 完全符合我们对它的所有期待。
我们是一个两人团队,可以完全掌控项目。因此,我无法断言 Svelte 是否能像 React 那样,扩展到拥有数千名开发人员和数百万行代码的公司规模。但直觉告诉我,它是可以的。Svelte 与 React 鼓励的组件模型和分隔样式保持了一致。然而,为了效率,它也为开发者提供了很多强大的功能,但滥用这些功能可能会导致维护困难。
最值得一提的是,Svelte 强调单向绑定,但在适当的情况下也允许双向绑定(通常是在表单元素中)。遗憾的是,关于 “适当” 的情况并没有硬性规定。我完全能想象到,大公司的实习生可能会觉得双向绑定更简单,但这实际上可能会给未来的维护带来噩梦。但任何有经验的开发者都会告诉你,审查实习生的代码比自己编写代码需要花费更多的时间和精力,所以我并不认为这些不良习惯会真正合并到项目中,对吧?
总的来说,项目的代码越少,维护起来就越容易。这也是为什么会有更高级的语言和框架存在的原因。如果 Svelte 能够稳定地将维护代码量减少 30-40%,那么我期望它也能用到大规模项目里。
4 流畅起步:快速领略 Svelte 的高效魅力
尽管 Svelte 实际上是一种新的编程语言,但它非常依赖 JavaScript、HTML 和 CSS,因此如果你懂得 Web 开发,那么 Svelte 对你来说并不陌生。当然,也有一些独特之处需要你去学习,尤其是在响应性方面。不过,令我们惊讶的是,在阅读了几个小时的教程后,我们在 Svelte 中的生产力很快就得到了提升。
Svelte 立刻就显得非常合理。它给人的感觉就像是充分利用了标准技术,而不是像许多框架那样重新实现它们。
事实上,学习起来如此轻松,以至于我经常觉得我是在“回忆”如何使用 Svelte,而不是重新学习。我会感叹:“噢!这才是 Web 开发应有的样子。我都忘了!”
5 守护质量:全面测试在项目重写中的保驾护航
这次重写能够如此迅速且简单地进行,一个重要原因在于我们拥有一套非常完善的测试套件以及一个详尽的电子表格,其中详细列出了应用程序中的每个功能及其手动测试方法。这些工具的重要性简直无法用言语形容,我此前从未在真正的公司中见识过如此完备的测试体系。
我们的测试套件完全采用 Cypress 编写,主要用于端到端(e2e)测试。当然,我们也包含了一些单元测试,但这些测试同样是在 Cypress 运行器中实现的,主要是因为我不想为 testing-library 额外设置持续集成(CI)。
这里稍微提一下,我们对 Cypress 并非完全满意。但在我们选择它的时候(几年前),它是唯一一个能与 contenteditable 元素稳定协作的工具。由于我们是一个小说写作平台,因此 contenteditable 元素的使用相当频繁。Cypress 的表现还算不错,所以我们最近并没有尝试其他替代方案。
不过,Cypress 测试并不能直接无缝迁移到我们的 Svelte 应用程序中。这主要是因为我们在迁移过程中并未总是将 data-cy 属性一并移植,同时某些在 React 应用程序中适用的选择器在 Svelte 中并不直接兼容。但经过一些轻微的调整,我们很快完成了迁移工作。在调试 Cypress 不稳定性的过程中,我们积累了大量经验,这使得迁移过程相对顺利。
有一点我想提一下,那就是在 Svelte 应用程序中,我们不得不在测试中增加了更多的 cy.wait 调用。据了解,Svelte 的响应模型是这样的:即使页面上的某个元素已经更新了内容,也并不意味着所有依赖该更新内容的其他元素都已经接收到新值。
以我们的大纲测试为例,添加新章节或场景后,它们可能会立即显示在大纲中,但操作选定场景的工具栏按钮却可能无法立即识别出这些新添加的项目。因此,我们不得不在那里添加一个 cy.wait(50) 来确保元素状态同步。这多少有些令人无奈。我希望 Svelte 5 能够解决部分这类问题,否则我们可能需要在可点击元素中添加额外的 data- 属性来传递那些原本应由响应系统处理的数据。
虽然目前的情况还有改进的空间,但重要的是,我希望你能够认识到一点:如果你打算迁移或重写项目,那么务必在原始项目中构建一套全面且易于迁移的测试套件。这是确保迁移工作顺利完成并验证新版本正确性的关键所在。
6 权衡利弊:React 与 Svelte 作为单页面应用框架的对比思考
我们的 React 应用原本是一个单页面应用。然而,Sveltekit 在支持服务器端渲染、预渲染(静态站点)以及客户端渲染方面表现出色,它甚至允许我们在站点的不同部分混合使用这些渲染方式。
起初,我们选择了服务器端渲染来构建 Svelte 应用,因为我认为这是 Sveltekit 中备受推崇且支持度较高的方法。然而,鉴于我们应用程序的特定结构,我们并未从服务器端渲染中看到太多明显的优势。我们并不进行直接的网络请求,而是将所有数据存储在 IndexedDB 中,并依赖出色的 Dexie Cloud 服务来处理繁重的数据操作。
因此,我认为维护和扩展一个单独的生产节点服务来运行 Sveltekit 的服务器端渲染功能,其开销并不比直接在边缘部署静态站点的简单性来得实用。在使用 Svelte 的过程中,我们并未明显感受到服务器端渲染与单页面应用之间的性能差异,尽管这两种方式都比 React 应用的性能要好得多的多。
说实话,我从未对服务器端渲染的理念有过太深的印象,因此这个决定并没有花费我太多精力。将计算从客户端移至服务器并不总是能带来显著的好处,特别是考虑到浏览器在渲染 HTML 和运行 JavaScript 方面已经做了非常出色的优化。
顺便提一下,最近我看了一个演讲,其中一位开发者认为他有一个 “绝妙” 的想法,即在 onMount 处理程序中渲染内容以加快服务器端渲染的速度。听到这种将计算推送到客户端的做法,我只能摇头叹息。
当然,服务器端渲染在某些情况下确实有其用武之地,特别是当涉及到高度动态或用户生成的数据,并且需要进行 SEO 优化时(例如电子商务网站)。然而,对于搜索引擎无法索引的登录保护数据,我认为服务器端渲染并不具备太多优势。它似乎是在优化错误的方向。
鉴于 Svelte 无论采用哪种渲染模式都表现得非常迅速,我并不打算在此问题上过多纠结。特别是考虑到 Svelte 5 承诺将为我们带来更高的性能,我猜测使用 Svelte 5 进行客户端渲染将足够快速,适用于大多数场景。而且,我真的很喜欢提供静态 HTML 和 JavaScript 文件所带来的最小化运维开销。
7 揭秘之旅:Svelte 5 的期待与潜在挑战
Svelte 的响应性确实需要一些时间去适应,主要是因为它的响应性很难被关闭。经过一个星期的使用,我才逐渐适应,并确信它不会在我不希望的时候自动更新。虽然这有时候比该更新的时候不更新要好,但在 Svelte 4 中,想要关闭响应性却不太直观。
据说,Svelte 5 已经解决了这些问题。有了符文和 untrack 函数,确保响应性在正确的时间和位置发生变得轻而易举,而不再是不受控制的。此外,Svelte 5 更加易学易用,因为它相较于 Svelte 4 减少了概念和自定义语法的数量,但更有效地复用了这些概念。更值得一提的是,Svelte 5 的一些新语法与 React 更为相似,因此如果你正在从 React 迁移,Svelte 5 会比 Svelte 4 更容易上手。
如果有人考虑将 React 重写为 Svelte,我可能会建议先等待 Svelte 5 的普及。虽然它现在仍处于预发布阶段,但在我们的情况下,我们急需尽快发布生产应用程序(尽管 “尽快” 可能只是 “三周”)。因此,我们不愿依赖 alpha 软件,尤其是当我们对 Svelte 还不是很熟悉时。在学习一个新框架的过程中,当遇到的错误既可能是因为自己的误解,也可能是因为框架本身的问题时,会面临诸多挑战。
另外,我们不想选择 Svelte 5 的另一个原因是其库生态系统尚未完成迁移。虽然 Svelte 5 应与 Svelte 4 完全兼容,但它也预设你会逐渐迁移到新范式上。即使现有的 Svelte 4 库与 Svelte 5 完全兼容,我更期待那些从头开始设计或从当前版本重新设计以充分利用 Svelte 5 优势的库。
8 痛点解析:深入探究 Svelte 的挑战与不足
每个库的选择都伴随着权衡,这确实是一个无法回避的现实。Svelte 在多数方面表现得相当出色,然而,样式化子组件却是一个令人不甚满意的领域。
Svelte 具备一些出色的功能,允许我们独立地为单个组件进行样式化。然而,一旦涉及到为第三方组件(如来自组件库的组件)添加样式时,情况就变得复杂起来。我们不得不采取一些不那么正规的方法,要么退而求其次使用全局样式,要么依赖于库来接受某种类或属性字符串。
因此,我们发现,在样式化方面,几乎每一个 Svelte 组件库都让人头疼(或许 melt-ui 是个例外)。其中大多数组件库都依赖于 tailwind-css,并通过传递类名来应用样式。但问题在于,你必须清楚组件所使用的类名,才能确保你正在修改正确的元素。更糟糕的是,如果组件结构复杂,你还需要根据正在样式化的子组件传递不同的类名(这取决于所使用的框架),这就要求你必须深入了解库的内部结构才能进行样式化。
我们从 flowbite-svelte 开始尝试。这个库确实令人惊艳,我们能够在短时间内取得显著的进展。然而,最后的 20%(即样式化部分)却成了我们的拦路虎。虽然不能说这个过程是痛苦的,但也绝非愉快。也许我们并不讨厌它,但我们也并不喜欢它,而我们真心希望我们的应用是能让自己喜欢的。
Tailwind 有时被形容为 “爱恨分明”。一开始我们并不介意它,但随着时间的推移,我们开始对它产生了反感,所以看来我们属于 “恨” 的那一极。虽然这并无大碍,但经过深入研究,我们认为,在 Svelte 中,使用 Tailwind 或其类似工具来样式化子组件是目前最为合理的选择。
当然,最好的替代方案可能是完全不使用组件库,这意味着我们需要将组件复制粘贴到自己的设计系统中,并自行进行样式化。即使我们选择了这条路(或许还会结合使用 melt-ui),我们仍然需要仔细考虑如何将适当的 props 或类名传递给我们的组件,以确保它们符合设计要求。
另一个选择是更加自由地使用全局样式。对于小型应用程序来说,这可能是一个可行的方案,但我们都清楚,长期来看这并非可扩展的解决办法。
我相信,随着 Svelte 5 的 Snippets 功能的推出,这个问题将得到一定程度的缓解,但恐怕无法完全解决。在 Svelte 的问题跟踪器中,我们可以看到许多关于这个问题及其潜在解决方案的讨论。因此,我期望一旦 Svelte 团队完成了庞大的 Svelte 5 重写工作,他们将有更多精力专注于解决样式化问题,为 Svelte 带来与其他方面同样优雅和简洁的体验。
9 迁移决策:React 到 Svelte,值得一试吗?
关于是否应从 React 迁移到 Svelte,我确实无法给出一个明确的答案。但对我们而言,Svelte 似乎是个潜在的竞争优势,毕竟它能让我们以更少的人力实现更快的开发速度。然而,如果你的应用和我之前接触的许多 React 应用类似,即缺乏足够的测试,充斥着死代码路径或过时的代码路径,且无人真正了解,那么迁移起来确实会非常棘手。虽然从长期来看,这有助于提升项目的健康度,毕竟死代码路径会被清理,误解的代码路径也会变得明朗,但长期健康的维护往往伴随着众多彼此争抢资源的优先事项,如新功能开发、满足用户需求和应对日常的运维挑战。若你只有 18 个月的时间窗口,那么规划三年后的维护工作并无太大意义。
我猜测,大多数生产项目可能不会像我们所预期的那样迅速完成迁移。不过,抛开虚假的谦逊,我们确实是一支实力强劲的开发团队,因此我对我们 React 应用的迁移前景充满信心。
但话说回来,如果你的应用并未达到轻松迁移至其他技术的状态 —— 比如测试不足、非端到端的测试、临时性的 API、不完整的或不可维护的文档,以及使用了团队从未完成迁移的已废弃库或范式等 —— 那么,你恐怕也难以自信地为其添加新功能,无论你如何自我安慰。尽管重写的过程可能会很痛苦,但这也是一个绝佳的机会来整理你的 “代码之家”。
在我的职业生涯中,我见证了众多重写成功的案例,也目睹了不少失败的尝试。通常,前端重写的效果要比后端重写更为理想,因为数据迁移的复杂性相对较低,有时甚至不是问题。成功的重写往往源于这样的立场:“我们已经将应用简化到了极致,现在是重写它的最佳时机。” 而失败的案例则往往沿着 “这个应用太复杂了,让我们推倒重来” 的路线。
我坚信,Svelte 在性能、开发速度和开发者满意度方面的卓越表现,使其相较于 React 有着十倍的优势。因此,它完全有可能成为新项目的首选框架,取代现有的主流选择。当然,我并不是说 React 会消失(毕竟现在还有招聘 jQuery 开发者的岗位,尽管 HTML 标准已经有效地取代了它),但 Svelte 确实有可能在新项目中比 React 更受欢迎。
特别是当你从零开始启动一个全新项目时,我相信你会更倾向于选择 Svelte。
至于那些将 Svelte 评为 “最受喜爱” 框架的头条新闻,嗯…… 我承认,起初我只是把它们当作噪音忽略了。但下一次调查出现时,我一定会积极参与,坚定地站在 Svelte 的阵营中为它摇旗呐喊。
作者简介
Dusty Phillips ,加拿大作家兼软件开发者。
原文链接:告别 React,拥抱 Svelte:21天重写应用,开发速度翻倍代码量减半!_架构/框架_Dusty Phillips_InfoQ精选文章
代码生成交给机器,我们的工作将变成软件概念的设计师 新程序员
【导读】本文探讨了软件设计的本质和未来趋势。作者通过分析三个爆款产品,提出了基于“概念”的软件设计方法。他认为,真正的创新在于简化应用场景,而非创造全新功能,创新往往不是创造新的东西,而是让原本的事情做起来更加简单。文章还讨论了如何利用概念思维进行编程,并预测随着 AI 代码生成技术的发展,人类工作将更多地转向概念设计和提示工程,而具体的代码编写则可能交给机器完成。
本文整理自 MIT 计算机与 AI 实验室(CSAIL)副主任、ACM Fellow Daniel Jackson 教授在 2024 全球软件研发技术大会 中的演讲,同时收录于《新程序员 008》。《新程序员 008》聚焦于大模型对软件开发的全面支撑,囊括 Daniel Jackson 和 Daniel Povey 等研发专家的真知灼见与“AGI 技术 50 人”栏目的深度访谈内容,欢迎大家。
作者 | Daniel Jackson
责编 | 王启隆出品丨新程序员编辑部纵观软件设计的历史,可以见证这几十年来我们取得的巨大成功。
如今,软件具备了可用性 ,每天有数十亿人在工作和娱乐中使用软件。得益于编程技术、分布式计算、网络服务、数据库等领域的成功,软件具备了可 扩展性 。而且,由于人工智能技术的蓬勃发展,软件变得越来越智能 。
但与此同时,我们也面临着一些巨大的挑战。
由于在用户界面、设计系统、以用户为中心的计算方面取得了诸多进展,软件逐渐变得可用了,但它仍然经常过于复杂 ,使用起来负担很重。用户经常需要花费大量时间去理解他们正在使用的软件,而这一点在企业软件中尤为明显。我们的软件或许是可扩展的,但它经常不安全 ,容易受到针对公司隐私和安全的攻击。而且它也常常存在安全隐患,有引发灾难的风险。还有个大家都很担心的问题:尽管软件正在变得更加智能,但也存在更加不可预测和不可靠 的风险。
那我们该如何应对这些挑战呢?我的建议是,回归本源。作为工程师,不能仅仅通过发明新技术来解决这些基本的潜在问题,而是需要思考软件的本质是什么。特别是,工程师需要检查软件的功能,并提出问题:软件应该做什么?如何构建这种功能?
除此之外,软件模块化的问题可能比以往任何时候都更加重要。为了实现这点,还需要考虑用户的心智模型,即让用户理解我们正在构建的系统。为了探讨这些问题,我想分享三个所有人都很熟悉的成功产品案例。通过这些故事,再进一步思考:是什么让这些产品如此成功?
iPod 的成功之处:改变了人们听音乐的渠道iPod 诞生于 2001 年,可谓是相当有年头的产品了。那么,iPod 的创新之处在哪里呢?显然,它的创新并不在于工业设计。原因是,只要你看看 Dieter Rams 在 1958 年设计的口袋收音机,就很容易能发现它对 iPod 外观设计的影响(如图 1 所示)。iPod 无疑是具有标志性的,但它在设计方面并不新颖。
图 1 iPod 和六十多年前的收音机设计雷同
在技术上,iPod 其实也没什么突破。史蒂夫·乔布斯在当年找到了东芝生产的一款异常小巧的 5GB 硬盘,从此缩小 iPod 的尺寸,取得了成功。而苹果公司本身也有一个文件传输协议,使上传音乐的速度变得更快。但这些都不是 iPod 真正必需的。因为数字设备公司(Digital Equipment Corporation, DEC)在 iPod 问世的几年前就已经生产过一台 5GB 的个人点唱机,名叫 Personal Jukebox。
因此,我认为 iPod 的成功源于一个更简单的原因。那就是 iPod 的基本应用场景简化了人们以前必须做的事情 。试想在 iPod 出现之前,人们使用 MP3 播放器时必须经历的步骤:下载盗版音乐/转录 CD 光盘里的音乐 —— 上传至个人的播放设备 —— 开始播放音乐。其中,前两步的用户体验其实很差,因为用户要么只能偷偷盗版一些网络上的非法音乐,要么必须翻录自己的 CD 光盘收藏,而无论是哪种选择,操作都相当繁琐。然后,他们还得想办法弄清楚如何上传曲目到自己的个人设备,最后才能播放。
iPod 的出现,把一个非常简单的使用方式打包成了单一的应用场景。用户从 iTunes 购买歌曲后,可以立即在电脑上播放,同步到 iPod 后也可以在上面播放。在这个应用场景中,iTunes 作为辅助角色至关重要。事实上,如果观察 iPod 当年销量的爆炸性增长,会发现这款产品真正主导市场的时刻是在 2006 年左右。那时,iPod 的销量超过了任何其他 MP3 播放器。而在这一年,苹果已经通过 iTunes 销售了数亿首歌曲。
所以,iTunes 对 iPod 来说是必不可少的,因为它使这个简单的应用场景成为可能。现在,一个真正有趣的问题是:索尼为什么没能做到这一点?
你可能不知道,早在 1999 年,索尼就拥有成功所需的所有要素。索尼一直被视为全球最大的音乐集团之一,他们当时已经推出了一款出色的数字音频播放器 Network Walkman,这是其经典产品 Walkman 的后代。它甚至在日本已经有一间名为 Bitmusic 的歌曲商店,但不知何故,索尼无法将旗下的顶级播放器和歌曲商店整合在一起。关于原因,也已经有很多讨论,有人猜测可能是因为索尼使用了专有的压缩方案(ATRAC),还有人猜测是因为索尼将商店限制在了日本本土发布,更有甚者认为是数字版权管理(DRM)控制使软件难以使用……但我认为,最关键的是,他们没有一个简单的应用场景 。
当我们打开索尼 Network Walkman 的用户手册时,只需浏览一页,就能立即感觉到这不是一个容易使用的设备(如图 2 所示)。显然,它缺乏一个简单的应用场景。
图 2 Network Walkman 繁杂的使用说明书
WhatsApp 的成功之处:群聊第二个案例是 WhatsApp。很多人认为 WhatsApp 的创新在于提供了免费短信服务。但实际上,在此之前已经有免费短信应用了,那就是比 WhatsApp 早了三年的 TextFree。如果回顾 WhatsApp 的发展历程,我们会发现它从一开始就稳步增长,然后在 2011 年的某个时点突然出现爆发式增长。那时发生了什么?原来,WhatsApp 团队当时发布了一条推文(见图 3):「群聊功能现已上线。」
图 3 WhatsApp 的历史性推文但在 2011 年的时候,其实有许多不同的公司都在争相成为手机上运行群聊的首选应用程序,比如 GroupMe, Beluga 和 Yobongo。而 WhatsApp 最终在这场竞争中胜出。群组和群聊为什么如此重要?我认为,这还是与简化应用场景 有关,并且还是一个非常简单的创新。
仔细想想,在没有群组的应用场景中,比如说电子邮件,每次你想扩大参与对话的人群时,新成员都必须被明确地添加为对话的接收者。这个过程和下载盗版音乐一样,其实非常繁琐。而通过群聊,你可以邀请人们,他们可以异步加入对话。这本质上是一种分布式的负担:每个人都以自己的节奏加入聊天,而不是由发起人一次性添加所有成员,让人被迫接受对话。这样,发送消息的人不会因为添加新成员而增加负担,使得群组的扩展变得更加简单和高效。
Zoom 的成功之处:会议链接Zoom 的崛起是一个引人入胜的故事,它在疫情初期就取代了 Skype,而如果我们去查看那些预测 Skype 用户增长的统计数据,会发现许多网站都认为 Skype 的用户数量可以一直稳步增长到今年 —— 事实是,Zoom 的出现让这些预测都化为了泡影。至少在美国,除了 Zoom 以外,几乎每个视频会议平台基本上都逐渐衰落了,而 Zoom 则呈现爆发式增长。
那么,Zoom 的创新之处在哪里?这里有一个插曲:当时的英国首相鲍里斯·约翰逊发了一条广为人知的推文,宣布他在 Zoom 上将举行世界上首次“数字化内阁会议”。不幸的是,当他展示会议截图时,无意中泄露了自己的会议链接(Meeting Link)。当时 Zoom 还没有默认的安全控制,所以理论上任何人都可以通过输入那个会议链接加入英国政府的内阁会议。
这个事故背后,其实隐藏着一个重要细节,且它恰恰揭示了 Zoom 成功的真正原因。
iPod 的创新之处不在于听音乐,WhatsApp 的创新之处不在于聊天,所以 Zoom 的创新也不在于视频通话本身。视频通话技术可以追溯到 1960 年代,而后来的 Skype 也在视频通话这方面做得很好了。此外,Zoom 并没有在设备上实现视频通话的技术创新,因为早在 1994 年,世界上首款商用网络摄像头 QuickCam 就已经很便宜了(当时售价 100 美元)。虽然 Zoom 确实有很好的视频质量,但它的核心创新也不是视频编码技术,像 H.264 这样的视频编解码器(用于高效压缩数字视频的标准)早已存在多年。
相反,Zoom 的创新在于它巧妙的“一键会议”应用场景 。在 Zoom 之前,如果你使用 Skype 想邀请一群人开会,会遇到两个麻烦。首先,你需要注册应用程序,而且所有参会者也必须注册。这意味着每个加入会议的人都必须是 Skype 用户,并且拥有一个账户。其次,发起通话时,你必须逐个将所有参与者添加到通话中。
显然,对于大型会议来说,这非常不便。诚然,Skype 确实有一个群组功能,使这个过程稍微简化了一些。但它远不如 Zoom 的“一键会议”应用场景那么便捷。Zoom 的创意是,当你创建会议时,它会生成一个会议链接,你只需跟和别人分享这个链接,然后任何人都可以通过这个链接加入会议。这看似是个微小的创新,但实际上影响巨大。
我认为这正是 Zoom 成功的关键。纵观其他视频会议平台的成功案例,没有一个能真正与 Zoom 相提并论。而 Zoom 也并非首创者。事实上,几年前就有一家名为 Join.me 的公司在其产品 Log Me In 中率先提出了会议链接的概念。Zoom 借鉴并完善了这个创意。这个创意如此成功,以至于随后被其他所有竞争对手采用。特别是 Skype,在 2020 年 4 月才开始使用会议链接功能。但到那时,Zoom 已经拥有 3 亿用户,Skype 早被甩的远远落后了。这个创意之后还影响了其他平台,例如,Microsoft Teams 在几年后也采用了会议链接的概念。
从场景(Scenario)到概念(Concept)应用场景定义了产品,是关于如何使用产品的生动描述。
它既是一种社交协议(规定了用户如何互动),同时也是一种 API(定义了技术层面的交互)。它揭示了产品将如何满足用户需求,代表了一种典型但并非唯一的使用方式。这种以应用场景为中心的产品思考方式不同于传统的用例方法或多场景产品规划。相反,它强调通过单一的核心应用场景来捕捉产品的设计理念。
现在,我想提出一个更具争议的观点:创新几乎从来不会真正使全新的事物成为可能。 仔细想想。回顾你所知道的所有创新发明,问问自己,“它们真的让我能做任何全新的事情吗?” 事实上,几乎所有时候,创新所做的是让你更容易完成你已经在做的事情,它 是用一个新的应用场景来替代一个存在不便之处的旧应用场景。
现在我想将“场景”扩展到我称之为“概念”的想法上。以 Zoom 为例,它的核心应用是提供会议链接服务,但除此之外,它还支持多种场景,比如在聊天会话中,用户可以在聊天窗口发送文本消息。这些不同的场景交织并存,相互穿插,而每个场景都体现了我所说的“概念”(Concept),即一种独立的功能单元。
概念是单个软件、一类软件以及各类软件的特征。概念可以让开发者比较软件,注意其必要的功能以及知道如何有效地使用这些功能。
因此,你可以将 Zoom 理解为由这些概念构建而成,包括会议链接、视频频道(VideoChannel)等。其中一些概念是创新性 的,如会议链接;有些是我们非常熟悉的,如在线聊天;还有一些是通用性 的概念,几乎在每个应用程序中都能见到,例如用户身份验证,这是最典型的通用性概念之一。
这些概念通过“交互 +同步 ”的方式融合在一起。在使用 Zoom 时,你不需要特意开启聊天功能;事实上,当你加入或启动会议时,就已经自然而然地进入了聊天模式。这种自然过渡的方式就是概念如何巧妙地结合在一起的体现。
因此,我们可以将应用程序视为概念的集合,并将其简化为最核心的部分。例如,对于 Zoom 来说,基本的概念可能是会议链接和视频会议。对于像 Calendly 这样的日程安排软件,核心概念可能是自助预约和通知。而对于 iPod 来说,则是歌曲管理、音乐商店以及文件同步的概念。这些概念实际上混合在了我所描述的各种场景中,并且可以容易地分解为三个独立的场景,进而理解为三个独立的概念。
当我们设计概念时,我们不仅仅是在考虑场景 。例如,对于我提到的聊天概念,设计这种概念首先要明确它的目的,而聊天概念的目的可能是为了在群组中分享简短的消息。一个概念的目的应该是有说服力、以需求为中心、具体和可评估的。概念的目的很少能用比喻解释清楚。
接下来,就可以定义场景,或者说定义一个操作原则(operational principle),这一术语源自哲学家迈克尔·波兰尼的思想。操作原则用于展示如何通过操作实现目的,这是理解概念的关键。对于聊天概念来说,场景定义可以是:当两个用户加入聊天后,如果其中一个用户发布消息,另一个用户就能阅读它。
但是,我们可以进一步具体化这些规则,因为概念本质上是一个可以通过 API 来定义的服务。比方说,列出一系列动作。这些动作实际上是出现在场景中的行为,可以被视为 API 的组成部分。接着,我们可以思考支持这些动作所需的状态。
例如,对于聊天概念,状态可能包括聊天中的消息集合、每条消息的具体内容、消息发布时间、用户何时加入聊天等。实际上,当你深入研究概念的细节时,你会经常发现一些原本在场景中并未显现出来的重要设计问题。在这个例子中,很多聊天概念的工作方式实际上存在一个显著的设计缺陷,那就是用户通常只能查看他们在加入聊天之后发布的消息。
在 Zoom 中,这就是一个不小的困扰,因为它意味着如果有人在会议开始时发布了一条聊天消息,比如会议议程,那么任何晚一分钟加入的人就无法看到这条消息。这对于那些想要获取会议初始信息的迟到者来说是非常不方便的。
如何运用概念?我们现在可以问另一个问题:有多少概念能使一个应用程序与众不同?就像我描述的那些应用程序,有些我认为实际上只有一个关键概念。以 Zoom 为例,我认为它的核心就是会议链接这个概念。而对于 WhatsApp 来说,它的核心实际上是群聊的概念。
在另一个极端,有些应用程序真正引入了一整套复杂的概念。我认为最能代表这类应用的是你可能称之为生产力应用(productivity apps)的那些软件。想想像 Photoshop(Adobe 公司开发的图像处理软件)这样的应用,以及所有遵循 QuarkXPress(一款专业的桌面排版软件)模式的桌面出版应用,现在还包括像 Adobe InDesign(Adobe 公司的专业排版软件)这样的应用。
这些应用有一套非常精细的概念集合,说实话,这些概念相当难以学习。以 Photoshop 为例,它不仅仅是像素数组的概念,还有图层、蒙版和通道这些关键概念。正是这些概念实际上使得 Photoshop 能够击败如此多的竞争对手。
概念的组合为创造性设计提供了机会,即使其中的每个概念都是通用性概念。 概念不像程序那样,可以用较大的包含较小的。相反,每个概念对用户来说都是平等的,软件或系统就是一组串联运行的概念组合。
但有趣的是,还有一些应用程序实际上没有引入任何新的概念。比如 Gmail(谷歌的电子邮件服务)。Gmail 基本上只是结合了我们已经知道的两个通用性概念,即电子邮件和搜索。又或者是 Arc 浏览器,它做了一件非常有趣的事情,将浏览器中的标签概念与书签概念巧妙地结合在一起。
当你心中有这样的概念时,你可以会从一个不同的角度来思考设计和推理可用性。让我们以 Zoom 为例。Zoom 有一个表情反应按钮,如果点击按钮,你会看到如下图 4 这样的界面。
图 4 Zoom 的表情反应界面
长久以来,Zoom 用户界面基本的底层功能是一直不变的。例如,用户一直可以点击“鼓掌”按钮,为演讲者喝彩;点击“对”和“错”的按钮,表示赞同或反对;然后还有两个按钮,用于示意演讲者放慢或加快说话速度;右侧的“咖啡杯”按钮,则可以表示你想喝杯咖啡缓缓;或者是界面下方的“举手”按钮,点击之后可以像现实的会议一样举手示意。
有趣的是,点击这些按钮的反馈效果却有所不同。例如,点击“爱心”按钮,冒出来的爱心表情会在 10 秒后自动消失 —— 这可能是 Zoom 设计中的一个遗憾之处。奇怪的是,如果我们点击“举手”按钮,出现的表情并不会在 10 秒后消失,而是一直停留在界面上。经常使用 Zoom 的人都知道,人们经常会忘记放下举起的手,这导致了各种混乱。
此外,顶部的这些按钮其实是互斥的,这意味着你不能在“鼓掌”的同时发送“爱心”,这似乎不太合理。更奇怪的是,点击按钮的反馈效果也是互斥的。这意味着如果你先点击“赞同”按钮,再要求演讲者放慢或加快速度,最后去“喝一杯咖啡”,后点击的按钮往往会取消前一个按钮的效果。更奇怪的是,有些按钮会被计数,你点击之后屏幕上会显示“xN”,代表点击了 N 次,而有些按钮则不会。
因此,Zoom 的界面存在很多不一致的设计。如果我们想解决这些不一致的问题,并思考如何以更系统的方式设计 Zoom,可以尝试识别其底层概念,并将它们分离成更健壮、更简单、更引人注目的概念。我可能会将其 Zoom 的底层概念分解为以下四种:
1. 反应概念 :这本质上就像发送一个小的情感反应。那些使用 Slack 的人会对此很熟悉。
2. 投票概念 :当有人提出一个问题,我们用赞同或反对来回答。
3. 反馈概念 :你可以告诉演讲者加快或放慢速度。
4. 在线状态概念 :将咖啡杯(表示我离开)按钮和举手按钮整合在一起。
如果让我重新设计这些功能,可能会这样做:在屏幕左下角放置一个单选按钮,让用户可以选择几种状态之一。
最基础的状态,适用于打开麦克风交流的情况:“正在发言 ”。
“正在观看和聆听 ”。当用户选择这种状态之后,可以保持视听,但会自动静音终端音频。
“离开 ”。选择这个状态时,让软件直接静音。
在用户点击“举手”按钮时,它将表示用户正在请求发言,所以需要先将用户静音。
只要演讲者反馈了点击“举手”按钮的用户,该用户举手状态将消失,并进入“正在发言”状态。
因此,我相信有可能围绕一个更连贯的在线状态概念,对所有这些不同的动作进行很好的重新设计。同样,我认为反馈按钮可以被组织成一个单独的概念,其用户界面控件甚至可能不需要总是显示,除非演讲者决定他们想接收反馈。这就是使用概念来思考应用程序用户体验的想法。
用概念驱动编程如今,我们甚至可以运用概念设计的思维进行编程。2023 年有一份关于 GitHub Copilot(AI 编程辅助工具)的报告指出,当前大约一半的代码是由使用 Copilot 的开发者自动生成的。至少在 Python 基准测试上,大模型编码似乎变得非常出色,在许多简单函数上达到了约 92% 的准确率。
但关键是,这些数字都是针对单个函数的,是一种自动补全形式的代码生成——即要求大模型推导出一个单一的函数。当你考虑整个应用程序时,情况就大不相同了。GitHub 的首席执行官 Thomas Dohmke 就说过:“开发者的关键技能在于,‘我需要细化到什么程度才能用 AI 自动生成代码? ’”
我认为,是时候重新思考软件的结构来解决这个问题了。
举例来说,Hacker News(一个著名的科技新闻聚合网站)和许多应用程序一样,它完全由熟悉的功能组成:点赞、评论、发帖、板块还有“声望值”(karma)系统。
不同的网站或论坛对于这个“声望值”有不同的叫法和用途,比如在 Reddit 里这就是一个可以通过良好行为积累的积分点数,而在某些资源网站中可以设置“声望值”的门槛让资源更难被下载。而 Hacker News 的用户声望值只要达到一定水平,就可以对帖子进行点踩(downvote)。
所以重点在于,Hacker News 并没有创造性的变化,却带来了一些创造性的变化。这就是 Margaret Boden 在她的一篇著名论文中所称的“组合性创造力 ”(Combinatorial Creativity),即将熟悉的元素以新的方式组合在一起。
对于 Hacker News 来说,它是标准概念的一些变体。例如,一个帖子只能包含标题、ID 作为链接,或者一个问题,但不能同时拥有两者。又如,评论在两小时后不能被编辑,两周后就不能对帖子进行评论。这些聪明的小规则确保了网站的时效性和新鲜度。然后是特有的声望值规则,例如说,用户需要 501 点声望值才能踩一条评论,或者 30 点才能标记一个评论。
在我看来,这其实是构建 Web 应用程序的新型框架(如图 5 所示)。
图 5 构建 Web 应用程序的新型框架
每个概念都将是一个小型后端堆栈,包含了概念的行为。这些都是构成应用场景的用户操作的程序。然后是一个数据库来支持概念的状态。
关键点是,所有这些概念之间没有依赖关系,因为概念是完全独立的,并且以独立的方式定义。这真的是概念中的大想法。概念之间的连接只发生在路由中,HTTP 端点通过我称之为行为同步的方式连接到概念。这恰好对应于我展示的不同概念的应用场景之间的这些链接。
有了这个想法之后,实际上还可以用它生成代码(如图 6 所示)。
图 6 概念驱动代码
关键在于,我们将采用提示词(Prompt),为每个概念生成代码,完全独立于其他每个概念。这意味着我们不必担心随着应用程序变大而增长的上下文。但每个概念都可以独立实现。然后要做的是,为路由编写一个提示,将再次独立地为每个路由生成路由代码。
当然,正如我先前提到的,路由需要做的是调用概念的行为。所以还需要使用大模型从生成的代码中提取每个概念的 API。然后路由将使用这些概念 API 来合成路由代码 —— 最后,记得对前端也故技重施一遍,把它包装起来,进行部署。
代码生成将成为可以交给机器完成的工作总结来说,我第一个想法是创新简化应用场景 。例如,在群聊的概念中,场景的简化体现在发送消息的人不必添加所有收件人。如果我们回顾软件领域的所有创新,你会发现几乎所有的创新都涉及这种应用场景的简化。
其次,虽然我们经常从用户界面的角度思考,但软件设计实际上是从功能开始的,而概念帮助我们构建它 。比如说 Zoom,我认为会议链接的概念对 Zoom 的本质和成功真的至关重要。
再比如说网络,我认为 URL 的概念可能比任何其他概念更能解释它的成功。我相信 Tim Berners-Lee(万维网的发明者)也认为 URL 比其他任何概念都重要。
最后,我想强调三个要点:
第一,模块化是关键 ,正是因为概念是完全独立的,我才能够独立地讨论这些概念。正是因为它们是独立的,我们才能理解应用程序,
第二,提取出熟悉的概念 。许多概念是通用的,在我们进行的代码生成中,根路由和同步是需要定制的部分。当你进行概念设计时,一个重大创新是它允许你将熟悉的部分与真正新颖的地方分开。
第三,告别敏捷开发 。多年来,很多人一直相信代码就是王道,规范其实并不重要,前期的大规模设计是个坏主意。但我认为,既然大模型真的很擅长生成代码,那人类的工作将更多地集中在编写提示、进行设计和塑造概念等方面。代码生成将成为可以交给机器完成的工作。
大模型刷新一切,让我们有着诸多的迷茫,AI 这股热潮究竟会推着我们走向何方?面对时不时一夜变天,焦虑感油然而生,开发者怎么能够更快、更系统地拥抱大模型?《新程序员 007》以「大模型时代,开发者的成长指南」为核心,希望拨开层层迷雾,让开发者定下心地看到及拥抱未来。
读过本书的开发者这样感慨道:“让我惊喜的是,中国还有这种高质量、贴近开发者的杂志,我感到非常激动。最吸引我的是里面有很多人对 AI 的看法和经验和一些采访的内容,这些内容既真实又有价值。”
相关问答
做嵌入式软件的一般用什么软件写和看 代码 比较好?我认为应该学习一下51和STM32,但是仅限于学习它的原理并了解它的应用,不用用来做太复杂的东西。利用51和STM32巩固自己的C基础、数据结构、计算机组成原理等基...
电路板上的 代码 各代表什么元件呢?电路板上的代码代表不同的电子元件。电路板上的代码其实指的是电子元件的值、功率、型号等。这些代码不同表示的是不同的元件。例如,R代表电阻,C代表电容,D代...
microsoft的 电子代码 是什么?Microsoft的电子代码指的是Microsoft公司所开发和使用的电子产品的源代码,也可以称为Microsoft的软件代码或程序代码。Microsoft是一家全球领先的科技公司,涉...
你好,机打发票时候,出现错误 代码 410768,应该怎么解决?_快账金税设备事件代码410768解决办法如下;金税设备事件代码410768,说明离线限额了,就是你开了发票都是没有上报的,需要你手动上报一下,没有网络开的也...
电子 邮件的 代码 是什么?电子邮件的代码是一种用于传输和交换电子邮件的协议。最常用的电子邮件代码是SMTP(简单邮件传输协议),它用于发送邮件。另外,POP3(邮局协议版本3)和IMAP(...
EDA哪些软件比较好?都有哪些优点?EDA工具层出不穷,目前进入我国并具有广泛影响的EDA软件有:EWB、PSPICE、OrCAD、PCAD、Protel、ViewLogic、Mentor、Graphics、Synopsys、L...
电子 票据 代码 是什么?通行费电子票据的票据代码为8位,编码规则:第1-2位代表财政电子票据监管机构行政区划编码,第3-4位为02,第5-6位为99,第7-8位代表财政电子票据年度编码。票据号...
电子 元器件etf 代码 是多少?代码是515260我认为未来随着汽车电子市场进一步上行、消费电子市场逐步回暖,电子行业将迎来逐步复苏。电子芯,中国造!上交所首只电子ETF(515260),覆盖26...
microsoft账户安全 代码 怎么接收?你可以在注册Microsoft账户或者在更改账户安全设置时,选择使用“短信验证”或“手机通话验证”的方式接收安全代码。当你选择“短信验证”时,你会在手机上...
开票组织机构 代码 如何填?组织机构代码是三证合一以前的,当时有组织机构代码证、税务登记证、营业执照。三证合一以后统一使用全国企业统一信用代码,改代码既是组织机构代码、营业执照...