Re: [w3ctag/design-reviews] Design questions around pay-for-what-you-use (#421)

Let me post for the TAG's consideration my stance on built-in modules, adapted the private reflector thread above:

I believe that built-in modules are harmful and should not be pursued. This realization came after two years of leading a team within Chrome that explored using them for the web, so it is hard-fought. What follows is my personal opinion; as I am no longer working in this area, the Chrome team's position may be different. (But to the extent I can influence it in this direction, I will.)

One of the first things you notice about built-in modules is that they are confusing. JavaScript developers often believe that by virtue of new APIs being in a module, they are part of a new "standard library" effort, somehow distinct from all of the previous standard library work that landed as built-in globals. It's never clear exactly what they believe to be different; in particular, few seem to realize that the difference is only in the exposure technology and syntax. We won't move any faster in building the standard library if we switch from putting it in globals to putting it in modules. We won't stop needing specifications, and consensus, and tests. The only difference is that new classes designed after some point in time will end up accessed one way, and classes designed before another.

However, built-in modules become technically problematic when you realize that we have to re-solve all of the things we've already figured out with built-in globals. This includes things such as: loading things needed for application startup synchronously; how to polyfill; how to virtualize; how to name things; and how to integrate with the rest of the ecosystem (e.g. a new temporal module integrating with the Intl global). My team pursued answers to these questions for a long time. But eventually we realized that we were piling complexity on top of complexity, only to re-achieve the same capabilities we already get with built-in globals. When viewed from that angle, we decided to drop all that complexity (e.g. [removing support for built-in module fallback from import maps](https://github.com/WICG/import-maps/pull/176)).

Built-in modules are also dangerous in that they are being used as an opportunity to relitigate decisions that support the foundation of the web today. These include attempts to move away from the shared cooperative namespace of the global object; to introduce a new governance structure for names; to cripple the flexibility and polyfillability of the platform via freezing or cons-ing up new objects; or the introduction of developer-facing versioning to a versionless language and standard library. Although I too was excited about the opportunity to break slightly with the past and [refine API definitions](https://github.com/heycam/webidl/issues/755), what it took some time to realize is that others were seeing this as an opportunity to make much more drastic and harmful changes.

With all of these drawbacks, built-in modules would need a lot of compelling advantages to still be worthwhile. But what our team has found is that those advantages are chimeral. The problem of a "polluted global namespace" is largely a non-problem; the only global introduced by specifications that has had conflict with developer code is globalThis. A second global namespace of module specifiers, while theoretically cleaner in that it doesn't collide with global variable declarations, in practice will have the same potential collisions---unless you also take some of the more dangerous suggestions alluded to above and cripple polyfillability. Shorter post-import names is another surface advantage, but this can be accomplished just as easily with namespace objects. My team explored writing built-in modules in JavaScript instead of C++, but we could have (and Chrome [has](https://bit.ly/v8-extras) in the past) written built-in globals this way; modules do not enable anything new here.

The only potentially-compelling advantage of built-in modules is that, because importing is necessarily async, they might be able to be lazy-loaded, or "pay for what you use". But this, too, is not very accurate. When I mentioned this idea to Eric Rescola (Firefox CTO) when my team was first getting started, he pointed out that lazy-loading is also possible for globals, just by hiding the APIs behind promises (cf. specs like [navigator.getBattery()](https://w3c.github.io/battery/#the-navigator-interface)). And when we [brought up this idea on whatwg/html](https://github.com/whatwg/html/issues/4697), Ryosuke Niwa from Apple's WebKit team [pointed out](https://github.com/whatwg/html/issues/4697#issuecomment-506644355) that modern OS kernels are smart enough that it won't spend memory on unused APIs anyway, and in fact dynamically loading built-in modules could create drawbacks due to each realm bringing in different modules, causing at least WebKit/JSC to end up with more "dirty memory".

Overall, the benefits of built-in modules are slight or nonexistent, and their drawbacks are substantial. As such, I urge both TC39 and the wider web standards community to stand strong against the idea of bifurcating the web standard library into globals-before-2020, modules-after-2020, and instead stick with globals. If there is a desire to change aspects of how the standard library is specified, e.g. hiding more APIs behind promises, using namespace more extensively, or collaborating between TC39 and the Web IDL editors to align on conventions like enumerability, those can all be pursued while continuing to use globals. Built-in modules do not bring enough to the table to outweigh their dangers, and the module system should remain the purview of user-space libraries or specific hosts like Node.js.

I hope the TAG finds this perspective valuable.

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/w3ctag/design-reviews/issues/421#issuecomment-561705979

Received on Wednesday, 4 December 2019 15:51:50 UTC