Why we shifted to monorepo from sub modules?
Managing(versioning, deploying, linting) multiple code repositories starts to become a hastle specially when code grows with time. Similarly, when teams grow, cloning and having multiple repositories at local, starts making the developers a little uncomfortable.
Moreover, at times chunks of code have to be re-written on the frontend(FE) as well as backend(BE) (or vice-versa), respectively. Which can easily be shared between both the sides. Consider a case when
export interface IUser {
id: string;
name: string;
age: number;
...
}
an API/users/:id
returns an object, which implements IUser
interface on BE (assuming js on server side).
When the FE recieves the result, maybe for type safety, my partner FE engineer has to re-write the whole interface in the repository and use it in the project. This adds to code redundancy which eventually grows with time. Can you think of some cases when you had to write redundant code?
Lets consider another example.
BE is using a helper function while recieving some data in the API.
public isMultiline(string: string): boolean {
return string.includes('\n');
}
Now, a case arises when I at FE side, need to validate the input if its multiline or not before sending the data to BE. If I had the access to this helper function (which a BE person had already written) it would have saved my time as well as DRYied our team code. (DRY - don’t repeat yourself which we were told in the second class of intro to programming :p)
Let’s say, I have 3 FE applications where users authenticate from an external server. Either
- I’ll create a sub module (or sub repo — having all the authentication implementation logic) and import it in all the three parent applications.
- or I’ll copy paste the exact same code in three different applications.
Since I heard about writing DRY code, so I went with option 1. What if there is some additions in authentication mechanism and now I thought to implement refresh token mechanism as well?
Most probably,
- I’ll implement the new feature.
- I’ll bump the version of sub module.
- import(or pull) the latest version of sub module in all the three parent applications.
- bump the versions of parent applications.
- finally I’ll kick the CI/CD to deploy the applications.
Hmm cool. Things seemed to be working fine, but now since I was in a hurry and couldn’t test much, there is a major bug in the authentication mechanism and I need a revert. Ooops, either
- I revert the parents applications to previously released version
- or I revert the sub module to previous version, and bump up the versions again for parent applications pointing to previous version of sub module to kick CI/CD and get the previous state of my submodule deployed. (as a new state for parent applications though).
(bumping the version is not a 🚀 science. Just from 2.0.0
to 2.0.1
in version file for my CI/CD to kick and keep track)
Just scale out these small problems, you might face in everyday development within your team. FE person is on leave, and there is a key
change in a particualr object from BE for which FE has to make the appropriate change and do a deployment.
It would have been better, if the BE person just made the change on both the sides with a single commit.
Having a monorepo might solve these issues for you are facing them in your everyday development.
Essentially, monorepo gives me a power to have multiple (BE, FE) applications under one umbrella.
I set the things up once, and all the packages, sharable code required by the respective applications, is imported and complied. Node modules are also reused amongst the applications (talking about js applications)

I can put the sharable code, in shared static libraries. e.g.IUser
interface now can be put in sharable folder, and imported by first setting the path in your tsconfig.json
to@shared-interfaces/user.ts
and then simply importing IUser
in my application via @shared-interfaces/user.ts
. Similarly, helper functions, classes, modules can all be placed in sharable libraries. (I just DRYied my code a little).
Furthermore, as I mentioned a potential versioning problem eariler, version controlling can generally be improved as well in monorepos (though depends on the standards your team has set). Rather than bumping versions back and forth for parents and child repo, maybe for the above example, I can revert to previous version of monorepo and achieve a full revert in less time and hastle (for simplicity I am considering my last commit in the monorepo was of authentication refresh feature implementation I implemented).
Having a monorepo is an ideal option to have a seemless fullstack development experience having all your client side and server code in one place — or having multiple FE applications or BE applications in one place etc, saving time for both the sides :) You can read more about it here as well. Though, monorepo has its down sides as well, but for now I’m focusing on the +ves ;)
If you’re having similar problems to what I mentioned eariler (since they actually happened with us in our team at scale) monorepo can be the way forward. We had to make a call in our team and eventually shift slowly towards bringing in monorepo for managing our entire product.
PS — NX can help you setup your monorepo and get you going in a flash. Happy coding :)
Special thanks to my partners in crime Ali Hussan and Igor Boiko for guidance and support!