Software development is about solving problems. Sometimes the problems are tightly coupled with the domain and other times they are generic, not related to a specific business or field.
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.
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.
There are different kinds of libraries in terms of size and purpose. Whole frameworks could fall into an umbrella term of software libraries, but 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
- easily extendable
- well tested
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 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 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 in 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 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 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 CLI-s that solve most of the common problems of the library set up so we can focus on the code itself.
- Zero-config CLI for TypeScript package development
- Zero-configuration bundler for tiny modules