[HN Gopher] Speeding up the JavaScript ecosystem - Polyfills gon... ___________________________________________________________________ Speeding up the JavaScript ecosystem - Polyfills gone rogue Author : jviide Score : 106 points Date : 2023-09-21 19:31 UTC (3 hours ago) (HTM) web link (marvinh.dev) (TXT) w3m dump (marvinh.dev) | e_y_ wrote: | > Polyfills that don't polyfill | | This is what's sometimes called a "ponyfill". The idea is to | avoid messing with global scope (monkeypatching), which could be | problematic if you have multiple polyfills for the same API or | polyfills that don't perfectly match the native behavior. | | This can be a good thing in some situations, but in general it's | probably best to leave polyfill decisions to the bundler so you | can decide which browsers you want to support. Or even produce | multiple versions, a lightweight one for modern browsers and one | with tons of polyfills that gets served to ancient ones. | mhagemeister wrote: | Author here. | | Good point. Agree that the ideal scenario would be that the end | user (or the tools they use) have the final say in which | polyfills to load. It's a bit of a bummer that they are shipped | as part of npm packages without an easy way to get rid of them. | | I wonder if our industry will move to publishing the original | source files to npm in the long run. Only the last piece of the | chain, the developer using these dependencies, knows what their | target environments are. So the bundler could then downlevel or | polyfill the code for the specified targets. | no_wizard wrote: | Marvin is doing wonderful work in the JS ecosystem around | performance. It has been largely focused on tools and node, | however, he did have an interesting set of things to say about | how they optimized performance in Preact as well on his website | that was really interesting too. | | One thing I've noticed is the rampant duplication of polyfills | and babel helpers. To the point that I now have overrides setup | via pnpm and I re-write many imports of polyfills to point at my | own shims, which simply re-export existing functionality native | to the language, most of the time. | | For smaller utility packages, I often simply clone the repo and | copy things over that we need, or copy the src right out of the | node_modules folder if possible, then I strip away all the | superfluous imports (and often convert from commonjs to ESM if | needed) | | Saves so much headache, its better for users, smaller builds etc. | Dextro wrote: | That sounds awesome. I've dabled with something like that but | only for lodash (makings sure all different flavours get | aliased to a single thing) but I never went too far with all | the other stuff. | | You wouldn't happen to have an example of what you're doing | laying around would you? I'd be genuinely curious to try stuff | like that out. | mhagemeister wrote: | Author here. | | Thanks for the kind words! It's feedback like this that | encourages me to keep writing about it. | | I share your experiences regarding babel helpers and haven't | found a good solution myself. Similar to you, I often patch | unnecessary stuff out via patch-package, but that approach | doesn't scale well. | romellem wrote: | There is some interesting [drama][1] with this, since this | article noticeably doesn't mention any PRs they opened to remove | some of these older polyfills. | | The reason those PRs were never opened/merged is the maintainer | of many of those libraries [has a strong stance on "breaking" | changes][2] in software: | | > I have developed an intense avoidance for breaking changes in | all of my packages, because I don't want to inflict hundreds of | millions of dollars of person-hour cost on the entire industry | unnecessarily. | | IMO this argument avoids the _opposite_ claim, that people then | spend a ton of time (and money) trying to make old tech work with | newer tech since not everyone maintains to the same standards of | backwards compatibility. | | But regardless, no one is required to stick to a particular way | of creating open source software, so the one benefit here is that | you are free to [fork the library][3] (assuming its license | allows for that) to remove some backwards compatibility that | isn't relevant to you. | | [1]: https://twitter.com/ljharb/status/1704912065486618915 | | [2]: https://github.com/import-js/eslint-plugin- | import/pull/2447#... | | [3]: https://www.npmjs.com/package/react-outside-click-handler- | li... | potsandpans wrote: | Ljharb is harmful to the ECMAScript community | silverwind wrote: | I'm thankful for his work, but I do agree, this polyfill | madness has to stop. | gibolt wrote: | Is there not a config for minimum supported JS version? That | would appease everyone, while maintaining backwards | compatibility. | | Docs should show the recommended version (modern) and show what | options are available to go deeper. | | Obviously adding those settings for every pollyfill in non- | trivial, but burdening everyone with every pollyfill ever is | also suboptimal. If anything, this would make cleanup easier | going forward since it would all be classified | petetnt wrote: | I don't really care to comment about the practice itself, but the | "Polyfills that don't polyfill" section is missing the point: the | function is called directly instead of patching the global object | so that the global object is not polluted by an possibly non- | standard implementation. Additionally it does use | Object.defineProperty if available - furthermore it doesn't even | call itself a polyfill in the first place. If it's needed in 2023 | is a valid point however. | fiddlerwoaroof wrote: | I think it would be better to just expect the standardized | functions to be present and then document that the project | needs them (e.g. via peer dependencies), allowing users to | install them themselves as needed. | shpx wrote: | That's a lot more work than your library just working in more | places. | fiddlerwoaroof wrote: | I don't think it's all that much more: basically every | bundler I've used uses browserslist to include polyfills | for the developer's target audience. | | But, also, I think this sort of easy path inflicts a huge | cost on the ecosystem as a whole: writing to the standards | and expecting your users to supply a compliant environment | solves a lot of N*M problems in the dev process. | ComputerGuru wrote: | I maintain a few JavaScript libraries that I manually verify | compatibility against IE6 (and have lints to catch violations). I | manually polyfill a few necessities and quality-of-life | improvements up top in the script. Out of curiosity, I removed my | polyfills and tried swc and babel both, followed by an eslint | pass, and the results were absolutely atrocious. Everything gets | polyfilled, even stuff that has been supported in every IE | version ever. The usage-based detection is completely borked, and | is completely based off string searching property/function names. | Using toString() anywhere pulls in the polyfills for Date to | string, Regex to string, and Object to string. Using regex | anywhere pulls in a bunch of regex polyfills. It was a nightmare | and the size of my library increased by orders of magnitude! | | (I tried opening an swc issue about optionally using typescript | ast info (via a plugin, not in swc core) to have more correct | usage-based polyfill detection, but that was closed as unlikely | to be acted upon.) | mhagemeister wrote: | Author here. | | That mirrors my experience too on working in various projects. | The automatic polyfilling story is such a good thing in theory, | but reality isn't as rosy and much more polyfills than | necessary are included. | ComputerGuru wrote: | Thanks for writing your article and sharing! One thing I do | on my other libraries is also to polyfill a few heavier | things asynchronously instead of making everyone pay the | price upfront, so for example I detect if a JSON polyfill is | required before asynchronously loading that polyfill. I think | the expectation for most things is that the browser will | support them by default, and it's ok if all/any polyfills are | loaded separately and asynchronously. | spankalee wrote: | Why are you supporting IE6? | ComputerGuru wrote: | I replied to a sister comment to yours but it's an old | library that started off with IE6 support maybe 10 years ago | and just kinda never lost it. | H1Supreme wrote: | > I maintain a few JavaScript libraries that I manually verify | compatibility against IE6 | | Genuinely curious why anyone would target IE6 in 2023. Is it a | personal goal to have massive coverage for your library? | ComputerGuru wrote: | It was used in the checkout flow on our website and the | marginal cost of supporting IE6 wasn't worth a lost sale. | That was many years ago, and since then I don't think we have | anyone on less than IE8 due to TLS version issues with | CloudFront, but the code already supported IE6 and there | aren't many polyfills extra compared to IE10, so :shrug: | smallnamespace wrote: | The reason libraries call polyfills directly is because it's | impolite for a library to change global scope underneath you. | | Usually it's the top-level application's author who chooses and | configures polyfills. | | Now one may reasonably ask, why doesn't the library just call | Object.defineProperties directly, and tell the user to install | the appropriate polyfill? | | I'm going to guess that a library that Just Works after an npm | install will see much better adoption than one that requires each | user to configure their babel/swc/etc. correctly, especially | since the library can be a dependency of another library. | | There's currently no standardized mechanism in the npm ecosystem | to do the equivalent of "Install this library, and also configure | your environment to pull in all required polyfills" so that the | required functionality is available in global scope. One reason | is because the transpilers that automatically polyfill into | global scope are third-party tools. | | Maybe a standard mechanism like this _should_ exist, but it doesn | 't today, hence the quite reasonable choice of library authors to | directly use polyfills because doing so: | | 1. Avoids pollute the global namespace by avoiding applying a | polyfill globally | | 2. Works as a dependency without additional configuration by the | user | | 3. Preserve backwards compatibility | | A somewhat cheap fix to at least reduce duplication of polyfills | would be for libraries that need polyfills to accept a wide | version range. That would give the package manager room to pick a | version that's compatible across call sites. | tedunangst wrote: | But why are we polyfilling a function that exists in every | version of node? When did this code _not_ just work after | installation that it required a polyfill? | [deleted] | azemetre wrote: | This series is great! | | You should seriously think about consolidating them into a book. | Something I notice other engineers struggle with is how to | properly assess performance, read heap snapshots, or even | understand how to read a flamegraph for stack tracing tools. It | would be nice to point, or buy, them a resource showing this. | | I'd definitely buy a copy. | bjnewman85 wrote: | Second that, I would definitely buy a book based on this | series. | mhagemeister wrote: | Thanks for the kind feedback! It's definitely something in | the back of my mind. I feel like I need to collect a little | more content to fill a whole book, but I'm enticed by the | thought of writing one nonetheless. | leipert wrote: | Part 3 send me down a debugging route of eslint performance | in the GitLab project and we were able to move from 25 min | of listing time in CI, down to 5. So, thanks for the | inspiration! | jluxenberg wrote: | For what it's worth; `eslint-plugin-react` has been around for a | long time and seems to support running in very old versions of | Node.JS (back to v4[1] apparently! tho I can't find anything | documenting that for sure.) | | I was surprised to learn that Object.values is only supported in | Node >v7, Object.fronEntries was added in v12, etc. So for this | project maybe the polyfills are needed. | | [1] https://github.com/jsx-eslint/eslint-plugin-react/pull/1038 | mhagemeister wrote: | Yeah, engines are a moving target. I'm all for backwards | compatibility, but I'm worried about promoting old node | versions with known unpatched security issues. Given that | eslint itself only supports node >= 12.22.0 it seems like it's | time to get rid of the polyfills. | | I wish we as in the industry would find a better solution to | adapt to this. It's a bit unfortunate that the polyfills as | part of the library code itself, which makes it difficult to | get rid of them once they're not needed anymore. | gigel82 wrote: | Genuinely curious, are there people out there using newer | versions of this package with old / unsupported versions of | Node (in production)? | silverwind wrote: | Not really. Adoption of new Node versions is quite quick, | given their short support periods. | | https://nodejs.org/metrics/summaries/version.png | cxr wrote: | You'll never get NPM apologists to acknowledge this. One of their | only skills is making non-specific appeals to the necessity of it | all (as essential infrastructure) and vague arguments that boil | down to "you need to trust the wisdom of the crowds" (and e.g. | the fact that it exists and everyone else is using it means that | anyone who disputes its value just doesn't understand it--bonus | points for them if they manage to work in a slight that's | designed to paint you, implicitly or explicitly, as a junior), | despite not being able to attest to any firsthand knowledge of | the real _why_ of anything they 're defending. | | > The new dependencies were all polyfills for JavaScript | functions that have long been supported everywhere. The | Object.defineProperties method for example was shipped as part of | the very first public Node 0.10.0 release dating back to 2013. | Heck, even Internet Explorer 9 supported that. And yet there were | numerous packages in that dependend on a polyfill for it. | CharlesW wrote: | > _You 'll never get NPM apologists to acknowledge this._ | | How should NPM prevent archaic dependencies, or the "even more | bizarre" (author's words) problem of developers calling | polyfills directly instead of the function that the polyfill | fills? | whstl wrote: | The parent is not criticising NPM the tool/registry, but | rather the ecosystem and culture. | cxr wrote: | I'm happy to criticize NPM the tool. The whole thing is | designed as a second, crummier version control system that | lives in disharmony with and on top of your base-level | version control system (so it can subvert it). It's a | terrible design. | | There's basically at most one reasonable use for npm: as a | glorified download manager, i.e. to quickly fetch a module | by name (right before you check it in to version control | with Git). This differs wildly, of course, from how it's | actually used, which is as a drug that sweeps mountains of | unaudited code under the rug so people can trick themselves | and others into thinking that none of it's really there on | the basis that it's not visible when anyone first clones | the repo. | | To answer the other commenter's question, "How should NPM | prevent archaic dependencies": it shouldn't; it's okay for | programmers to be responsible for their work. | spankalee wrote: | How is npm a version control system? | | npm is a fairly standard package manager, much like many | others, pretty good even. | | I don't know of anyone who says that package managers and | source control solve the same problems. They both happen | to use the word and concept of "version" but to mean | _different_ things. Yes, some projects vendor in their | dependencies into their source control system, but they | must either manually verify package version compatibility | or use a package manager like npm to help them do it. And | vendoring doesn 't work for actual packages published to | the package repo. If they vendored dependencies then | every dependency would be duplicated always, defeating | the very purpose of a package manager! | cogman10 wrote: | Back in 2011ish or so when npm was just getting started and | Ruby on rails was all the rage. "Do not repeat yourself" was | seen as a gold standard for programming. The end result has | been a mess, particularly for npm. There were FAR too many | articles talking about how "there's no such thing as too small | a dependency" and talks given about how much a virtue it was to | create "is-odd" or "is-even" "look you saved 3 lines of code | that you don't have to test!" | | Unfortunately, that compounded with browser of the era | (Internet explorer...) having basically 0 support for modern | javascript led to a proliferation of dependencies, polyfills, | etc that are nearly impossible to remove from the ecosystem. | | I've not seen a lot of node apologists that are fine with the | current ecoystem. The problem is righting the ship is going to | be terribly hard. Either existing frameworks/libraries need to | go through the effort of saying "Ok, do I really need is-even, | let's remove it" or we need new frameworks/libraries to abandon | tools and the ecosystem in favor of fatter and fewer | dependencies. | | I think the issue all stems from the fact that before 2010ish, | there was one library and one framework, jquery (Ok, there were | others... but were there really?) and that added a good 1mb to | any webpage. The notion was we do more with less if we had a | bunch of smaller deps that didn't need to be brought in. | no_wizard wrote: | I wonder if there would be any interest from folks for an | alternative npm registry that would automatically cleanup | dependency chains like this (among other things, possibly), | remove polyfills etc. | | I always thought about something like this, with on the fly | manipulation of packages via SWC would be pretty fast I think | Klonoar wrote: | jQuery is the one that's outlasted them all, but yes - there | were absolutely other frameworks in use. It also discounts | the detour into the Backbone era before Angular and React | took off. | arbol wrote: | MooTools springs to mind | whstl wrote: | Most people fine with the problems of the ecosystem have | financial incentives. | | Stating that you maintain 800 NPM libraries brings more clout | and money than maintaining a foundational one. | | Even with foundational packages things tend to go wrong. Why | add features to an existing package if I can write several | plugins? Or even worse in some cases: why use the existing | configuration file if I can instead just ask users to install | dozens of dummy packages that only exist to trigger a feature | in my Core package? | josephg wrote: | > "Do not repeat yourself" was seen as a gold standard for | programming. | | I remember this era. I've been using nodejs before npm | existed and so many silly things have happened in that time. | | I think the core problem the JS ecosystem has always had is | that most JS developers are relatively inexperienced. (JS is | very beginner friendly and this is the price we pay). I still | vividly remember being at nodecamp in 2012 or something | listening to someone tell me how great it would be if the | entire OS was written in javascript. It didn't matter how | much I poked and prodded him, he couldn't hear that it might | not be an amazing idea. I think he thought it would be easier | to reimplement an OS kernel in JS than it would be to just | learn C. And there were lots of people around with a sparkle | in their eye and those sort of wacky ideas - good or bad. It | was fun and in hindsight a very silly time. | | So yeah, of course some idiot in JS made is-even. And is-odd | (which depends on is-even). I see all of this as the | characteristic mistake of youth - that we go looking for | overly simple rules about what is good and bad (JS good! C | bad!) and then we make a mess. When we're young we lack | discernment about subtle questions. When is it better to pull | in a library vs writing it yourself inline? When is JS a good | or a bad idea? When should you add comments, or tests? And | when do you leave them out? | | Most of the best engineers I know made these sort of stupid | philosophical mistakes when they were young. I certainly did. | The JS ecosystem just suffers disproportionately from this | kind of thing because so many packages in npm are written by | relatively new developers. | | I think that's a good thing for our industry as a whole. But | I also get it when Bryan Cantrill describes JS as the failed | state of programming languages. | TeMPOraL wrote: | > _The JS ecosystem just suffers disproportionately from | this kind of thing because so many packages in npm are | written by relatively new developers._ | | I think it also suffers because it grew in the age of | Internet and Open Source, which made the problem | compounding. Programmers write a lot of stupid code when | learning, it's part of the process - but it used to be that | the stupidity was constrained to your machine and maybe a | few poor souls who ended up reading or using your code. In | JS ecosystem, all that stupidity gets published, and ends | up worming its way, through dependency chains, into | everything. | beepbooptheory wrote: | I thought is-even/is-odd were satirical? Granted, satire | echoing these same points... but, you know, just for-the- | record. | phire wrote: | It's hard to tell. | | The author of both packages has since moved the github | repos to an organisation named "i-voted-for-trump" and | labelled the packages with the "troll-bait" tag. | | But the author also claims those packages where created | when they were learning to program, and they seem to have | stuck with the idea of small libraries (just not quite | that small). The changes seem to be due to grief the | programming community has given him about those packages, | as some of the worst examples of the pattern. | | I suspect the author was being slightly satirical by | choosing to do the simplest possible NPM library. But at | the same time they probably believed it was a useful | package, allowing new programmers (like himself) to | calculate the evenness/oddness of numbers without needing | to know or google the mod-2 trick. And if you are going | to learn how to create npm packages, might as well start | small. | | If they were aiming to be slightly satirical, they were | not expecting that level of outrage. | | It's worth pointing out that the libraries do slightly | more than just mod-2. It checks the argument passed in | was actually an integer, and throws descriptive error | messages if the argument is not a number or not an | integer. | pphysch wrote: | Those may be satirical, but `leftpad` is real. | | https://qz.com/646467/how-one-programmer-broke-the- | internet-... | phire wrote: | _> I still vividly remember being at nodecamp in 2012 or | something listening to someone tell me how great it would | be if the entire OS was written in javascript._ | | That reminds me of Gary Bernhardt's "The Birth & Death of | JavaScript" talk[0], which one of the best comedy | programming talks. Despite the comedy, it's also a half- | decent idea. | | [0] https://www.destroyallsoftware.com/talks/the-birth-and- | death... ___________________________________________________________________ (page generated 2023-09-21 23:00 UTC)