5 Practical Ways To Share Code: From NPM To Lerna, Git Submodules and Bit
Between multiple repositories, monorepos and micro-services the discussion about sharing common code between projects is increasingly raging.
Sharing common code between projects and repositories is a key factor for better modularity and faster development, but it’s also complicated. I’ve written about it before, sharing from our own experience as a team.
Here is a practical overview of 5 methods for sharing code between these repos and projects for 2018. Just remember, down the line it’s really all about communication between people, culture and keeping modularity in mind.
Bit (GitHub) is a popular JS tool that manages both source-code changes and dependencies for shared components. It’s a powerful way to share code as a team, and keep it synced across different apps and projects.
Bit’s ability to isolate and share code from any repo to another, while maintaining universal control over changes and updates, makes it a very scalable way to increase code-sharing as a team, and reduce overhead.
Bit lets you seamlessly isolate and share modules from any repo, while it wil automatically define their environments and dependency tree. The result is almost instant publishing of components from any repo, with 0 refactoring.
Then, you can install components in other projects with NPM/Yarn, or use Bit to import and develop the code right from the consuming project itself. When changes are made, Bit helps you update the versions and merge the changes.
Bit’s platform (bit.dev) also provides discoverability and collaboration over the components you share, which are organized in visual collections in the platform with per-component testing, build, visual rendering and more.
Share reusable code components as a team · Bit
Easily share reusable components between projects and applications to build faster as a team. Collaborate to develop…
2. NPM with / without Lerna
As you probably know all about NPM, let’s focus on a few limitations and how to overcome them. Making smart choices from the get-go saves time later.
First, it can be hard to setup and maintain many repositories for many packages. For this reason, some projects are built as multi-package repositories, also known as “monorepos”. Tools like Bit and Lerna (see below) can help you turn projects into multi-package repos, you can learn more here.
This also leads many teams to choose shared-libraries as a solution for sharing many smaller components (see below), as it’s hard to have a package for each.
Second, when someone else publishes a package you are limited in your ability to develop it, often leading to a pull-request to the packages’ repo. Tools like Bit help mitigate this problem, as it lets you bring the component right into any repo, make changes and share a new version.
“Many blog posts — scratch that, entire websites — have been created to try and mitigate the difficulty of finding what you need on npm…”
“It’s up to you to evaluate the library: does it have tests? Can you understand the source code? Is it actively maintained? Is the documentation easy to find and consult?”
Keeping different packages in different repositories can quickly get out of hand and make it very hard to update changes across your projects.
Lerna helps you to keep and configure multiple packages in a single repository. It can help mitigate the pain of different repos for different packages, and help build and test the project as a whole. This way, you don’t have to keep and maintain separate repos for different packages.
To learn more about going monorepo check this out:
Monorepos Made Simpler
How to leverage Bit + NPM to simplify monorepo architecture, with and without Lerna and friends.
3. Shared and common libraries
The advantage of shared-libs is that you keep all your shared code in one repo, which is easier to maintain and distribute than multiple micro-packages. However, unlike Lerna monorepos, they will be consumed as one package.
Keeping all your shared code in one repo forces users to add the entire library to their project with redundant code, dependencies, complexity and weight, all to use a single component.
It also makes the process of updates and modification very cumbersome, as every change requires the owner of the project to update the package. This can impair the adoption of these libraries within the organization. Discoverability for different components within the library is also an issue.
These problems led communities such as Lodash to work long and hard in order to publish their components as individual packages to NPM. Google’s Polymer project (by Eric Bidelman and other OSS wizards) also keeps over 100 web elements in over different 100 repositories.
Lerna can be used to separate components within the library into packages within the repo. Bit can be used to share components from existing libraries.
How Do We Really Use Reusable Components?
I’ve talked to 30 teams about their reusable components. Here’s what I’ve learned.
Shared Components Best Practices for 2019
Successful practices for developing and managing shared components between teams and projects with Bit and friends.
4. Git sub-modules
Submodules are effectively separate repositories within the directory tree of their parent repository. The only linkage between the parent and the submodule is the recorded value of the submodule checked-out SHA, which is stored in the parent’s commits. Changes in that recorded SHA are not automatically reflected in the submodules.
However, submodules have many, many problems. Just run a quick “git submodules” search on Google, and you won’t like the results. Among the major issues, you can find the simple fact that submodules don’t manage the dependency relationships between the modules. Smaller issues include issues like the fact that git submodule doesn’t work well with that parent, because a `pull` to the parent directory doesn’t automatically reflect in the submodule.
Also, git doesn’t save the pointer to the submodule when resolving conflicts. This means that if you don’t update the pointer manually you will lose resolved conflicts when merging changes. When you commit to submodule, git will leave you with a detached head in the parent because it is referring to an old “head” which is different from the current “head”. and guess what? When you push parent or submodules other parents don’t get exported.
There are various tools that provide additional automation around the Submodule feature, such as git-subtree, gitslave, braid and giternal. But as of today, the only tools that manage both source-code changes and dependencies for modules across projects and repos is Bit, in JS.
5. Copy-pasting code
Because let’s face it, what did future me ever do for me?
To be honest, it is perhaps the most commonly used practice for code “reuse” on earth. I’d like to think that in most cases it’s the result of the lack of a “cheap” effective alternative and somewhat chaotic delivery cycles.
The problem is, code duplications are not cheap at all. Far from it.
Duplications are an ever-growing debt in your code base. You will quickly lose control, making maintenance a nightmare and delivery cycles ever longer. Many problems will only be discovered after delivered to production.
is-string was duplicated over 1,000 duplications of 100 different implementations in only 10k GitHub repos.
Think about the new features that could have been written if more people would share code, rather than duplicate or reimplement it. Think about the costs of maintenance and long delivery cycles for your organization.
Sharing code is about people
Each project and every developer has their own set of concerns, tools and workflows. Still, sharing code is the key to true modularity, which becomes increasingly popular in todays’s ecosystem and provides great advantages.
We chose to build Bit. No matter what method and tooling you choose, it’s important to encourage a culture that breeds sharing and collaboration.
After all, sharing between projects starts with sharing between people.