cover of episode Node.js and the Javascript Ecosystem with Gil Tayar

Node.js and the Javascript Ecosystem with Gil Tayar

2024/11/28
logo of podcast Software Engineering Daily

Software Engineering Daily

People
G
Gil Tayar
Topics
Gil Tayar: 本次访谈主要围绕Node.js生态系统,模块化和Monorepo展开。他分享了自己在软件开发领域的35年经验,以及在Microsoft, Wix等公司的工作经历。他强调了模块化在软件开发中的重要性,并详细阐述了其在Node.js项目中的实践经验,包括如何构建独立的包,如何管理大型Monorepo项目,以及如何处理ESM和CommonJS模块系统之间的兼容性问题。他还分享了自己对前端开发中非标准import语句的看法,以及对Node.js加载器的理解和应用。 Josh Goldberg: 作为访谈的主持人,Josh Goldberg与Gil Tayar就Node.js生态系统,模块化,Monorepo,ESM和CommonJS模块系统,以及前端开发中非标准import语句等话题进行了深入探讨。他引导Gil Tayar分享了其在这些领域中的经验和观点,并就一些技术细节和问题进行了提问和讨论。

Deep Dive

Key Insights

How did Gil Tayar get into software development?

Gil started programming at the age of 13 with a ZX81, moving on to Turbo Pascal and eventually joining the army as a software engineer, where he spent six years as an instructor. He then transitioned into industry roles at companies like Wix and Microsoft.

Why does Gil Tayar enjoy mentoring and teaching others?

Gil enjoys mentoring because he finds it fulfilling to help people understand complex concepts in a structured and logical way. He believes in starting with context and motivation before diving into the details, which helps learners grasp the material more effectively.

What is Gil Tayar's approach to explaining complex programming concepts like promises?

Gil's approach involves breaking down the problem step by step, starting with the basics and gradually building up to more complex ideas. He emphasizes the importance of understanding the context and motivation behind each concept before diving into the technical details.

Why does Gil Tayar prefer not to transpile code in Node.js?

Gil dislikes transpilation because it adds complexity to the development process, turning it into a pipeline. He prefers to avoid transpilation and instead uses JSDoc typings for type checking, which works well in most cases.

What is Gil Tayar's opinion on monorepos versus polyrepos?

Gil prefers polyrepos because each package can have its own independent configuration, making it easier to reason about and modify. In contrast, shared configurations in monorepos can become overly complex and difficult to manage, especially at scale.

How does Gil Tayar handle dependency updates in a polyrepo setup?

Gil suggests updating dependencies only when developers modify a package, using a script to automate the process. This approach ensures that dependencies are kept up-to-date without the need for a shared configuration across all packages.

What is Gil Tayar's view on the Node.js ecosystem and NPM?

Gil considers the Node.js ecosystem unparalleled in terms of productivity and the scale of NPM, which has enabled developers to iterate and share code at an unprecedented level. While it has its problems, NPM remains an amazing success story.

Why does Gil Tayar advocate for ESM (ECMAScript Modules) in Node.js?

Gil believes ESM is crucial for the future of the Node.js ecosystem because it standardizes module systems, reducing the mess created by the coexistence of ESM and CommonJS. He sees ESM as a necessary evolution to simplify the ecosystem and improve developer experience.

What are the challenges of requiring ESM modules in CommonJS?

Requiring ESM modules in CommonJS can lead to issues with top-level await, as CommonJS cannot handle async imports. Additionally, allowing CommonJS to require ESM could delay the adoption of ESM across the ecosystem, which Gil sees as a negative.

What are some use cases for Node.js loaders?

Node.js loaders can be used for transpilation (e.g., TS-node), mocking modules in tests, and handling different protocols like HTTP or loading from zip files. They provide a standardized way to intercept and modify module loading behavior.

Chapters
Gil Tayar's journey into software development started at age 13 with a ZX81, progressing through Turbo Pascal on an Apple II and a software engineering course in the army. He recounts his experiences working at various companies like Wix and CloudShare, and his current role as a software engineer at Microsoft. He highlights his passion for staying updated with industry trends and his enjoyment of mentoring.
  • Started coding at age 13 with a ZX81
  • Army software engineering course and six years as an instructor
  • Worked at Wix, CloudShare, and currently at Microsoft
  • Enjoys mentoring junior developers

Shownotes Transcript

<context>Node.js 和 JavaScript 生态系统与 Gil Tayar

<raw_text>0 Gil Tayar 是微软的首席软件工程师、开发者倡导者和会议演讲者。Gil 对 Node.js 生态系统的贡献包括为 Node.js、Tomoka 和 Test Double 添加对 ECMAScript 模块的支持。他加入节目讨论他在软件工程方面的历史、单一代码库与多代码库、JavaScript 的现状等更多内容。本集由 Josh Goldberg 主持,他是一名独立全职开源开发者。

Josh 在 TypeScript 生态系统中从事项目工作,最著名的是 TypeScript ES Lint,这是使 ES Lint 和 Prettier 能够在 TypeScript 代码上运行的工具。Josh 还是 O'Reilly 出版的《学习 TypeScript》一书的作者,是微软开发者技术的 MVP,并且是 Twitch 上的实时代码流媒体。可以在 Blue Sky、Mastodon、Twitter、Twitch、YouTube 和 .com 上找到 Joshua K. Goldberg。♪

