查看原文
教育与科研

【第3066期】@Malte Ubl :扩展前端应用开发的原则

排骨AI 前端早读课 2023-09-22

前言

Vercel 的首席技术官 @Malte Ubl 在 2023 React 峰会上做了一场《 扩展前端应用开发的原则》的分享。今日前端早读课文章由 @排骨 AI 分享。

正文从这开始~~

在谷歌工作了十多年后,现任 Vercel 的首席技术官 Malte Ubl 对于负责团队的软件基础设施并不陌生。然而,负责定义人们如何编写软件,进而构建他们用来编写软件的基础设施,会面临重大挑战。Malte Ubl 的这个演讲将揭示领导大型软件基础设施的指导原则。

视频摘要

本次演讲讨论了通过原则来扩展前端应用程序,如拆除障碍、在单一代码库中共享代码以及使删除代码变得容易等。演讲还强调了渐进式迁移、接受知识的不足以及消除系统复杂性的重要性。演讲突出了在代码迁移中使用自动化的作用,以及消除障碍以实现更顺畅的代码迁移的重要性。

介绍和背景

大家好吗?这是我第一次参加会议,经过一段休息后回来了。我曾经构建过大型的 JavaScript 应用,现在是 TypeScript。React 已经发展壮大,现在对每个人都可用。我加入了巴西,并发现了一个内部的 Dex 团队。我们将它转变成了一个产品团队,以解决常见的问题。我们宣布了 Versell Spaces,以实现讨论的概念。

大家好吗?谢谢大家谈论 JSConf 等相关话题。我们明智地决定不举办 2010 年的活动,因为无论如何都不会成功,这实际上是我参加的第一次会议,至少如果你算上社区会议的话,在每个人生活中都有一个较长的休息期后我才回来的。我来自旧金山。之前我实际上在德国,仍然有点时差,但我正在尽力在这里带来一些活力。

因此,略微回顾一下,五年前在 JSConf 澳大利亚,我做了一个演讲,我从这个句子开始说:“你好,我曾经构建过非常大的 JavaScript 应用程序。” 当时我仍在谷歌工作,而且我正在担任一种高管角色。所以我实际上并没有亲自构建东西,但我学到了一些东西,所以我认为给个演讲是个好主意。但那是那时的事情了。我回来了。我实际上又开始构建 JavaScript 中的东西,非常大的应用程序。显然,今天没有人再这样做了。现在都是 TypeScript 应用程序,就在这里。

在那个演讲中的另一张幻灯片是我认为 React 很不错。当时的背景是,10 年前,在谷歌我构建了一个名为 Wiz 的 JavaScript 框架,我在考虑是否要将其开源,然后就在那个时候,React 出现了,看起来非常出色,我认为世界不需要这个。我会把这个留给自己。我现在认为那可能有点错误,因为关于应用程序可扩展性的一些好的贡献可能对生态系统有价值,即使只是展示出来。但现在我在 Brazil,我可以帮助推动 React,我认为有关服务器组件等方面的内容,我们在谷歌那个时候考虑的情况已经发生并且现在对每个人都可用,这太令人惊叹了。很酷,好吧。

所以我加入了 Brazil,我与所有团队交流,发现我们有一个内部的 Dx(开发者体验)团队。所以我和他们交流,你们在做什么?他们向我解释了他们正在做的事情。我认为这听起来是个很好的主意。然后我去与客户交流,我们的客户告诉我,他们的问题是我们的内部 Dx 团队正在解决的问题。所以我想,为什么不将这变成一个产品团队,并使其对每个人都可用,这样不是每个人都必须一遍又一遍地解决相同的问题。现在,这在某种程度上是相关的,因为我当时给出的演讲是如此理论重的东西,因为你知道,我没有什么开源的东西。我只能告诉大家,这是我学到的东西。但你必须自己想办法将这些知识转化为实际的东西。现在,如果你想自己构建,那就更好了。你知道,我们几周前宣布了一个名为 Versell Spaces 的产品,试图以可重用的方式实现我将要谈论的一些东西。

