On Wednesday, November 18th, TJ VanToll, and I interviewed Julian Gonggrijp, a scientific programmer for the Digital Humanities Lab at Utrecht University. He recently spent quite a bit of time reworking the structure of the popular Underscore.js library. On the show, we discussed what it was like to analyze and undertake the modularization work. We also discussed bundlers, code comments, why Underscore.js is still quite relevant, and the pros/cons of nested ternary operator clauses. You can find the broadcast recording on the React Wednesday YouTube Playlist.
The Underscore.js Library
The Underscore.js library has traditionally been considered the missing “Standard Library” for JavaScript. In Underscore.js, you’ll find the functional utilities and syntactic sugar you need to build JavaScript applications efficiently. To quantify the popularity and scale of the library, according to npm-stat, Underscore.js was downloaded 28-35 million times per month in 2020. This download quantity is roughly similar to the monthly downloads of React itself on npm.
Why Modularize It?
The dominant architectural preferences for JavaScript applications have changed substantially since the initial release of Underscore.js in 2009. Rather than include a handful of libraries and decorate pages with dynamic functionality, today’s JavaScript Developer includes many MB of JavaScript to support Single Page Application architecture. To provide the best user experience, developers must ensure the application payload size is as trimmed as possible. One mechanism for doing so is to avoid shipping code the user won’t need. The original author designed Underscore.js as a single JavaScript file, and easy to include into the application structure. At the same time, the monolithic architecture complicates stripping out unneeded code. To meet the modern needs of JavaScript developers, Underscore.js needed a re-architecture to enable bundling and code splitting.
Why Doesn’t Tree Shaking Work?
The Underscore.js library has a unique scope called _ or Underscore. Before modularization functionality came to ECMAScript, libraries needed a scope that contained everything. The developer using the library could then have a durable reference to all the library’s services and functionality. Since this scope was the entry point to the library, it made sense to make it a function in its own right with its own tricks. With the advent of modules, this is no longer necessary, but the extra features offered by _ are still very useful and depended upon by many users. Besides the challenge with _, there is also a substantial amount of code in Underscore that bundlers cannot treeshake due to a limited ability to predict side effects.
Using Rollup for Bundling
Julian chose to use Rollup.js for the bundling library. His main reasons were:
- Rollup.js was suggested by Jeremy Ashkenas, the original author of Underscore.
- Julian felt the code produced by Rollup.js was the cleanest. Clean code remains an important focus area for the Underscore.js project.
Is Underscore.js Still Needed?
It’s true ECMAScript and TypeScript both bring a lot to the table. That said, most features in Underscore.js still aren’t available in the standard language. One example Julian showed us was using a map function over a plain object. The ability to handle this operation using a straightforward and readable line of code made for good source code readability and developer productivity.
Special Feature of Underscore.js, It’s a Story In Code
One bonus feature of the Underscore.js codebase is the code is written like a story. You can read the source code from top to bottom and get a clear understanding of how it works. The author and all contributors have paid particular attention to shipping code that is readable, understandable, and beautiful. Talk about Code Craftsmanship! Julian made sure to keep this element intact in the new modular version.
Helpful Links – Julian
- Julian’s Website
- Julian on Twitter @juliangonggrijp
- Julian’s Patreon page where you can support his work
- Julian’s Article Introducing Underscore.js Modularization