Gil,欢迎来到软件工程日报。最近怎么样?很好,谢谢你,Josh。谢谢你邀请我。我对此非常兴奋。你在这个行业已经待了一段时间,涉及了很多领域,比如微软、Wix 和 Node.js。但你能告诉我们,你是如何进入软件开发这个美妙世界的吗?嗯,在我13岁的时候,我是犹太人,所以要举行成人礼。

我向父母请求了一台个人电脑。当时,最顶尖的个人电脑是 ZX81,只有 1K 的内存。

我把它升级到 16K,并通过这种方式学习了基础语言。从那时起,我转向了 Apple II 上的 Turbo Pascal,在那里我参与了 Anders Helmsfeld 开发的精彩 IDE,他至今仍是软件行业的杰出人物。现在我们都在同一家公司工作,微软。

然后是军队。我作为软件工程师加入了军队。在那里我参加了一个软件工程课程,类似于九个月的训练营。我在软件工程学校担任了六年的讲师。所以我继续担任讲师。从那时起,我很容易就进入了行业。像 Magic 这样的公司,今天没有人记得。

但绝对是 Wix。我甚至在 2000 年,也就是互联网泡沫高峰期,创办了自己的公司,大约在一切崩溃前一年。但我们幸存下来,公司以微薄的价格出售。从那时起,我在 Wix、CloudShare 工作,今天我的角色是在微软担任软件工程师。所以大约 35 年。是的,差不多。哇。

我记得在一次会议上,有人提到你是他们在军队里的讲师。听到你教过的人现在也在会议上发言并在行业中扮演重要角色,感觉如何?是的,我在军队教过她,Tali Barak,她也是一位出色的演讲者。我教过很多人,并且指导了很多人,不仅仅是在军队,这真的很久以前的事,但我还曾经做过一次前端训练营。

所以,我教了大约 10 个人,我们有时仍然会聚在一起,回忆往事。我指导了很多初学者,帮助他们找出如何进入行业,因为至少在以色列,初学者很难进入这个行业。

所以我帮助他们。我侄子和侄女在我的一点帮助下进入了软件行业。所以是的,很多人,这很有趣。我喜欢指导和帮助人,无论他们是初学者还是资深人士。在某种程度上,这就是我所做的。这是我喜欢的。你是如何保持与他们相同的心态的?你是如何能够与这些带着如此不同技术、人员和环境的人产生共鸣的?

我的意思是,我不再用 COBOL 编程了。好吧,我从来没有用过,但可以这么说。我保持对所有趋势和一切的关注,因为这正是我热爱的事情。我不是因为需要而这样做。我这样做是因为对我来说这很迷人。我经历了很多次革命。ZX81,所以是个人电脑革命。我仍然记得...

在军队里,我想去做个人电脑的工作,而不是大型机和小型机,那时这些都是热门。我告诉他们,等着瞧。这就像一个预言,它实现了。

所以是的,我经历了很多次革命,这很迷人。计算机行业和软件行业,面向对象编程革命、函数式编程革命、网络革命、移动革命,现在是人工智能革命。这真的很神奇,也非常有趣。所以我保持着前端的关注。我开始...

大约在 2000 年,编写前端代码时,你必须有一个包含大量 JavaScript 的大文件,你能做的事情非常有限。然后 Knockout 出现了,然后是 AngularJS,接着是 React。看到为同样的问题提出的各种解决方案真是太迷人了。我就是喜欢这一点。所以当人们来找我时,我知道我在说什么。我们在同一波长上交流。

不过,他们年轻。是的,我想稍微重新表述一下这个问题。当你对某件事情有深入的专业知识时,可能很难与刚接触它的人保持同理心。他们面临的挑战与你截然不同。例如,当我在 Codecademy 工作时,我们有一些对 C++ 深入了解的专家,他们试图向从未编程过的人解释如何编写 for 循环。

所以你觉得这很困难,还是你有什么策略来教导像侄子或侄女这样的人,尽管你对计算机的所有内容都有深入的理解?是的。

我不认为这是一场悲剧,我喜欢这样做。我总是觉得,我知道很多人在解释时,他们会深入到问题中,而你根本听不懂,因为你已经深陷其中,周围的人都在问,嗯,我们在哪里?我们在哪里?我什么都不懂。但他们并不是因为礼貌或困惑或羞愧而不理解。

我总是想,好的,让我们从背景开始,像事情的上下文一样,慢慢地逐步解释每一个步骤。我真的很努力让它有条理、逻辑和结构。很多人来找我说,哇,这就是应该解释的方式。再次强调,这是因为我退后一步,给出动机,理解上下文,然后慢慢而有逻辑地理解一切。一旦你做到这一点...

就没问题了。我的意思是,这有效。而且不,当我在处理 JavaScript 时,我显然不会解释内存管理。所以你需要理解你所解释的内容的范围。

这真是个好说法。你一定很有耐心。我知道很多人没有足够的情感耐力,能够坐下来一次又一次地向人们重新解释整个领域。有一种说法是耐心。另一种说法是,哦,给我一个舞台,或者一个坐在我旁边的初学者,我可以向他们解释一些东西。我就像,好的。

就像有人带着问题来找我。我就像,好的,让我们从头开始。让我们解释一下 Promise。好的,你在这里做的事情是 blah, blah, blah, blah, blah,等等。我喜欢这样。所以这不是耐心。这是爱。这不是耐心。这是爱。哦,这就来了。作为一个人。是的。很好的生活建议。

