5

Is it safe to share code between Microservices?

 9 months ago
source link: https://blog.bitsrc.io/is-it-safe-to-share-code-between-microservices-2b5729401927
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Is it safe to share code between Microservices?

Understanding the Fundamentals of Code Sharing in Microservices

0*i-uWb0Qm_N1bKMwn.png

Over the last decade, Microservices established itself as a goto architecture style for autonomous teams. It also created a dilemma between developers on code reuse across these distributed services. As most of us have experienced, autonomy vs. sharing doesn’t go hand in hand. Sharing creates dependence. Therefore, many Microservices teams favor autonomy by limiting reusing code between service. Instead, each Microservice team is encouraged to reuse business functionality via APIs.

Is reusing code relevant?

As developers, when we write code, each line becomes a weight to carry for the rest of the application’s lifetime. And the functionalities we develop are not always new. Therefore, we used to write code with the reuse mindset, avoiding code duplication. Even the number of duplicate codes in a code base has become an indication of the code quality.

In the context of a single Microservice, we still follow these fundamentals of code reuse.

The question comes when we go beyond the Microservice boundary. Since each Microservice team requires the agility to add functionality and release autonomously, having a dependence on code with another team creates a bottleneck.

Let’s first understand why sharing code between Microservices is difficult before we figure out how to solve this problem.

Understanding the bottlenecks

0*T-9E3_J0ukYTE-vu.png

Let’s look at one of the common use cases when reusing code. Suppose you find a bug in the code reused from another team. You may have to wait for the team owning the codebase to fix it. Even if you can go through the code and fix the issue, it may be challenging to see its overall impact if more than one Microservice uses the shared code. The team owning the code has a backlog with their priorities.

But we always reuse third-party libraries even in Microservices, and why not from other teams?

The answer lies in what we share, how we share it, and how many dependents use it. Suppose the code we reuse is primarily technical, like a utility library or cross-cutting code like authentication, validation, etc. In that case, the scope of its functionality is focused and least changing in nature. That’s why even Microservices teams have a platform team that develops and maintains the code, which addresses cross-cutting concerns.

Also, these code libraries are available with semantic versioning, where you can decide when to get the latest version.

What about reusing business logic?

If the code is more functional and related to business logic, each Microservice team needs to get the latest update once it’s modified. Otherwise, the application or system as a whole may not function properly. Besides, the business logic as packages may create a dependency chain that makes it challenging to keep track of the impact after a modification. The complexity may increase if you have a distributed code base in multiple repositories.

To understand the complexity of sharing code across Microservices, let’s look at the different forms of code we share.

Different forms of code we share

0*K06rWdrGkQWRmf-Y.png

1.Raw code — The most basic form

At the foundational level, code sharing often begins within monorepos, where referencing code across services is relatively straightforward. In this approach, a mechanism should be embedded in the build system to identify the Microservices that consume the shared code, test them, and deploy the up-to-date version to keep consistency across the functionalities.

If we use multirepos, we must use techniques such as Git submodules or subtree implementations to interlink repositories, facilitating code sharing between distinct services.

However, while these methods enable code reuse, they also pose challenges. In monorepos, we must define clear boundaries in the file system and create a dynamic build system to build relevant services that depend on the modified code.

In multirepos, without using any external tools, managing dependencies and synchronizing changes between repositories can become cumbersome, leading to versioning conflicts and coordination issues. Therefore, reusing code in this form between Microservices could become a challenge in the long run.

2.Packages or modules — Code with well defined boundaries and managed lifecycle

Moving towards greater abstraction, using packages or modules is a logical progression.

In the context of JavaScript-based Microservices, NPM is an excellent example of packages and modules. A module is any file or directory in the node_modules directory that the Node.js can load require() function. Only modules that have a package.json file are packages.

This approach works well with utility libraries and foundation code, which doesn’t change often. Therefore, it is not suitable for business logic, which is subject to change with business dynamics.

Besides, if your packages depend on each other, it could make their management difficult. The main reason is that each package has its own lifecycle, where you need to test each dependent package in isolation to ensure they function correctly.

Additionally, reliance on external package owners for version updates can impede agility and autonomy.

3.Independent Components — A composable approach to share code organically between Microservices

Independent component architecture allows flexibility to share code in components with well-defined boundaries. The main difference between packages and independent components is that you have a toolchain that supports its development, testing its ripple effects rather than working with a single package in isolation.

This approach demands robust tooling to automate dependency builds, contribute changes seamlessly across services, and thoroughly test the impact of modifications on interdependent components.

Let’s use Bit build system as an example to understand independent components.

1*oHvztoPFFSmYEecdOhonSw.png

Independent components form a chain of component dependencies across separate remote scopes. ‘Scopes’ are component hosting servers enabling collaborative actions like fetching and updating components with new ‘snaps’ or versions.

The chain of dependencies enables changes to propagate from one modified component to all its dependent components. That is, a change made to one component triggers the component’s CI and the CI of its dependent components in a cascading fashion.

Simply put, independent components make it natural to share code between Microservices, as each service is composed of a collection of components.

Summary

0*nOUlGfmE5dJLKnkP.png

Comparing these different formats, it’s evident that while each stage offers advantages, it also presents distinct limitations.

Monorepos or Multirepo with Git submodule/subtree implementations provide a starting point for code sharing but lack scalability and efficient management. Package-based approaches offer modularity but are primarily suitable for utility libraries.

Moving towards independent components signifies a higher level of granularity and control. You need a toolchain like Bit to handle the underlying complexities and coordinate efforts across teams.

I hope this gives an overall understanding of sharing code between Microservices. If you have different opinions or suggestions, please share them in the comments below.

Thanks for reading!


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK