Any kind of migration breaks code. Every single line of <i>practical</i> code hit by a breaking change should be considered a bug. Before the security theater takes the stage, let me clarify by saying that I don't mean we should not have breaking changes at all, but that only by explicitly treating them as serious bugs, can we even hope to set up a process to minimize them. Of course, the minimization objective will have multiple other variables -- security being one of them.<p>Therefore, when it is possible to not break <i>practical</i> code, it should not be done. This is the "linux philosophy", if you will. Among JS runtimes, Bun is a great example. In bun today, you can, in the <i>same file</i> (!!), with zero code changes, 100% transparently:<p>1. require() a module regardless of whether it is cjs or esm [1]<p>2. <i>synchronously</i> `import` a module regardless of whether it is cjs or esm.<p>3. do either of this with typescript or javascript<p>Some of these required changes in JavascriptCore, which were implemented - it's good to see a lack of cribbing about "it needs to be supported in upstream" and a focus on practical end-user usage.<p>Any runtime/kernel that doesn't put this level of effort into not breaking code is just not serious. While Node is at least moving in the right direction, supporting require(esm) with some restrictions, Deno is completely hopeless - CJS code is completely unusable on Deno. The reasoning is crazy too - how does it matter if ESM is the "standard", if millions of lines of practical, real world code is using CJS? It does not matter that popular libraries move to ESM. Even a single internal tool that's locked into CJS for some reason means that a project cannot move away from it easily. Then like someone mentioned below, many "plugin architectures" and "command architectures" are pretty much locked in to require(). The whole Deno project screams "ideology > pragmatism". Hopefully, just like they came to their senses w.r.t node compat [2], they implement CJS interop as well.<p>[1] You cannot require() an ESM module that uses top level await<p>[2] In contrast, bun's node compat story is very good, they run the Node.js test suite on every commit to Bun, and pass a huge majority of the test cases. Track the progress here: <a href="https://bun.sh/docs/runtime/nodejs-apis" rel="nofollow">https://bun.sh/docs/runtime/nodejs-apis</a><p>There is even some effort put into V8 APIs(in a JSC runtime!!). This helps with using Bun with the usual debugger in the chrome browser/VSCode, modules that use `node:v8`, etc,. Read about it here: <a href="https://bun.sh/blog/how-bun-supports-v8-apis-without-using-v8-part-1" rel="nofollow">https://bun.sh/blog/how-bun-supports-v8-apis-without-using-v...</a><p>There is also compat the <i>other way</i> - there are the beginnings of node/browser polyfills for Bun-only APIs: <a href="https://github.com/oven-sh/bun/tree/main/packages/bun-polyfills" rel="nofollow">https://github.com/oven-sh/bun/tree/main/packages/bun-polyfi...</a>