为了让我开心,在我们开始深入探讨构建系统和 Web 工具的一些更深层次的方面之前,假设我知道 JavaScript,但我被传送到了回调和回调地狱非常流行的时代。你会如何简要地,30 秒到一分钟,向我解释 Promise 或 Promise A-plus,作为一个想要理解的人?

我有一个关于这个的演讲。2014 年,那是我的第一次演讲。我称之为“从回调地狱到异步天堂,再到承诺的炼狱。”这是一个精彩的演讲,我慢慢地从回调讲到 Promise,

好的。我是怎么做到的?我不再记得了。然后从 Promise,慢慢地我讲到了 yield 的概念,你可以 yield Promise,然后异步天堂,boom。我猜当时 20% 的观众真的理解了这一点。现在可能也是如此。

但我仍然认为这是一个非常有条理的解释方式。你只需解释回调的问题,然后从回调转到 Promise。这个想法是将回调视为一个值,返回 Promise。所以它是一个值。一旦你有了 Promise,然后你扩展到点、点、点、点、点。然后你做 yield,blah, blah, blah,一切都很好。

这很难,但可以做到,或者至少在某种程度上可以做到。是的,我认为你解释的方式与它的技术构建之间有一种美丽的平行关系。因为对于那些没有深入了解 Promise 的人来说,它确实是建立在你有生成器的概念之上,这又推动了 async-await,但为了做到这一点,你需要在底层处理基本的原始 Promise。没错。我今天看到的正好相反。人们在使用 async-await

而他们不知道什么是 Promise。所以他们不使用 .then 和 .catch。一旦你进入 Promise 的世界,你不是在等待某个东西,而是将 Promise 放在一边,然后运行其他东西,他们就会感到困惑。我问过某人,你有一个异步函数,然后你在控制台记录对该函数的调用。它在控制台记录什么?很多人说,假设它只是返回一个五。他们说五,而不是一个 Promise 返回五。

很多人今天,因为有这么多抽象层次,而且今天已经根深蒂固,实际上并不理解 await 和 Promise 之间的联系,更不用说回调了。如果你在团队中工作,想帮助人们理解这一切是如何构建的,你有什么特别的策略或方法吗?慢慢来。总是慢慢来。

你说耐心。最终,如果你走得太快,人们就无法理解。所以慢慢、逻辑性和有条理地,没有其他办法。

在任何技术领域,我们不断在旧的基础上构建东西,学习如何更好地做事情,创造越来越高的抽象层次,这在某种程度上是不可避免的。因此,尽管我们作为开发者更强大,亲爱的上帝,我无法回到异步或预异步的回调地狱。我们也有更多的东西需要学习,以真正理解整个堆栈。是的,绝对如此。我还编写过汇编语言,所以我甚至更深入。

知道所有位和字节的去向确实有一种满足感。是的,很多。那么,让我们谈谈堆栈,因为在你的职业生涯中,你在构建系统、Web 工具和 Node.js 上做了大量工作。你与 Node 项目的构建系统或 CICD 的关系是什么?

它结合了我最喜欢的两个东西,即构建系统和 Node.js。即使在 2000 年,当源代码控制只是一个想法,只有 20% 的开发者使用时,构建系统几乎不存在,只有 make,差不多就是这样。我创建的公司决定构建一个构建系统。那是大量的 Perl 代码,它处理 XML 文件,或者,或者,

我不认为那是 XML。那是闪电。我们需要解析所有内容并构建一个构建。那是极其美丽的。显然,

过了一段时间就变得难以管理,但我们真的很努力,因为这对我来说非常重要。构建系统并不是重要的,重要的是模块化的理念,不要将所有代码视为一个巨大的整体,而是将其视为一组模块或包,正如我们今天在 JavaScript 中所称的那样。这是我喜欢的一个方面。另一个爱好是 Node.js。

我做过很多事情,比如汇编、Pascal、基础语言、C、C++、Java。

Python 和现在的 Node.js 和 JavaScript。我曾经最有效率的开发是 Node.js。今天我有一个开发者。我给了他一个 Node.tlv 的加票。6 月在以色列有一个会议,Node.tlv。所以我有一张票,因为我在演讲,我把我的加票给了他。他来找我说,我对 Node.tlv 感到非常兴奋。我问,为什么?他说,因为我在写 C#,我真的很想念 Node.js 的生产力和生态系统。他说得对。这是无与伦比的。我的意思是,它有它的问题,但没有人构建出任何与 NPM 相提并论的东西。这是一个惊人的成功。很多问题,但这是一个惊人的成功故事。

是的,它确实无法直接与任何其他现存系统相比,因为人们迭代、发布、相互学习的能力,以及正如我们所说的,在 NPM 中构建抽象的能力在整个人类历史上从未见过,这是一种奇怪的规模,可以应用于过去十年或二十年中出现的事物。绝对如此。我的意思是,我记得在 2000 年代初期,构建组件模型是一个圣杯。

每个人都想构建一个组件模型,因为这个想法是错误的,如果我们有很多构建良好的组件,我们可以将它们粘合在一起并构建一个应用程序。这有点天真,但...

NPM 是我们最接近这个目标的,因为我可以真正地,比如说,我今天需要一个 Emutex,一个异步 Emutex 来锁定代码的部分。然后我说,好的,NPM,搜索一下。那里有 SNCC 顾问,它告诉你这个包是否健康,基于许多不同的指标。然后,boom,我选择一个包,NPM 安装它,好的,YARN,然后使用它。没有任何问题。这在任何其他语言中都不会发生。

