JavaScript Monorepos: Exploring Decentralized Alternatives



This content originally appeared on Bits and Pieces – Medium and was authored by Eden Ella

Enhancing Your Javascript/Typescript Codebase for Flexibility, Shareability, and Scalability to Address the Needs of Your Micro Frontends, Microservices, or Any Other Micro Architecture.

Going from a standard monorepo to a composable architecture to a completely decentralized

Monorepos are a popular way to manage multiple projects in a single repository. They are often used to address the challenges that come with micro-architecture patterns, which are becoming increasingly popular, especially in Javascript and Typescript projects.

Micro-architecture is an umbrella term for various software design patterns that break down a monolithic codebase into smaller, more manageable pieces. We know it as microservices, micro frontends, micro apps, and so on. In a way, even a simple division of ‘backend’ and ‘frontend’ can be considered a form of micro-architecture.

“Micro” is great. It’s the essence of composability, which is the key to building scalable, maintainable, and collaborative software systems. However, as we all know, it certainly has its challenges.

Sharing code between micro-projects, managing dependencies, and ensuring consistency across the codebase are just a few of the challenges that come with these patterns. In many cases, monorepos are the obvious solution.

A simplified diagram of a generic monorepo

Monorepos allow you to manage multiple projects in a single repository and understand the relationship between these projects. They make it easier to share code, manage dependencies, and ensure consistency.

But what if you don’t want to use a monorepo? What if you prefer to keep your projects separate but still want to enjoy the benefits of a monorepo? After all, you don’t really enjoy the simplicity of micro projects if you have to deal with the complexity of a monorepo. And, in many cases, your monorepo solution does not provide teams with the autonomy they need to work independently.

In addition, you might want more flexibility in managing your projects, dependencies, and releases. Your team size and project complexity might also vary, and you might want to manage your projects differently based on these factors.

Perhaps some projects should be maintained as a single project in a single repository, while other existing projects should be split into smaller, more manageable pieces. You never know what the future holds, and you want to be prepared for any changes that may come your way.

Polyrepo

An alternative to a monorepo architecture is to have each project in its own repository in what is known as ‘polyrepo’. If some projects share code, you can publish that code as a packages and install it as dependencies in the projects that need it.

From monorepo to multi-repo/polyrepo

Using this approach, the micro-project builds are isolated and, therefore, “naturally” effective. No need for sophisticated build tools to manage the complexity of a monorepo. Moreover, you can manage each project independently, which gives each team the autonomy they need to work effectively.

This is a common approach, but it has its limitations. It’s incredibly hard to manage dependencies and ensure consistency across the codebase.

Development standards and consistency are hard to maintain, code discovery is difficult, and it’s hard to ensure that changes in one project don’t break another project.

Composable codebase

“Composability,” in the context of web technologies, usually means Bit.

Bit allows you to manage each component of your codebase as a standalone entity called a Bit component.

From monorepo to composable

These Bit components can be shared across projects, edited and updated from any project, installed as dependencies, and versioned independently.

Bit components enable you to manage your codebase in a decentralized way without the need for a monorepo. But before we jump into a completely decentralized codebase, let’s see how projects can benefit from this approach when implemented in a single repository. You can call it a fine-grained monorepo, or a composable repository/codebase.

From Libraries and Apps to Bit Components

A monorepo hosts two types of projects: libraries and apps. Libraries are reusable pieces of code that are shared across multiple apps, while apps are standalone projects that use these libraries (and other dependencies). “Apps” in this context can be anything from a microservice to a micro frontend to a full-stack application.

Bit components can be apps (or services) and libs. There is no fundamental difference.

This distinction does not exist in a composable repository. Bit components make up the entire codebase, from the most basic utility functions and UI elements to full services and applications.

This greatly simplifies your project maintenance. Your “fine-grained monorepo” is a simple collection of Bit components that are agnostic to their location inside the repository and even outside of that repository (as we’ll see later).

A table summarizing the main differences between apps and libs on one side and Bit components on the other

