Choose your own route!

Published: 2020-01-20 by Lars

Ecce Cycles Opus Wood

"Let's use React Router!"

"No, let's build our own router!"

This type of discussion comes up frequently on any project. This blog post is not about React Router specifically, but a look into which principles should guide us when we try to make a decision between taking in yet another third party dependency, versus building yet another in-house library.

There will be situations where the right choice is to add an existing library, and there will be other situations where the the right choice is to roll our own. But the right choice will be different for different projects, and whether a given choice was actually the right one for a specific project is usually not known until months or years later.

So how do we make a good choice with a high chance of being the "the right" choice?

In the following discussion, I cover six criteria to assess the trade-off between writing our own library and using the hypothetical library, LibX. I will illustrate the trade-offs using examples from the front-end JavaScript world, inspired by choices we made early on (mostly around 2016) at Triggerz:

1. Is it widely used?

If LibX is widely used, we should definitely consider using it, instead of writing our own. There will be lots of community support and it will be much easier to find developers who already know this library. Widely used libraries also tend to work well with other widely used libraries.

If LibX is not (yet) widely used, using it will be more risky. Maybe LibX is about to become widely used, but it might also stay a mostly obscure library, with a waning community and a lack of development.

At Triggerz, we initially chose React as our view rendering framework, despite React not being widely used at the time. But React scored well on most of the remaining criteria, and we correctly predicted that React was soon to become widely used. Today we are very happy with our decision.

At Triggerz, we also chose React-rangeslider early on, which wasn't really widely used either at the time, and has turned out to become a mostly obscure library. Today there are better options available, but back then, we would probably have predicted that React-rangeslider was never going to become widely used, and we would probably have been better off writing our own slider component.

2. Is it high quality?

We must assess the quality of LibX before we consider using it. Is the library well-covered by automated tests? Is it well-documented? How good is the support provided by the authors? Is LibX free of serious bugs? Is the API robust and reasonably stable? If the quality is not high enough, it could quickly become a better trade-off to invest in writing our own library than spend time fighting a buggy, non-supported LibX.

At Triggerz, we initially chose Webpack as our asset bundler, despite Webpack having substantial quality issues at the time. Webpack scored well on most of the remaining criteria, and happily Webpack eventually got their quality under control, to a level where we haven't really had to spend much time on it.

At Triggerz, we decided not to use Moment as our time-and-date library, for a number of reasons, including concerns about its level of quality. Moment still appears to struggle with quality, having hundreds of open issues and pull requests (at the time of this writing).

3. Does it handle all our needs?

Yet another criteria for choosing to use LibX instead of building our own library, is that LibX will be able to handle all our needs, not just today, but also future needs.

If LibX is missing some tiny feature we need, we might choose to fork LibX, and add the feature we need, and keep our fork updated with new versions of LibX. We might also be able to convince the maintainers of LibX to accept a pull request with the feature. This way of combining the strengths of "reuse existing library" and "build your own library" is a good example of the benefit that Open Source brings to software development.

At Triggerz, we use a library to read Excel-files, providing us with a business-friendly configuration file format. We were able to find multiple libraries that were sufficient for our use-case, since we didn't need much besides the ability to read and write the files.

At Triggerz, we use Fetch-mock to mock HTTP requests, which at some point stopped supporting IE11, which is an important requirement for us. In this case we forked the library to get the functionality we needed.

4. Do we need all of it?

The flip side of "does it do all we need?" is whether we need all the features provided by LibX. You may think that this is not really important. Who knows, maybe we will need some of these currently unused features, some day in the future? Isn't that a good kind of insurance?

However, all extra functionality comes with a cost, so the question becomes whether we want to pay that price for features we only potentially might need in the future. The cost of extra functionality comes in many forms: with more functionality there will usually be more complexity in the code, which can lead to slower performance, and almost certainly will make bugfixing and further development slower for the authors of LibX. It also often makes the library harder to work with for us, because of the cognitive overhead in having to understand and work around concepts, we don't actually use or need.

So it is usually optimal if LibX covers just what we need.

At Triggerz, we do not use GraphQL, but instead wrote our own very simple data query library. GraphQL provides a ton of functionality that we don't need, and avoiding that overhead keeps our code base easier to work with.

At Triggerz, we use React, which provides quite a lot of functionality compared to simpler view rendering libraries, and we believe we use most of it.

However, at Triggerz we also use Webpack, and the Excel library mentioned above, and here we probably use only a small subset of the functionality provided. We wouldn't want to implement our own libraries for this type of functionality, but if we could find smaller libraries which scored well on the remaining criteria, we might benefit from switching.

5. Does it fit our paradigm?

It is usually a good practice to follow a specific paradigm for your code-base, to make it easier to read and maintain. Some projects prefer an object-oriented paradigm with classes, others prefer a functional paradigm with immutable data, others again prefer a reactive paradigm with observables.

At a more specific level, some projects centralize state management, using something like Redux, where others prefer to distribute state management between individual components.

At a very detailed level, some projects prefer to use unix time (milliseconds since 1970-01-01) as the common representation of time, other projects prefer to use a built-in Date object, and others again prefer to use ISO-8601 formatted strings.

When considering if LibX would be good for our project, we should check if it fits the same paradigm as the existing code base and libraries already in use.

For example, at Triggerz, we mostly follow a functional paradigm with immutable data, as required by our use of Redux for state management. So instead of using Lodash or Underscore that have some mutating functions, we picked Ramda.

Our use of centralized state management with Redux, also made us decide against React Router which does its own handling of navigation state. Instead we ended up writing our own small Redux-based routing library.

At Triggerz we avoided Moment as our date-time-library because its object-oriented paradigm wasn't a good fit with our functional approach. We could have chosen the functional Date-fns instead. However Date-fns works with JavaScript Date objects, but we use ISO-8601 formatted strings throughout our code-base. For this reason we ended up writing our own small date-time-library building on top of the native JavaScript functions.

I was recently tempted to use Redux Toolkit on a new project, until I discovered that it bundles Immer into its API. I believe Immer is a cute and clever library that completely misses the point of doing functional programming in JavaScript. Allowing developers to write "imperative" looking code, might make for easier onboarding of new developers, but also substantially increases the risk of developers writing actual imperative code somewhere else in the application where Immer is not around to save us.

6. Do we have the skills?

Even if a lot of the criteria above lead us to consider writing our own library, we must concern ourselves with our ability to take on such a task. Writing a library that has an easy-to-use API and is free of bugs, is not in itself an easy task. In addition, there are all the details about getting potentially tricky functionality implemented correctly for all the myriad known and unforeseen circumstances. We should act humbly here.

At Triggerz, we have plenty of examples of libraries that we decided not to attempt to implement ourselves. Examples include: Draft.js for rich text editing, React DnD for drag-and-drop, Handlebars as template engine, Engine.IO for websocket communication, Lunr for fuzzy searching.

On the other hand, we did decide to implement our own data query library, instead of using something like GraphQL. Some of our team members had succesfully developed dedicated data query libraries on earlier projects, so we were confident about our skills in this area.

Summary

You can use the checklist below to assess your options when given a choice between one or several existing libraries and writing your own library.

I will suggest that you discuss with your team how to prioritize and weigh the different criterias relative to each other for your particular project.

Remember: the right choice will be different for different projects, and whether a given choice was actually the right one for a specific project is usually not known until months or years later.

This blog post is brought to you by the Eleventy static site generator, which is not currently widely used, is pretty high quality, handles all of my needs, but also quite a bit more. In a few years, I should know if this was a good choice!

Wooden Bicycle Cutout

Discuss on Twitter