扩展前端应用程序

所以让我们像 Google 或 Vercel 一样发布软件,因为现在是 2023 年。但我认为这个说法实际上是正确的,迭代速度可以解决所有已知的问题。我的意思是,当我们开发软件时,随着时间的推移,我们会犯错,我们必须以专业的方式来处理这个事实。本次演讲的其余部分将讨论如何通过构建的软件和在团队中建立的机制来实现超越自己,从而使整个团队变得更好。我想谈谈扩展前端应用程序的原则。我有六个原则。让我们从第一个原则开始:消除障碍。

好的,让我们像谷歌或 Vercel 一样在 2023 年发布软件。但说到谷歌,有个叫做埃里克・施密特(Eric Schmidt)的家伙,他曾长时间担任 CEO,至少在内部,也许我泄漏了一些东西,抱歉,他总是说 “收入解决了所有已知问题”。基本上是在说,如果你只是赚更多的钱,其他你在做什么都不太重要。但我认为这是错误的。

当然,我们没有无限的钱,你也没有无限的钱。我认为谷歌也不再有无限的钱。所以这不是一个很好的座右铭。但我认为这个座右铭是真实的,迭代速度解决了所有已知问题。我的意思是,当我们制作软件时,实际上今天的第一个演讲也有类似的观点,随着时间的推移,我们会犯错误,对吧?我们唯一知道的是未来是不确定的,我们必须专业地处理这个事实。以专业的方式处理的方法是,在你犯错误的那一刻能够做出反应,迭代并在第二次做正确。所以,有了这个,让我们思考一下我们在其中的角色,在接下来的演讲中。

那么,资深工程师,或者我真正的意思是小写的资深工程师,对吧?我们已经有点经验了。我们要做些什么呢?你可能会在今天稍后的一个关于职业建议的会议上听到一些东西,他们可能会告诉你,你想要产生更多的影响力,你需要超越自己,而人们通常与此相关的是要么承担管理责任,要么至少告诉其他人该做什么。这是关于人际关系的事情,对吧?因此,这个演讲的其余部分与这个非常不同。它关于如何通过你建立的软件以及你在团队中建立的机制来超越自己,使整个团队变得更好,但通过你正在构建的软件。所以在接下来的这些幻灯片中,基本上把自己看作是你的虚构的平台团队的虚构的领导,为你的虚构的工程团队,我不知道,12、50、100 人的工程团队,你就是那个人,你的任务是使团队更好地扩展。很酷。那是一个很长的介绍。不管怎样,我想谈一谈扩展前端应用程序的原则。我有六个。我有一个额外的,但我觉得我太慢了。所以我会讲六个。好吧,让我们从这个开始。打破障碍。这实际上是一些我当时并不清楚会产生多大影响的事情。正如我所提到的,我在谷歌工作了很长时间。我没有使用每个人都在使用的工具,但我在做这个会议,JSConf。我自己参加这些聚会,我会听到台上的人对小模块感到非常兴奋,对吧?有一家叫做 NPM 的公司,推出了一种叫做私有模块的东西。

代码共享和单一代码库

如果你想获得一剂肾上腺素的刺激,那就进行单一代码库(monorepo)迁移。这绝对是值得的。它鼓励协作并降低了协作的门槛。GitHub 的代码所有者(code owners)解决了可扩展性问题。作为我们 Spaces 产品的一部分,我们正在推出一个名为 "owners" 的东西,它基本上就像代码所有者(code owners),但更加优秀。