A repository consisting of Bit components will have a simple structure that is similar to any other composable design. Each Bit component has its source files in its own directory and a single file to serve as its entry point or “main” file. Each Bit component consumes other Bit components via their symlink/installed instance in the node_modules directory.

For example, this repository structure is of a simple application that consists of two Bit components: a button and an app component.

button/
button.ts
index.ts (entry point/ "main" file)
my-app/
my-app.ts
my-app.bit-app.ts (defines 'my-app' as a deployable app component)
index.ts (entry point/ "main" file)
node_modules/
@my-scope/button/
index.js -> (symlink for type support and intellisense) ../../button/dist/index.ts
button.js -> (symlink) ../../button/button.ts
dist/
index.js (compiled)
button.js (compiled)
@my-scope/my-app/
index.js -> (symlink) ../../my-app/dist/index.ts
my-app.js -> (symlink) ../../my-app/my-app.ts
dist/
index.js (compiled)
my-app.js (compiled)

Maximizing code reuse in and across repositories

Normally, a piece of code needs to be extracted into a library before it can be properly reused across projects. This process can be cumbersome and time-consuming and often results in the reuse of only a limited part of the code.

Bit solves this problem by automating the usual process of creating and maintaining libraries. Every Bit component is a “library” by default, and it can be reused across projects (and micro-projects) without any additional effort.

To name just a few examples of this sort of automation:

  • Dependency management: Bit automatically detects and intelligently resolves dependencies for each Bit component. When a component is built before it’s shared, Bit generates its package.json file.
  • Versioning: Bit versions each Bit component independently when it changes or when a dependency of if changes.
  • Documentation and examples: Bit automatically generates documentation and examples for each Bit component, making it easier for developers to understand and use them.
  • Testing and CI: Bit runs tests and CI for each Bit component, ensuring that it works as expected before it’s shared. Components are never tested in their current context but in a clean environment that ensures they are truly reusable.
  • Discoverability: Bit components are easily discoverable and searchable, making it easy for developers to find and reuse them. Each component can be found in the Bit platform, which is a central hub for all Bit components.
A demo full-stack “blog” project composed of Bit components.
See this “blog” Bit scope example to explore a demo composable project.

Decentralized codebase

A decentralized codebase is one that is not managed in a single repository but is distributed across multiple repositories or even none at all.

As mentioned earlier, when working with Bit components, you decide their source of truth. It can be a project-level repository hosted on a platform like GitHub, GitLab, or Bitbucket, or a component-level version-control hosted on the Bit platform.

Bit components are hosted in access-controlled component collections called “scopes,” which are usually hosted and managed on the Bit platform.

Depending on how you structure your team’s workflow, you can decide what would be used as their source of truth: the Bit scopes on which they are hosted or the project repositories in which they are maintained.

Maintaining Bit components in a decentralized way has several benefits. You can achieve fine-grained access control, where components maintained by the same repository can be modified only by the team members who have access to their Bit scope.

A composable approach can be implemented as a monorepo or as polyrepos

But that doesn’t stop there. Having the Bit scopes as the source of truth also allows you to manage your components in a more flexible way.

Components can “move around” between repositories, and you can decide where they should be maintained based on your team’s workflow and the project’s requirements. All that, without the usual overhead of managing dependencies and ensuring consistency across the codebase.

In its most radical form, a decentralized codebase can be maintained by “disposable” repositories. These repositories are created and deleted as needed, and they host only the components that are needed for a specific task or project.

When the source-of-truth for Bit components is their Bit scope, the repositories transform into disposable tools

This approach makes the most out of the independent nature of Bit components, allowing you to manage your codebase in a truly decentralized way where autonomy, simplicity and flexibility are the keys.

A no-repository / completely decentralized approach

Learn More


JavaScript Monorepos: Exploring Decentralized Alternatives was originally published in Bits and Pieces on Medium, where people are continuing the conversation by highlighting and responding to this story.


This content originally appeared on Bits and Pieces – Medium and was authored by Eden Ella