Editor’s note: This blog post was written by Daniel Ehrenberg, a software engineer on Bloomberg’s JavaScript Infrastructure and Tooling engineering team.

Bloomberg and Node.js

At Bloomberg, we depend on Node.js as one of the key technologies that power our main product, the Bloomberg Terminal, a Chromium-based desktop application for finance professionals. Using Chromium and Node.js to render the Terminal UI gives us a lot of functionality out of the box which would’ve been much more expensive to build and maintain all by ourselves. Therefore, we believe the responsible thing is to invest in the maintenance of this powerful piece of open source software. This has been our strategy with Node.js, not only in sponsoring a full-time Technical Steering Committee (TSC) member to develop and maintain Node.js Core, but also in the development of new standards to reinforce Node’s functionality.

Sponsoring Node.js Core work

Since 2018, Bloomberg has been sponsoring Joyee Cheung’s work on Node.js Core and its TSC, via Igalia, which previously sponsored her work. Before that, Joyee had already contributed significantly to Node, through a mix of her free time and a smaller fraction of her working hours. The increase in Joyee’s hours dedicated to Node.js, enabled by Bloomberg’s sponsorship, has allowed her to take on bigger and more ambitious projects.

One of these recent projects was to improve startup time using code caching and snapshotting. Code caching refers to pre-calculating the V8 bytecode for a JavaScript source file, so that it can be loaded faster later. Snapshotting goes further: a snapshot that represents the JavaScript memory heap can be loaded into memory efficiently, without executing any JavaScript at all.

To enable snapshots for Node.js Core, Joyee refactored many systems within Node.js to separate out the part which is just about loading its code and never changes (which can then be incorporated into the snapshot) from the part that actually needs to run when Node starts. To top things off, she exposed this capability to application code: Node.js applications can partially run themselves to generate a snapshot. Joyee integrated application-specific snapshots into Node’s single executable application support, and snapshots are also accessible via build flags and command-line flags.

Snapshots ensure that Node.js’ startup remains performant as it continues to implement more built-ins and globals, and also enable user applications to have a performant startup. As the following chart shows, the TypeScript compiler takes less time to start up due to Node.js Core’s built-in snapshot, and its startup can be even faster if a custom snapshot is integrated (from Node.js’ test suite). Your results may vary, but the TypeScript team is already experimenting with using snapshots.

Here are some of Joyee’s additional accomplishments through work sponsored by Bloomberg over the past few years:

  • Integrated a subset of Web Platform Test into the Node.js core test suites with a custom test runner and automation to update the tests
  • Implemented various utilities for profiling and diagnostics, including --cpu-prof, --heapsnapshot-near-heap-limit, --heap-prof, etc., as well as better book-keeping of embedder objects in heap snapshots
  • Improved memory management in Node.js, initiated support for Oilpan, and addressed known memory issues that impact the Node.js ecosystem, e.g., Jest

WinterCG and AsyncContext

Historically, Node.js collaborators had trouble making themselves heard in standards bodies like TC39 and WHATWG. The Node.js community is now leading the way in standards, with collaboration among server runtime environments in WinterCG.

WinterCG aims to form a common API for JavaScript runtimes for server environments. Bloomberg is a founding member of WinterCG, a step driven by our motivation to reinforce Node.js’s web API support, as well as to create a standards-driven common target that we can apply to our own JavaScript runtime.

Around the same time as WinterCG was founded, many server runtimes faced demand from customers to support AsyncLocalStorage to support improved diagnostics. In fact, for Bloomberg’s JS runtime, we had already built our own analog. This common need led a few WinterCG members from Vercel, Igalia, Bloomberg, and Alibaba to work together to develop a TC39 proposal — AsyncContext — to solve this problem.

Bloomberg has been supporting AsyncContext through both direct involvement in TC39, as well as by sponsoring Andreu Botella’s work through Igalia, including detailed design, implementation, testing and HTML integration work for AsyncContext. You’ll be able to learn more about this during my NodeConf EU talk: “AsyncContext: Observability in JavaScript through TC39 and WinterCG.”

Faster startup with deferred module evaluation

One challenge when adopting native ESM modules in Node.js is ensuring fast startup performance. We’re working on upstreaming and standardizing an essential technique that we’ve been using at Bloomberg: avoiding executing modules until they are used. This is different from build-time tree-shaking, because a module might only be used just down one conditionally-executed runtime path.

Within the Bloomberg Terminal, we use an internal version of deferred module evaluation to significantly improve app startup time. And we’re not alone: Firefox DevTools and Babel use a similar technique. In all cases, the lack of this optimization prevents us from adopting native ESM. To solve this shared problem, all three organizations (Bloomberg, Babel via Igalia, and Mozilla) are working together on designing, implementing, and standardizing a solution.

In the context of CommonJS (CJS), loading can be delayed by putting a `require()` call inside of the function where the result would be used, rather than at the top level of the module. To make this work in ESM, we’re proposing the introduction of an `import defer` syntax in the JavaScript language to defer executing a module until one of the properties of the namespace object is accessed.

Bloomberg has been sponsoring the work of Igalia’s Nicolò Ribaudo to drive specification work, as well as implementations in tooling. Simultaneously, our engineers have been aligning our internal implementation with the draft specification and providing input to the standards process based on this experience.

It takes a village… to maintain infrastructure

Since the days of the io.js fork, Node.js has harnessed the collective power of the community to build modern APIs and integrate with modern versions of dependencies like V8. In the years since the creation of the Node.js Foundation (now part of the OpenJS Foundation, of which Bloomberg is a member), this community has included more than volunteers. Many tech companies of all sizes have also made strong contributions to Node.js Core, though some major companies have subsequently decreased their engagement.

It’s essential that people keep contributing to Node.js over time — for everything from adding HTTP/3 support to emergency releases of all LTS versions when an OpenSSL vulnerability is discovered. Fortunately, the Node.js community does have contributors doing these things. Sustained investment is needed in order to maintain infrastructure like this.

Surprisingly, it can be easier to fund brand new projects, or your own company’s value-add on top, rather than the shared base that we all depend on. But it really should be the opposite: investments in open source infrastructure are even more cost-effective because of the force multiplier that results from everyone else’s contribution to the cause. The savings are especially great when you consider the alternative: Node.js becoming outdated, forcing you and your organization to migrate your applications to an alternative platform.

If your company depends on Node.js, please consider contributing to Node upstream. One way to do this is by allocating certain hours of employees’ work time to Node.js upstream contributions — this is probably the most common strategy used today. Bloomberg’s strategy of partnering with a consultancy (Igalia in our case) is another option. Another possibility to explore is building a funding pool to share the expenses required to employ more developers to work on Node.js.

Who’s interested?

Share Me

Related Reading


Don’t miss a beat

Get all the latest NearForm news, from technology to design. Sign up for our newsletter.

Follow us for more information on this and other topics.