因此,人们会在他们的公司通过将模块发布到 NPM 来共享代码。所以我加入了 Vercel,我看到这种情况发生,比如某个团队会说,嘿,你有一些很酷的代码,你能发布到 NPM 上,这样我就可以使用它吗?然后我必须更新我的 package JSON 到你的新版本。也许有人在帮助我,也许我正在自动执行这个过程。这是一个非常糟糕的过程,如果你有一个较大的团队,规模扩大后,这真的会造成困扰。所以我有一个有点奇怪的建议,如果你还没有尝试过,但想要获得一剂肾上腺素注射,那就迁移到一个单一代码库(monorepo)吧。这真的很值得。当然,显然它也有权衡之处。在软件工程中的一切都伴随着权衡。几个月后,你会注意到,哦,每个人都可以更改任何东西,也许这不是地球上最好的想法。但拥有一个鼓励协作并将协作的门槛降低的基础是有好处的,你可以处理在路上遇到的问题。例如,GitHub 的代码所有者(code owners)解决了某些可扩展性问题。不过,奇怪的是,在 GitHub 的其他很棒的产品中,代码所有者似乎不是特别出色。因此,作为我们 Spaces 产品的一部分,我们实际上正在推出一种称为 owners 的东西,它基本上就像代码所有者,但更好。所以去看一下吧。

简化代码删除和数据获取

使删除代码变得容易是在管理大型代码库中至关重要的。通过鼓励工程师尽量减少代码,包括删除不必要的代码,我们可以维护一个更高效和可扩展的应用程序。使用像 Tailwind 或 CSS-in-JS 库这样的工具可以轻松删除代码,因为 CSS 与代码共存。同样,组件内部的数据获取,例如在 Next.js 13 中,减少了不必要的代码,并提高了性能。尽管存在一些怀疑,但这种方法已经成功地应用于较大的 Google 应用程序,证明了其可扩展性和有效性。

好的,对于第二个原则,我称之为使删除代码变得容易。因为关于任何大型代码库的一个真实情况是,它会变得越来越大。在某种程度上,这是好事,对吧,就像写代码是我们的工作,我们会写代码,你无法阻止它。但我们可以再次作为平台领导者,专业地管理和鼓励我们的工程师尽可能少地添加代码,包括删除代码。

现在,如果我们都考虑一下我们的代码库,里面可能有一些东西,对吧?有一些明显不应该在那里的代码,你知道,2017 年之类的。也许最初它是一个 React 组件,我们摆脱了那个 React 组件,对吧?我们有一个 CSS 文件,也为我们的 2017 新年消息做出了贡献。谁知道那个选择器是否可以删除,对吧?这真的几乎是一个停机问题类型的问题,所以它一直存在。另一方面,如果你使用像 Tailwind 或 CSS in JS 库这样的东西,因为你的 CSS 与代码共存,你可以有很高的信心直接删除整个东西,摆脱它。再次强调,作为领导者,你应该有这种思考方式,说这就是为什么我选择使用像 Tailwind 这样的东西,因为它实际上使删除代码变得更容易。作为这种思考方式的另一个很好的例子,我认为是 Next.js 13 中组件内的数据获取。在这个例子中,我们有一个 Tweet 组件,对吧?我们只传递了 ID,Tweet 组件自己获取自己的数据。现在,如果我们删除了 Tweet 组件,一切都消失了。还记得这在类似 Next.js 的旧版本或者 remix loaders 中是如何工作的吗?数据获取被提升到路由的顶部。因此,如果我删除了 React 渲染树中很远的某些内容,我必须记住也删除数据获取代码。如果你有一个庞大的团队,有些人会有时候忘记,这不仅会使你的应用程序变得更大,并且有不必要的东西,还会使它变得相当慢。因此,对于数据获取,你可以引入成本,因为你调用了某个后端系统,实际上你不必调用。所以,再次强调,这与 CSS in JS 的思想相同,只不过这是针对 JS 中的数据获取。我知道有些人会说,哇,这不可行,等等。我认为在谷歌的一个我最自豪的成就之一就是引入了这种方法。因此,在过去的八年左右中,你使用过的任何较大的谷歌应用程序都在使用这种技术,其中数据获取是在渲染树中进行的,而不是以某种提升的伪可扩展方式进行的。

渐进式迁移和应用标准

始终进行渐进式迁移。从一开始就计划逐步迁移。以 Next.js 13 的应用路由器为例。一次迁移一个路由。向 Next.js 团队致以嘉奖。第四个原则:始终进步,永不倒退。引入 Lint 规则以确保应用程序标准。注释可以作为技术债务的记录。

