Observing useful tools and ideas for Monorepos in the wild…
Lately, I’ve been hearing and talking a lot about ways to scale code-sharing. A popular topic is the “monorepo”, or a multi-package shared repository.
Keeping separate packages inside a single repository is useful for code-sharing as it enables teams to share parts of their projects for other teams and projects to use, without the overhead of setting up and maintaining many repositories.
For example, imagine an organization where different frontEnd teams wish to collaborate and work with shared React components. To support that, they built a shared React library (like Pinterest’s Gestalt or HP’s Grommet).
To make this possible they think about going “monorepo”; keeping all components in a single repository while each is its own package. This “useful evil” also makes it easier to contribute, run CI/CD and manage changes.
In this post, I’ll review and share knowledge about the best tools and solution in the ecosystem for making this possible. Feel free to comment and suggest your own insights and tools from your experience!
1. Bit component monorepo
The thing about **Bit** is that every repository is already a monorepo.
You don’t need to refactor anything. You don’t need to restructure the project, set up different packages, define package.json files or anything else.
Instead, you just need to add Bit to the repository and share the components. This becomes possible thanks to Bit’s ability to automatically isolate components from the project, including all their dependencies and more.
Dozens of components can be shared within minutes from adding Bit to the repository, to become available in Bit’s hub, where they can be organized, shared and played-with online, then installed with NPM and Yarn.
When you make changes to one component in the repository, Bit will automatically track the changes and identify other components which use it as a dependency. Then if you run bit status you can see all the components which were automatically modified as their dependency was modified.
Meaning, no manually going through package.json files to manage changes to dependencies, as Bit does this work for you to make your monorepo maintenance much simpler.
You can also add Bit to your library’s release management (CI/CD) to update the components on every release with all the changes. Since Bit runs, build and test your components, you can quickly learn the new status of everything.
PRs can be made directly to the monorepo, or via bit import which lets others import and develop components right from their end project.
To sum up, if you have a component library you want to make a multi-package monorepo, Bit is probably the fastest and lowest-effort way to go about it. It also provides added values as discoverability, an online community and more.
Example
Semantic-UI component library before Bit.
Semantic-Org/Semantic-UI-React
*The official Semantic-UI-React integration. Contribute to Semantic-Org/Semantic-UI-React development by creating an…*github.com
Semantic-UI component library with Bit (0 refactoring).
teambit/Semantic-UI-React
*The official Semantic-UI-React integration. Contribute to teambit/Semantic-UI-React development by creating an account…*github.com
Pros
0 refactoring to the repository. It takes minutes from 0 to publishing 100 packages from your existing library. Try it.
Scalable code-sharing without the overhead.
Great visual discoverability for components.
Bit automatically manages all dependencies in the repo, including changes to component dependencies, and updates all components as required. No different package.json files to maintain or update.
Change management can be done via PRs to the repo, or via issuing component updates from any other repository by importing a component, making changes and issuing an update to the original repository.
Automated updates on releases (Bit in CI/CD).
Cons
Extension system is coming up. Until then, you might have to modify build/test environments a bit for different tools you work with.
Install as packages via Bit’s registry with the NPN/Yarn clients (your choice). Not supporting NPM’s registry.
All the hub’s features (search, playground, npm install) require that you host code in the hub (like GitHub and NPM). There’s no vendor locking (Bit is distributed like Git and can be set up on any server).
2. Lerna Monorepo
Lerna is a tool for managing multiple packages within a single repository.
With Lerna you can refactor your repository to keep multiple packages within it, while Lerna allows you to link dependencies (lerna bootstrap) and version them all separately or together as you choose (Independent mode would mean that every sub-package would have its own semver and get updated accordingly when its dependencies got updated).
my-lerna-repo/
package.json
packages/
package-1/
package.json
package-2/
package.json
Lerna automates tasks for packages in the monorepo. Thelerna run build will run npm run build inside each sub-package and lerna publishwill publish all packages to npm and will update git tags, etc. Then, packages can be installed from NPM’s registry.
Lerna requires some work at the get-go and more along the way, but is a solid way to keep a small number of core packages in a single repository and publish each independently to NPM.
Some open-source projects use Lerna to handle core packages, including Babel, Jest and Gatsby. It has many tutorials and posts around the web. It’s particularly useful for managing several parts of a single project in one repository, while each is a stand-slone “mini-project” on its own.
Example
babel/babel
*🐠 Babel is a compiler for writing next generation JavaScript. — babel/babel*github.com
Pros
Mature and plenty of tutorials around the web.
Version all packages separately or in a single line.
Link dependencies in the project with lerna bootstrap.
Automation for task execution for all packages in the repo.
Lerna publish publishes all updates to NPM.
Cons
Heavy refactoring for the repository. A lot of initial overhead.
Maintenance overhead grows with different package.json and environments for every package.
Discoverability for packages relies on NPM/library's docs.
PRs can only be made in the repo, and it can be hard to onboard new developers into the repo, which in turn impairs adoption of packages.
3. Bit + Lerna + Yarn workspaces combo
Lerna and Bit can play together in a single repository.
For example, Lerna can be used to manage the core packages while Bit can be used to share all smaller components. Bit also provides discoverability for components, and reduce the amount of refactoring and maintenance needed.
The two can also play with Yarn workspaces. While Bit will automatically manage dependencies between the components in the repository and Lerna will help you handle larger core packages, Workspaces will help consolidate and optimize the installation of external packages in the repository.
Pros
Use Lerna to handle 3–4 larger packages in the library while Bit will handle the dozens of components.
No overhead for sharing any number of components from the repo.
Discoverability for components and simpler collaboration.
Combine with Yarn workspaces to optimize workflow and performance.
Cons
Use 2 tools instead of 1.
You’ll have to manually maintain the larger packages.
Use 2 package registries (both work with the NPM client so after running 1 command it shouldn’t affect internal consumption).
4. Git submodules
Git is the SCM of choice for most dev-teams. It allows you to put one repository as a subdirectory of another repository, creating a single working-tree for the entire project, and one project can use code from another project.
But… Git submodules is, for most developers, a painful topic.
First, they only work on master branch. Second, submodules create highly coupled code between projects and make it hard to merge and collaborate on cross-repo tasks. A submodule repo also has no idea that it is nested, and might have a dependent repo.
There are various tools that provide additional automation around the Submodule feature, such as git-subtree, gitslave, braid and giternal. These tools try to improve some of the usability issues of Submodules, but are not supported by a specific vendor and have different drawbacks.
Conclusion
The discussion around Monorepos is thriving in the community. While most agree on the virtues of a multi-repo architecture, some use-cases make it just too hard to maintain and publish multiple packages from multiple repositories. Particularly, when working on a shared library.
To make this easier, new tools emerged in the ecosystem. From Lerna which automates the setup around keeping and publishing larger packages in a single repository, to Bit which takes the experience around component libraries to the next level, at 0 effort and with all the benefits.
Down the line don’t forget, that sharing code is about technology but also about people. You and your team have to be oriented to writing reusable code, thinking in modularity and encourage communication and efficiency. Thanks for reading, and please don’t hesitate to comment below and ask anything. Cheers
Top comments (1)
Can definitely see this working with vendoring. However, let's say we switch branches and fork some commit, how do we track down which version we actually forked without a dependency manager?