好吧,现代语言更好。Go Rust。在某些方面,Python 也是如此,但那里仍然是个恐怖的场面。但是 NPM 超越了所有。我期待人们愤怒地写信来讨论你使用“恐怖场面”这个词的描述。这不是我的问题。一旦你在 Python 中安装某种包管理器,嗯,也许它有效。但你知道,有这么多,它们是如此复杂。没错。

我不仅感到一种智力上的需求,而且出于幽默的缘故,我需要开始和你抱怨 Node 和 NPM 生态系统。因为正如你提到的,有一些缺陷,一些缺点,这当然是自然的,当你在快速发展时。你曾经说过一句很好的话,我想在此基础上进行扩展。你之前说过,模块化是软件开发中的头号问题。你认为在编写 Node.js 时,个人感受到的最大问题或困难是什么?

编写 Node.js?好吧,已经有一段时间了。我现在是前端开发者。我已经习惯了 TypeScript。

而且我不想进行转译。所以我不得不使用这些花招,比如 JS doc 类型来使其工作。它大约有 95% 的时间有效,但这确实是一个困难。配置。就像它不是开箱即用的。如果你使用 Rust 或 Go,一切都是开箱即用的。你会得到你的 linter,你会得到你的格式化工具,等等。如果我有一个新的 Node.js 或 React 或前端项目,

配置它,你知道,你需要一个向导来完成这件事。现在我已经做了这么多次,我可以闭着眼睛做到,但每次我这样做时,ESLint 现在有扁平配置。所以,好的,让我们试试这个。这与那个不兼容,等等。将所有这些组合成一个东西是困难的,你需要理解每一个工具。

一旦你开始组合它们,例如,正如你所知道的,TypeScript 和 ESLint 或 TypeScript 和 React 以及打包和 TypeScript 和打包等等,那就真的变得非常非常困难。今天你要么接受默认设置,要么成为配置的向导。这是今天的两个选择。

我们实际上刚刚发布了 TypeScript ESLint 的 V8 测试版,改变了你配置类型 linting 的方式。所以我期待你和其他人一起在其中施展魔法。我们会关注它。哦,这非常令人兴奋。但是,是的,假设你在领导一个团队,你需要帮助你的开发者设置所有这些工具,无论你是从基础开始还是成为向导或杰出人物。你会如何帮助人们理解在这个领域该做什么?

好吧,有理解该做什么和帮助他们设置的区别。所以我在 Round Forest 做的事情是,我是向导。我构建了一个配置,对于每个包,我构建了这个工具,你可以从一个包中获取,然后它是一个模板,你只需从中创建另一个包。这非常有效。这是一件事。第二件事是尽可能少做。我

我的意思是,配置越多,工具越多,你就越糟糕,因为你需要配置并使它们相互结合。所以我真的很努力减少配置和工具。因此,是的,ESLint、Praline 在后端,TypeScript 用于类型检查,JSDoc 类型。它不是用于转译,因为我讨厌转译。

而且我不喜欢转译的原因并不是我讨厌它,我在前端使用它,但我更喜欢不进行转译的原因是因为这使一切变得更加复杂。它变成了一个管道。从我的角度来看,少即是多。这是一件事。还有一件事,我不这样做,这与每个人的做法相悖,如果我有一个单一代码库,并且有很多包,每个单一代码库都有很多包,

每个人都试图为所有包创建一个配置。

这并不容易。不仅如此,一旦你有了这个,改变这个配置就变得像有一个伟大的向导理解一切,他们是唯一可以改变它的人,因为有这么多包需要适应,而且有这么多配置在里面,没人能理解任何东西。还有其他方法吗?这可以争论,但我在 Roundforce 这样工作,我真的很喜欢。每个包都有自己的配置。没有共享。我是说,你可以共享,但不要。

这带来了两件事。第一,你不怕改变配置,因为只有那个包受到影响。

第二,这是一个小包。配置非常简单,非常容易理解,非常容易更改,非常容易推理。这对我来说是最重要的。这就是为什么模块化是最重要的事情。一旦你有很多包,它们彼此独立,而不是像现代单一代码库那样,一旦它们彼此独立,它们就容易推理,容易理解,容易单独构建,等等。最重要的是,正如我所说,它们易于理解。

我们有一个大约 400 到 500 个包的单一代码库,它工作得非常顺利,因为一切都是独立的。不过,每个包都有自己的文件的一个缺点,你点头微笑着。一个缺点是,如果你需要更新某些东西,那么你必须在那 400 或 500 个包中更新它。那你在这种情况下怎么办?

有两个答案。一个是我不更新。为什么我需要在所有 400 或 500 个包中更新所有内容?这是一个。另一个是,如果我确实需要,例如,我想为包添加一个规则,我会这样说。每当开发者进入一个包进行修改时,首先更新所有依赖项。这非常重要。其次,他们运行一个脚本,知道如何更新那些包,就像配置的代码修改。

这个代码修改知道如何检查是否可以更改配置,等等。它 100% 有效吗?不,它 95% 有效。我没有像那些说共享配置的人那样,认为他们是错的。他们是对的。但是,

但我也对。问题是平衡。什么是更好的正确,或者什么是更糟的错误?从我的观点来看,我更愿意承担有时更新配置的负担,因为这只是偶尔发生的事情。如果你有 400、500 个包,你根本无法进行共享配置。这根本不可能。好的。所以我更愿意承担偶尔在更新配置时遇到问题的负担,