好的,第三个原则。始终采用渐进迁移。前几天我在 Twitter 上开玩笑说,基本上只有两种迁移方式。有渐进迁移,还有失败的迁移。有时你开始有一个大计划,你会说,我们一次全部搞定。但我认为更常见的情况是,也许你已经进行了三个月,你的老板走过来说,这一切都很好,但我们不能再等待六个月来证明这将有效,我下周或下个月需要发布一些东西,对吧?因此,可能最初是一个潜在的大爆炸式迁移最终通常会变成渐进迁移,所以最好从一开始就计划好。这只是一个示例,说明了用于 API 设计的类型思维,以使渐进迁移成为可能。我认为另一个很好的例子再次是 Next.js 13 的应用路由器,你可以逐个迁移单个路由,将其移入新的世界,对吧?没有人强迫你选择旧 API 或新 API,你可以将该页面放在这个 API 上,将该页面放在那个 API 上,并通过代码库自行解决。甚至在这个项目的更深层次上,你知道,这里有一些 get service at props,传统的。我应该说传统吗?我不知道。不管怎样。旧 API,对吧?你完全可以继续使用。但是,这就是它的样子,然后你可以执行第一个迁移步骤。请注意,这是一个微不足道的迁移。对吧?几乎什么都没有改变。我们只是将 get service at props 中的代码放入了顶级组件中。现在,比以前好吗?不,不是。对吧?但它是一个渐进步骤,你不必重写整个渲染树来利用一些新的功能。再次强调,这只是一个示例,说明了如何创建可以按照逐步方式迁移的 API,而不是一次完成所有工作。当然,再次向 Next.js 团队致敬,我认为他们做得很好。酷。接下来是第四个原则。始终进步,永不倒退。从某种程度上来说,这实际上与渐进迁移的主题有点相关,但更一般一些。所以一个确保你的应用程序具有一定标准的好方法是引入 lint 规则,对吧?这些规则告诉你,在我的应用程序中,你必须按照这种方式做,而不是其他方式。但有时会发生一些不太好的事情,比如这样的情况,顺便说一下,我只是随机从 Vercel 的内部超级机密代码库中复制粘贴的,如果有什么有趣的东西在里面,我很抱歉。实际上有趣的部分是注释,这些注释基本上在各个地方禁用了 lint 规则,对吧?实际上我认为这是正确的方式,我会谈一谈为什么这是正确的方式,但显然在你的代码库中添加这些没有价值的注释很糟糕,对吧?但从某种程度上说,这些注释现在就是技术债务的分类账,对吧?技术债务通常是一个超级抽象的东西。

允许列表和接纳不足的知识

对于我们的 Spaces 产品,我们使用外部允许列表以避免在代码库中乱七八糟。规则在新代码上的价值更高。接纳不足的知识,并以机器可读的方式编码代码库信息。例如:Next.js 中间件和使用允许列表来确保上下文特定更改的批准。

好的,现在来谈谈第四个原则,始终进步,永不倒退。从某种程度上来说,这实际上与渐进迁移的主题有点相关,但更一般一些。所以一个确保你的应用程序具有一定标准的好方法是引入 lint 规则,对吧?这些规则告诉你,在我的应用程序中,你必须按照这种方式做,而不是其他方式。但有时会发生一些不太好的事情,比如这样的情况,顺便说一下,我只是随机从 Vercel 的内部超级机密代码库中复制粘贴的,如果有什么有趣的东西在里面,我很抱歉。实际上有趣的部分是注释,这些注释基本上在各个地方禁用了 lint 规则,对吧?实际上我认为这是正确的方式,我会谈一谈为什么这是正确的方式,但显然在你的代码库中添加这些没有价值的注释很糟糕,对吧?但从某种程度上说,这些注释现在就是技术债务的分类账,对吧?技术债务通常是一个超级抽象的东西。

【第2333期】如何设计和架构渐进式微前端

