To avoid repetition in solving problems, we as developers have the ability to abstract & extract our solutions to self-contained modules, most often called libraries or packages.
And despite the fact that there are currently more than a million packages available on NPM, there is still potential and value in creating your own.
What makes a good library?
There are different kinds of libraries in terms of size and purpose. Whole frameworks could fall onto an umbrella term of software libraries, there are also one-liner functions wrapped in packages that are, by definition, also considered libraries. Their context is often different, but some common rules can be applied to all of them.
- solves a specific problem
- has good documentation
- is easily extendable
- is well tested
Why bother making a library when you can copy and paste?
Abstracting a problem has its own costs. Sometimes, making an abstraction too early or without a defined plan can lead to accidental complexity or incorrect implementation. Therefore, using good old copy and paste strategy is oftentimes the preferred solution — until we know the problem deeper.
But, if the code is copied too much, it can become difficult to maintain and upgrade. The reason behind this is often because the copied code relies on repeating some patterns in a certain way and if we don’t repeat the same structure, bugs can occur.
Knowing the right timing for abstraction is an art of its own, but a general rule of thumb is that a piece of code is a good candidate of abstraction after being copied for two or three times.
To be considered a JS package that can be published to NPM or Yarn, it should have the following set up:
- valid package.json
- Universal Module Definition
- Asynchronous Module Definition
- ES2015 Modules
This is a minimum requirement and, depending on the complexity of the software, there could be many more parts to it.
The best way to develop a library is to treat it as a realm of its own, if possible. That means that it has its own testing strategy and its own system for running the project in isolation.
In scenarios when this is not possible or we just want to test our package in a real-world application before publishing it, there are ways to use them locally.
NPM / Yarn Link
Local development can be achieved by linking our packages to a single local source stored on the disk. So, if we’re using Yarn, we can use the yarn link command, and for the NPM there is an equivalent command.
Yalc is a great tool to achieve more control of local package installation and dependencies. It behaves as a local repository and simulates the behaviour of having a remote repository like NPM.
Some libraries may be too big to be contained in a single package. Such libraries may be architected as a mono-repository, by storing multiple smaller packages in a single repository.
This can result in better control of the source code and better modularity, especially if many parts of the system depend on different independent modules, or consumers of the library don’t need to use everything that the library contains.
General software development practices can be applied to library development. Most notable elements are:
- semantic versioning
To simplify the package development, we can use CLIs that solve most of the common problems of the library set up, so we can focus on the code itself.
- TSDX — zero-config CLI for TypeScript package development
- Microbundle — zero-configuration bundler for tiny modules