而不是共享配置的负担,因为你真的无法更改任何东西,因为你无法在 400 个包上真正使其工作。而且对我来说,一个包必须是独立的。它必须独立构建、独立测试,等等。否则,它只是在另一种形式下的单体。

所以是的,非共享配置确实存在问题。我理解这一点。我在 Round Forest 每天都感受到这一点,但我仍然认为这比共享配置的单一代码库要好。那么,对于你来说,拥有一个单一代码库与一堆独立的单个代码库相比,有什么优势?只有一个,非常简单的优势。我可以在所有包中搜索。

这对我来说就是全部。顺便说一下,在 Round Forest,我们没有一个项目。它不是一个项目单一代码库。它是一个公司单一代码库,就像在谷歌一样。所以我们有很多项目驻留在其中,每个项目使用单一代码库中的不同包。我们处理它的方式是我们有 Visual Studio Code 工作区,每个工作区只有属于该项目的包。

所以当我在 VS Code 中搜索时,它只搜索属于该项目的包。然后我可以做任何事情,搜索、引用等等,搜索引用等等。它就是这样工作。所以这就是主要的事情。管理 400 个 Git 仓库基本上是不可能的。管理一个单一代码库,你可以搜索所有内容,对我来说,这就是那种单一代码库的唯一优势。

因为我更喜欢不使用共享配置的单体。那你如何处理构建系统的问题呢,你有 500 个不同的包,你不想在一次更改中重建其他 499 个?

现在人们会非常生气。好的。我们在本地构建。好的。我知道。我去,哦,不,但你不知道计算机上有什么。这在我那个时代是正确的,就像在 2000 年代初期。你永远不知道计算机上有什么。但今天,看看,这是 JavaScript 生态系统。它是 NPM。所有内容都在 package JSON 中本地化。你所需要的只是正确版本的 JavaScript。如果你有一个 NVMRC,它会自动处理这一切。那么...

为什么不在本地构建?我们在谈论包。我不构建所有内容。所以当我在一个包上工作时,我正在工作,构建它并运行测试,最多需要一分钟,也许两分钟。

所以,为什么要推送到 CI,进行整个过程,花费大约 10 分钟,因为有很多开销,然后你得到的反馈是,哦,有失败。所以,我们只是本地构建。我们有一个脚本来做到这一点。这个脚本基本上执行了 NPM install、NPM build、NPM test 和 NPM publish 来发布包。现在人们说,好吧,如果我想同时在两个包上工作呢?好吧,不。

这是个问题。需要同时在两个包上工作并不是一件好事。这是个问题,因为...

因为现在,一旦你同时在两个包上工作,它们在思维、测试方面开始相互依赖。你并不是在测试两个包。你只是在测试一个包,因为你无论如何都是一起构建它们。所以你突然有数百个包没有任何测试,你开始真的非常害怕更改它们,因为没有测试,它们可能会影响所有其他数百个包。所以你开始感到害怕,因为这些包并不独立。你基本上又回到了一个单体代码库。

我们在 Round Forest 和之前的 Applitools 拥有的代码库,每个包都是完全独立的。真的,真的。所以它有自己的测试,你可以只构建那个包并发布它。因此,你必须逐个包地工作。很多人说,哦,这真的是一个糟糕的开发体验。我说,是的,不完美,但它创造了真正独立的包。

<context>Node.js 和 JavaScript 生态系统与 Gil Tayar

<raw_text>0 他们有自己的测试,这些测试非常可靠,因为它们是独立构建和独立测试的。所以这很奇怪。我正在剥夺一些自由,那就是你按包构建包,好吗?但我在心理上获得的是一个惊人的生态系统,你可以拥有 400、500 个包,它们都是独立的,但现在没有问题。如果你想想,NPM 生态系统正是如此运作的。

每个包都是相互独立的。所以我构建的就像是公司内部的一个迷你 NPM。现在人们说,不,这不行。这不行。是的,我知道。但去 Roundforce 工作,看看它是如何运作的。我喜欢它。这是从包或单一代码库层面的唯我论。绝对如此。我真的很惊讶它运作得如此好。

本集软件工程日报由 Jellyfish 提供赞助,Jellyfish 是一个软件工程智能平台,采用专利分配模型,拥有行业最大的客户数据集。你知道,过去一年左右最大的变化之一是采用了像 GitHub Copilot 这样的生成 AI 编码工具。

工程领导者正在试图弄清楚他们的团队是否真的在使用它,他们使用的频率,以及它对业务的影响。他们是否交付更多?是否有更多的路线图工作在进行?你怎么知道,除了轶事和调查?这就是为什么 Jellyfish 在 2024 年与 GitHub 合作推出了 Co-Pilot Dashboard。

自那时起,他们分析了来自 200 多家公司超过 4200 名开发者的数据。想知道一个有趣的发现吗?开发者使用 Copilot 的交付量增加了 20.9%。Jellyfish 的团队有很多更多的见解,你可以开始查看自己团队的数据,以便更好地规划和管理。今天就访问 jellyfish.co/copilot 了解更多信息。

我不会认为它会有效,除非有人在播客上和我谈论在公司工作并使其有效的经验。我也是。我仍然觉得,哦,我的天,这真是个好主意。我觉得这只是零碎的东西。突然间它就发生了。然后突然间,哦,这不是,这不是发生的。这只是发生。这是一种方法论。所以...