但在这里你有一个列表。你想要修复的任务清单。再次强调,这对你的代码库来说有点不好,这就是为什么对于我们的 Spaces 产品,我们使用的是这些外部允许列表,不会在你的代码库中添加垃圾。你基本上有一个 JSON 文件,上面有一些规则,而在这个文件中,违反规则是可以的。

回到变得更好而不是变得更糟的主题,你真正想看到的是你团队的代码库是这样的图表。这些是随时间推移的违规情况。你希望这个图逐渐下降,然后你会看到这些上升的步骤。这实际上并不坏,这是当你引入新规则时发生的事情。你允许所有现有的违规情况,然后随着这段代码实际被触碰,它会随着时间的推移变得更好。我想鼓励每个人都允许这些东西。现在迁移一切并将其放入新状态并不重要。原因是,这个在你的应用程序上的规则的边际价值在新代码上比在旧代码上高得多,因为你的旧代码经过了战斗测试。它经历了 QA 测试,经历了生产故障,经历了用户对 "名称" 有创造性想法。你已经解决了所有这些问题,对吧?因此,你可能实际上不会找到任何有趣的东西。这些规则存在的原因是,当你编写新代码时,它会立刻告诉你,嘿,你犯了一个错误,去立即修复它,对吧?而且这样做也很便宜。这就是为什么将这些观点迅速纳入你的代码库以供未来代码使用非常重要,现在没有必要把生活中的一个月时间用于迁移你已经有的一切到新状态。

所以我在抽象层面上稍微谈到了这样的规则,给出了一个 lint 规则的示例,但实际上我想谈谈比 lint 规则稍微高级一点的东西,与代码风格等相关。回到原则,你知道,拥抱不了解。再次强调,你是这个更大团队的领导,团队中会有一些新成员,会有一些更年轻的人,他们可能会缺乏有关他们不了解的代码库的上下文,他们可能不知道或错过了,或者他们没有看备忘录,对吧?这都会发生。作为团队的领导者,我们必须接受他们可能没有那个上下文。所以这就是为什么将你对代码库的许多了解以某种机器可读的方式编码是很重要的原因。我这里有一个示例,说明了这是什么意思。而且这远远不止是关于 linting,还涉及到应用程序设计观点等更多内容。这个规则,再次强调,是从我们自己的内部代码库中提取的。所以我们使用 Next.js 中间件,这是一个非常强大的工具,对吧?它几乎在我们提供的所有请求上运行。这意味着如果我们的中间件很慢,我们的站点将会非常慢,对吧?你可以通过从某个其他服务获取一些内容来使你的中间件变得非常慢。另一方面,从某个其他服务获取一些内容可能正是你想要做的,对吧?因为这是你做有趣事情的方式,比如调用服务。现在,这意味着有时候你必须调用服务,但有时候这不是一个好主意,对吧?因此,通过这种允许列表机制,你可以说,默认情况下,进行这种更改需要得到具有在这个上下文中做出此决策的上下文的人的批准。这将允许的典型外部于代码之外的机制与我们的所有者机制相结合,其中允许列表的所有者不是编写代码的同一个人,因此你可以轻松强制执行,你的架构师或者你的平台团队的领导去看看并验证在这种情况下,是的,这是可以接受的用法。然后再次强调,这不是像 lint 规则那样最终可能需要有统一的代码库。在这里,你只需确保我们已经看过了,这确实是我们想要做的。

消除系统复杂性

消除系统复杂性在构建可扩展的应用程序中至关重要。分布式系统中常见的一个问题是版本不一致,即客户端和服务器不在同一个版本上。这在引入 API 中的新字段时可能会导致问题。为解决这个问题,像 Zot 库和 TRPC 层这样的工具提供了解决方案,但可能会很复杂。在 Vercel,我们引入了一个无服务器产品,可以提供站点的不同版本,确保客户端和服务器始终保持同步。这完全消除了问题,使大规模构建应用程序变得更容易。

