Modularizing to improve the developer experience

Aaron Francis General / February 2, 2021

Modularizing to improve the developer experience

One of the major things we’ve just released in Handsontable 8.3.0 is a new modularization system. We’ve been working on this project for a long time and wanted to cover it in a little bit more detail here.

Modularization is the process of turning things that are not modules… into modules. That’s not very helpful unless we first know what modules are. 

In their most basic forms, modules are self-contained bundles of code. More technically, modules are bundles of code wrapped up according to certain standards that make them available for consumption in other places in your app. In JavaScript, there are several standards for defining modules: CommonJS, UMD, AMD, and native ES6. There may be even more ways. That’s the good thing about standards, there are always new ones coming out.

There are many volumes written on CommonJS, UMD, AMD, ES6, and since this isn’t a technical article on how to modularize, but rather why we’re modularizing, we won’t dive all the way into those details. If you’d like to read more, I’d recommend any of the following:

JavaScript Modules: A Beginner’s Guide (freeCodeCamp)

JavaScript Modules (MDN)

Understanding (all) JavaScript module formats and tools (Dixin’s Blog)

The discussion around how to modularize is somewhat interesting from a technical perspective, but I think the discussion on why we’re modularizing is much more interesting from a developer experience perspective.

Modules as Units of Thought

The module concept goes by many names, depending on your language or framework. It could literally be “module,” or it could be “trait,” “concern,” “mixin,” or something else entirely. Here we’ll continue calling them modules, since we’re building in the JavaScript world.

Modules are supposed to be self contained, encapsulated, isolated. 

Modules are supposed to do one or two things really well, present a clean interface, and not leak their details to the outside world. 

Modules are meant to have good boundaries, so they are easy to reason about. 

That last point is key. When a module has well-defined boundaries, it’s easier for developers to hold the entire scope of responsibility in their minds.

If you’ve ever looked at the Rails source code, you know that DHH loves concerns

If you’ve ever looked at the Laravel source code, you know Taylor Otwell loves them too.

Even for some things that only ever get included in one place, you can still factor out a module at the boundary. DHH advocates for this explicitly in Rails for a specific purpose: it makes it easier for the developer to reason about.

The Handsontable Modularization Project

If you’re not familiar with the Handsontable codebase, it lends itself to modularization in a very natural way. 

We have several discrete concepts that have emerged naturally over the life of our project:

  • Editors – responsible for editing data
  • Renderers – responsible for rendering data
  • Validators – responsible for validating data
  • Cell Types – wraps an editor, renderer, and validator into a single package
  • Plugins – extends core functionality of Handsontable

We provide first-party cell types like text, checkboxes, passwords, autocomplete, dates, etc. All the normal stuff you’d need in an editable data grid. We provide dozens of first-party plugins as well.

All of these units have emerged from years of coding and refactoring, but the boundaries haven’t been as clearly formalized as we’re making them now. There are bits of implementation scattered throughout, plugins that have intimate knowledge about other plugins, shared state, etc.

One of the results of these almost-but-not-quite boundaries is that it takes a very high level of knowledge to be able to understand and contribute to the Handsontable codebase.

Without clear boundaries you have to think a lot more about externalities. Will this little change affect some unknown-to-me part of the app? Guess you better go figure that out! It takes new developers longer to get up to speed, and all developers have to maintain more state in their head.

The lack of boundaries affects developers implementing Handsontable inside their app as well. Without clear module boundaries, the developer can’t take advantage of tree-shaking capabilities of modern bundlers. Developers are forced to use Handsontable with all plugins included even if they don’t use most of them, there is no easy way to include only plugins required for their specific use case. This slows down the page load time and decreases the end-users experience.

Our goal is for Handsontable to be the most developer-friendly JavaScript data grid available.

We think aligning the experiences of the internal and external developer is going to go a long way toward that goal. Both internal and external developers should be able to reason about encapsulated plugins, renderers, etc., without intimate knowledge of the rest of the library.

During our modularization process, almost everything becomes a discrete module which can be imported, tree-shaken, tested, and reasoned about. 

The learning curve for new developers is lower, and the long intertwined tentacles of old code are chopped off.

Preparing for the Future

It feels great to finally repay the technical debt and meet the expectations of  a modern web developer, and it makes us even more excited for future development. We have a lot planned for 2021 and we need a solid foundation to build on.

We want to improve the developer experience, reduce the bundle size, and increase the speed with which our developers can ship improvements  and features to our users.

If you’re interested in reading more about how to use Handsontable’s new module feature, we’ve written a guide to show you how you can build  a highly customized build: read it here.

We’ve been working on this for ten years, and we’re willing to invest the time and energy to shore up the foundations for the next ten.