它有效。让我试着找个漏洞。你提到你最近在前端工作。假设你正在开发一个设计系统,然后被十几个不同变体的前端应用程序使用,你想做一个 API 更改,同时更改一些颜色。所以你需要测试 TypeScript 类型兼容性、逻辑兼容性、可访问性,以防颜色更改搞砸了。如果你有一个包,然后被其他包使用,你会怎么做?

你不想做的是,如果你有一个设计系统,检查所有其他 10 个应用程序,因为你根本无法做到这一点。根本不可能。所以你以向后兼容的方式构建它。如果它不是向后兼容的,你就进行重大版本更新。一旦你更改了它,应用程序就会使用该包。假设他们在那发现了一个错误。好吧。你引入了一个错误,因为我们是人,我们是不同的。

所以这个包选择不升级到设计系统的新版本,停留在那里,去那个包,修复错误,显然添加一个测试,发布,然后他们就可以使用。我知道这是一种迂回的方式,但看看,一个项目专用的代码库,在那里你共享配置,每个人都在所有包上工作,这适用于 40、50 个包。

一旦你达到 400、500 的规模,你就无法构建所有东西。你根本无法更改所有内容。如果你有一个包,你不能说,好吧,让我们在所有其他包中测试它。它有效,因为人们有一个项目单体或一个有很多包的应用程序,像 40、50 个包。就这样。但这不是规模。

这就是一个带文件夹的单体,我们称之为包,因为我们很酷。一个带文件夹的单体。是的,这很有趣。如果我没记错的话,这就是微软设计系统的工作方式。当我在办公室团队时,每次我们想要一个新版本的,比如 Fluent,我们都必须拉取并检查,好的,这是否有效?我们会有自己的拉取请求,带有我们自己的验证,检查这个新系统是否有效?

没错。你有测试。所以我不在乎。在 Round Forest,我们自动拉取。当你开始在某个包中工作时,我们只需升级所有依赖项,有时是小版本,有时是大版本,然后运行测试。99% 的时间,测试通过,boom,我们很好。1%,好吧,我们在这里修复一点,那里修复一点,或者我们说,不,不,这个包我不想升级,这没问题。

在我们转到 Node.js 的话题之前,你是否还有其他关于单一代码库和包管理的热门观点或强烈意见想要提出?是的,我想我已经说得够多了。好吧。你已经为自己在这一场谈话中承受了足够的内部网络仇恨,接下来我们来谈谈 Node.js。你是如何首次参与这个可爱的项目的?我在 Wix 工作时...

有一个项目需要转译。Babel 不存在,所以我使用了 Google 转译器。我不知道,我只知道前端 JavaScript。我明白我需要一些后端的东西来转译用户的代码。记住,Wix 是一个可以让你构建自己网站的地方。我们的项目不仅是构建你自己的网站,还允许人们在他们的网站上进行编码,像后端和前端等等。所以我们知道我们必须转译,我们必须做很多构建的东西等等。显然的选择是 Node,当时是 0.12、14、11、10 版本。我不记得确切的版本。我花了两天时间写了一个 POC。

那是回调的时代。没有看到 Promise。我当时想,哦,我的天,这太棒了。我想要更多。显然我们继续使用 Node.js,它仍然是我的宠物。我喜欢使用它,我喜欢这个生态系统,我也喜欢这个项目本身。这是我的初恋。我开始谈论 Node.js 的一些事情,其中一件事是 ESM,因为 ESM

模块化和包以及模块系统显然是非常一致的。我对 ESM 和 Node.js 如何与 ESM 一起工作产生了浓厚的兴趣,那时 Node.js 并不支持 ESM。它只使用 CommonJS。

我在日本做了一次演讲,Myles Borens 在场,我们在出租车和各处开始交谈。他建议我成为 Node.js ESM 工作组的贡献者和观察者。这是我参与 Node.js 的开始。主要是 ESM,主要作为观察者,主要作为关于 ESM 或 Node 中当前内容的演讲者。我不是那么多的贡献者。

并不是所有的听众可能对 ESM 有深刻的热爱和理解。什么是 ESM,它与 CommonJS 或 Node 在 ESM 之前所做的有什么不同?没错。所以我们在谈论后端,Node.js,而不是前端。好的,所以让我们谈谈 Node.js。Node.js 在没有 JavaScript 模块系统的时代。根本没有。import 和 export 只是 Dave Herman 和其他人的脑海中的想法。

所以他们发明了一个。它叫做 CommonJS。它不是事件。他们如果我没记错的话,借用了别人的模块系统,并使用了它。它是 require 的东西,你知道的,const x,x,load dash 等于 require load dash。这就是我们成功使用的东西。NPM 是基于 CommonJS 构建的。

这很好。但随后 ES6 出现了,ES6 说,“好吧,但我们需要一个通用的模块系统。”他们发明或标准化了模块系统,称为 ESM,ECMAScript 模块。

使用我们所知道的语法,从和导出无论是什么,导出默认和导出这个和导出那个。所以现在我们有两个模块系统,CommonJS 和 ESM。ESM,进入 Node.js 和浏览器花了很长时间。2015 年是标准化的,但我认为浏览器直到 2018 年才获得它,大概是这样。

而 Node.js 真的在 2020 年才获得它,我想。我不记得确切的日期,但大约在那个时候。所以花了很长时间才使其标准化。今天我们有两种竞争的方式,Node。Node 可以与两种系统一起工作,CommonJS 和 ESM。这就是 ESM。