非常好,来到最后一个原则,消除系统复杂性。再次强调,你是组织中领导平台团队的人,你看到人们一直在努力解决的问题。我认为不时地退后一步,列出人们一直在努力解决的问题的目录,并尝试通过引入某种抽象来一劳永逸地解决这个问题,让人们不再担心它,是很重要的。

我认为这种问题的一个很好的例子是,至少在大型标签中被称为版本不一致。版本不一致是在分布式系统中发生的一种情况,当你有一个客户端和一个服务器,它们不在同一个版本上,对吧?现在,你可能会说,你知道,我是一个前端工程师,我不写分布式系统,但你错了。实际上,你在做困难模式,对吧?因为在一个数据中心中,你可以控制部署方式,对吧?你知道它是如何工作的。你可以制定规则。另一方面,当你有一个 Web 客户端和一个服务器时,你知道,当 Web 客户端重新部署时,你无法控制它,对吧?所以,它访问你的站点,它可能会停留在那里,对吧?至少在一段时间内。与此同时,你重新部署了服务器。所以现在,你有了这个旧客户端、新服务器,你在 API 中引入了一个新字段,客户端一无所知,对吧?所以,这是一个你现在必须管理的问题。

现在,几乎有一个完整的生态系统围绕着这个问题。比如,TypeScript 的 Zot 库或者其上层的 TRPC 层,它允许你表达关于你的 API 的期望,并在不满足这些期望的情况下处理问题。但这是很复杂的,对吧?有很多事情你必须要做。如果你没有得到这个字段,你应该怎么做呢?你真的知道该怎么做吗?这是一个难题。所以,在 Vercel,我们看到了这个问题,我们在想,我们是否可以再次消除整个系统性的问题?所以我们做的是,我们说,嘿,我们有这个无服务器产品,对吧?我们不只有一个具有最新版本的服务器。我们实际上可以为你的站点提供比最新部署更旧的版本。所以,我们现在有一个非常实验性的版本,但在接下来的几周内我们将推出它,你可以选择在 Next.js 中启用一个模式,其中响应你的客户端的服务器将始终与客户端所在的版本完全相同,这意味着你可以完全忘记这个问题。你永远不必再担心它。特别是对于服务器操作来说,因为它们已经像函数调用一样了,现在它们真的像函数调用一样,因为你实际上知道你正在调用哪些函数,对吧?它不是某个抽象版本的某个名为这样的东西,但你不知道是哪一个。它将完全是那样的。所以,这只是一个示例,说明了你可以做的一些事情,使构建大规模应用程序更容易。

太棒了,这就是我今天要说的所有内容。这就是整个列表。我不必再次浏览它。我认为我们有问答环节,也许对问题和其他方面有所帮助。非常感谢。我们可能有时间回答一两个问题,所以让我们开始吧。

自动化代码迁移和消除障碍

使用 AST(抽象语法树)自动化代码迁移可以是有益的,即使是进行大规模的更改。Google 的机器人 Rosy 通过将一次性的 15,000 行更改转换为多个一行的更改,逐步应用以避免潜在的错误。这种方法消除了障碍,使代码迁移更加顺畅。

这是一个非常有趣的问题,我想进一步展开一下。问题是,你提到了始终进行渐进式迁移,但关于使用 AST(抽象语法树)自动迁移大量代码,你有什么看法?并谈一谈何时值得自动化?它是一个好处还是一个分散注意力的东西?不,自动化肯定是值得的。即使你自动化了一个更改,我仍然会应用这个原则。在 Google,有一个名叫 Rosy 的机器人,它可以将你的 15000 行更改变成 15000 个一行的更改,并管理以渐进的方式将其应用到你的代码库中,这样你就不必最终说,哎呀,我犯了一个错误,不得不回滚 15000 行的更改。它以递归方式应用更改,这真的很酷。消除障碍,Google。

【第2405期】你所见过的最简单的AST入门

关于本文
译者:@飘飘
作者:@Malte Ubl
原文:https://www.youtube.com/watch?v=tqhLK0Fb5_4

这期前端早读课
对你有帮助,帮” 
 “一下,
期待下一期,帮”
 在看” 一下 。

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存