我可以看到你在 20 分钟前的采访中所说的关于你如何分层描述的内容,当你真正说出 ESM 的技术细节时,它在我们从原始的四个节点系统构建到语言规范的背景下完全有意义。因为很多人忽视的是,A,正如你所说,它很旧。它几乎是十年前标准化的。B,它是对语言的补充。它并不像 CommonJS,那只是一些标识符和函数调用,恰好是魔法。

没错。这就是为什么我是 ESM 的大力支持者。今天,NPM 是 ESM 和 CommonJS 的混合,今天的前端开发者只需要导入 CommonJS 的包。这是一团糟。正如我们所知,打包工具试图将这种混乱隐藏起来,并且 99% 的时间都成功了。那 1% 就像是让人抓狂。

所以这就是为什么我希望 ESM 和 Node.js 成功。我认为这对生态系统至关重要。- 你认为现在正在采取的步骤是什么,特别是对我们这一生中实现成功产生积极影响的?- 我想感谢 Cindele Soros

Sindler Soorhus,我希望我说的名字正确。抱歉,如果不是。他是这个巫师,拥有大约一千个包。很棒的包,只需使用 mutex。谢谢你,Sindler。他们决定将所有包迁移到仅 ESM,所有下一个版本的包将仅为 ESM,而不是 CommonJS。这很困难。为什么呢?因为在 Node.js 中,

ESM 可以导入 CommonJS,这没问题,但 CommonJS 不能导入 ESM。所以如果你想使用 Cinderella 的包的最新版本,你需要将你的应用程序转换为 ESM。所以这是一种泥淖中的旗帜,或者你怎么说?当然。无论如何。

越来越多的包开始这样做,要么仅为 ESM,要么为双模式。我认为今天大约 15% 的顶级包是 ESM,要么仅为 ESM,要么为双模式。所以这很好。希望它会继续。我非常矛盾,但 Node 刚刚引入了要求 ESM 的能力。仍然是实验性的。我想,不,请不要这样做。不要,不要。因为我一直在宣传不要这样做。

很多人试图说服我这是件好事,但它有其问题。最终,代码获胜,PR 获胜。所以它将会在生态系统中,也许它会帮助,也许不会。但我认为最终我们会完全 ESM。

就像在几年内。所以这就是目标。所以我认为这两件事。包的迁移和可能要求 ESM。从一个天真的或初学者的角度来看,能够从 ESM 导入 CommonJS 包和从 CommonJS 导入 ESM 包似乎能够双向进行会非常美好。为什么你认为其中一种方式对生态系统来说并不理想?没错。有两个原因。一个是 ESM 本质上是异步的。

我怎么知道的?有一个叫做顶级 await 的特性,其中一个包在顶级代码中可以直接 await 某个东西。这意味着导入它是一个异步过程。这就是为什么你必须 await 动态导入某个东西,而你不能直接导入它。所以它是一个异步模块系统。

而要求一个 ESM 模块是奇怪的。如果那里有顶级 await 呢?今天的答案是,如果你要求一个具有顶级 await 的包或模块,require 会失败。就是这么简单。这会产生一个分裂。我不能使用顶级 await,因为也许有人会要求我。这是一个原因,不是一个大原因。另一个原因是

随着越来越多的包迁移到 ESM,这将迫使其他包和其他包等迁移到 ESM。一旦你有能力要求 ESM,人们就可以永远停留在 CommonJS。正如我们所知,我想要组织。所以这就是我认为它有问题的两个原因。我不确定我是否正确。绝对不确定。

当然。有痛苦。我知道 Sindra 因为迁移到仅 ESM 而在网上受到很多抨击。但正如你刚才所说,确实有好处,你知道,推动年轻的小鸟在能够飞翔时离开巢穴。顺便说一下,Sindra 的 GitHub 头像中有一只非常可爱的狗,这可能对这个事业非常有帮助。

在我们开始总结之前,关于 ESM 的另一件有趣的事情是,很多人,尤其是在前端,会从奇怪的扩展名导入东西,比如从 .png 或 .webp 导入图像,或从 .css 文件导入 CSS。作为一个在 Node 的导入和加载器领域深耕的人,你对非标准导入及其在生态系统中的地位有什么看法?我对此有很多问题。

这仅仅是前端世界。我的意思是,后端我们不这样做。最多是导入 JSON。我们在后端不这样做,因为我们没有...我认为我们不需要理由。但在前端,这非常普遍。它始于导入 CSS 和 CSS 模块,现在我们导入所有东西,SVG、PNG、无论什么。这是有问题的,因为,首先,它是非标准的。所以对这到底是什么的定义是变化的。第二...

打包工具需要处理这个问题,每个打包工具处理的方式略有不同,所以如果我尝试从 webpack 转到 parcel,再从 parcel 转到 vit,再从 vit 转到其他构建工具,

那么我总是不可避免地必须处理这些部分的问题。这非常非常难以在打包工具之间移动。因此,这就是我不喜欢它的原因。它是非标准的。它造成了很多问题。人们说,好吧,别换打包工具。但一个项目不会活两年。它活五年、七年、十年。你必须更改技术。如果你是非标准的,这会使事情变得非常困难。

但标准是由常见用法所决定的。事实上,许多人正在做像从 .css 导入样式这样的事情,这意味着这里确实有一个真实的需求。那么我们如何满足这种需求,而不进行这种奇怪、痛苦的现实世界实验?我不知道。说真的。我刚刚开始前端。我知道前端,但我刚刚开始真正开发前端。我的项目中,我们正在导入 CSS 和 PNG、SVG 等等。

所以我还没有真正想清楚,好的,如果我不这样做,会有什么问题?我该如何解决?我的猜测是,如果人们说,好吧,我不想通过导入来做,但通过其他机制,他们会想出办法。我认为这并不难。但我真的不知道。也许开发者体验是如此之好,以至于这是唯一的出路。我不这样认为,但我不知道。我真的,我真的不知道。

这是个难题。但 Node 现在也有内置支持,对吗?拦截是正确的词吗?说当你导入特定文件时,有某种导入器或加载器能够介入?在 Node.js 中,是的。有加载器。我有一个关于加载器的演讲。你可以在 YouTube 上搜索它们。是的,你可以像我们在 CommonJS 中那样拦截。

但在 CommonJS 中,我们也拦截了。这就是为什么你可以使用 TS node 并运行 TypeScript。因为 TS node 所做的就是拦截所有的 require 并实时转译它。

但在 CommonJS 中,这很有问题,因为拦截就像我们只是逆向工程了一切。但现在 Node.js 与每个人的做法紧密相连,所以他们无法改变任何东西。在 ESM 中,他们决定以正确的方式进行,并描述了一种实现加载器的标准方式。这真的很好。花了一段时间,但现在它来了,真的很好。

除了 CSS,加载器或导入的示例用例是什么?LTS node。这就是他们使用加载器的地方。Babel 转译,或任何形式的转译,基本上。第二个是 Marquee Modules。

你知道,你想说如果有人导入这个模块而不是那个,他们就会得到那个。我们在测试中经常使用它。所以加载器是实现这一点的唯一方法。这很棒。我实际上为 mocking 模块编写了第一个加载器。我测试了 double。这是一次非常非常好的体验。还有什么?哦,HTTP 加载或从 zip 加载。如果你想的话,你也可以这样做,而不仅仅是像 Node.js 那样从文件系统加载。

所以转译、模拟和其他形式的协议。这是我能想到的三个。哦,APM,应用程序监控。所以测试性能等等。是的,这是第四个。我之前没有想到这一点,能够将应用程序监控直接放入代码中与导入调用。是的。这真的很巧妙。确实如此。

不过,测试是如此有趣,因为这对使用无论是什么 Jest、VTest 或者 Mocha 的 Node 人员来说,已经是一个痛苦的事情,他们有时会以半标准化的方式导入某些东西。如果我导入某个东西,我希望它被模拟出来。人们在正确设置时遇到如此大的困难,你知道的,just dot mock,V dot mock,等等。你认为随着 Node 加载器变得更加标准化,这会变得更友好吗?没错?

或者实际上是标准的?好问题。Jest 有自己处理模块加载的方式。他们基本上在某种程度上替代了 CommonJS。VTest 在这方面更标准,因为它实时转译代码,但它仍然使用 Node.js ESM。但我认为...

我不认为他们在使用加载器。所以他们只是转译所有内容,包括导入,以便它们以预期的方式工作。我认为他们可以使用加载器。不确定。但 VIT 本身是,哦不,VIT 本身是一个前端。VIT 只是使用 VIT。而 VIT 是一个前端打包工具。它不能使用 Node.js 加载器。所以这就不行了。

许多后端项目使用 VTest。这真的很有趣,它建立在前端的打包系统之上。我不确定为什么。这只是让它们变得更慢,并对你的代码做奇怪的事情。所以我不喜欢它。我喜欢在前端使用它。但在后端,Mocha、NodeTest 本身、Ava,所有常规的只需导入测试文件并运行它们,我认为它们是最好的。它们是最简单、最容易理解的。

我很想和你深入探讨这个问题,但我们快要没时间了。我想在最后留出时间问一个有趣的个人问题。你是一个多产的读者和书籍、电影的消费者。你最近在阅读什么?

在过去几个月里?好吧,主流,非常主流,但《三体》。我真的很喜欢。我大约在两年前读过它。然后 Netflix 系列出来了,我说,好吧,让我们再读一遍。太棒了。这很奇怪。这很不同。它确实是中国的。这与美国或英国的科幻小说不同。所以就是这样。

我正在进行我最喜欢的作家 Samuel R. Delaney 的马拉松。他是 60 年代的作家。他仍然活着,仍然多产,是一位伟大的作家,主要创作主流的非常奇怪的文学。但他太棒了。每隔 10 或 20 年,我就会说,好吧,让我们再读一遍。

这仍然很棒。哇,他真是多产。他赢得了不少奖项。哦,格兰德大师,一切。是的,他值得。他真的是最复杂和有趣的科幻作家之一。你在科幻作家或他们的书中寻找什么?

这要看心情。我是说,我喜欢太空歌剧,所以我对此没问题。但最终,我倾向于更心理化、复杂、非常有文学性的科幻小说。所以是的,再次,这要看心情。

就我个人而言,这将导致一个问题。就我个人而言,我真的喜欢那些让我质疑并提高我理解自己和周围世界能力的书籍。像阿西莫夫的《基地》系列让我真的思考,嗯,人和系统的集体行为是什么?我们在朝什么方向发展?

有没有特别的书或系列,你会推荐给人们用来更深入地思考自己和周围的世界?哦,这是个问题。好吧,Delaney,抱歉,我必须回到他身上,因为他是第一个让我接触到后现代主义或后结构主义思想的作家,在那里你理解文本和语言可以以不同的方式一起阅读。

并且可以被不同的人以不同的方式理解。文化是非常的,它不是根深蒂固的,但它会变化和发展,每个人都有自己的文化。他以如此美丽的方式表达这一点。