Whenever we hear the world “Monolith” it kind a sounds like a large block of age old monument, but in the world of software development monolith is not age old, infact, monolith is the first choice. Whenever I start on a project, I prefer to go monolith. There are several benefits of that. For one, it has less components and its really fast to develop and release new features with.
But there are times when your monolith becomes a barrier for your development.
This happens because over time organization grows, more people and teams join the org, the org structure changes, multiple teams and developers work on the codebase and bring their own ways of working (which is good and worked for them in the past) but in the project as a whole, it gets too disparate, coupled and unmanageable. That is when you start thinking of breaking the monolith into micro-frontends.
Let’s discuss about the challenges of scaling monolithic applications and then you would be able to see how micro-frontends can help break down monolithic codebases.
Table of Contents
Challenges of Scaling Monolithic Applications
Scaling monolithic applications can be challenging, especially when accommodating the needs of a large team of developers across different region. So, let’s discuss some of the most common and challenging problems that the org face. Let me take you to a journey of a codebase. Let’s take an example of building a Shopping Cart.
In the beginning
When the project has just started there are not a lot of people writing the code for that project. There are just handful. Usually it’s a 1 pizza team and in some mid size companies it is 2 pizza teams. They all work very closely. They have regular standups and design discussions and everything just goes as expected.
The team is small, so they start with a monolithic code base. And I’m pretty sure any sensible person would start with a monolith because micro-frontend brings a lot of challenges in just setting up and getting started.
They create a basic boilerplate code and decide on their package structure. Usually, when deciding package structure we should think in terms of Domains and sub-domains (and this might sound very logical at first but to implement the same in the codebase requires some understanding of the key principles of DDD).
Let’s say their basic boilerplate package structure looks like the one below:
src/ ├── common/ │ ├── components/ │ ├── styles/ │ └── utils/ ├── cart/ │ ├── components/ │ ├── domain/ │ ├── store/ │ └── subdomains/ │ ├── summary/ │ ├── items/ │ └── checkout/ ├── products/ │ ├── components/ │ ├── domain/ │ ├── store/ │ └── subdomains/ │ ├── list/ │ └── details/ └── users/ ├── components/ ├── domain/ ├── store/ └── subdomains/ ├── login/ └── registration/
There are several benefits of dividing your codebase based on the domains from the get-go. You can read more about the folder structure in the following article:
- Domain Driven Design Structure Vs Fixed Structure
After 6 Months…
Since the team was working closely, they managed to create a disciplined environment that worked on the same principles and kept the code base intact in order. Now, their work is done and they move on to the next project. And by move I don’t mean they all move at the same time, usually there is a transition period for each dev, and a new dev takes their place. Now, since the company grew as well, they hire developers from all across the region. They need not be in the same timezone anymore.
Now that the base structure is ready, their job is to build new features on top of the existing ones.
And since the market is competitive, there is always the pressure to deliver quickly. In doing so developers start taking shortcuts and overlook the most basic principles. The shortcuts that I often find in the code is breaking the boundaries. For example, if they find a component that fits their requirement in some other package, they use it directly in their code.
Because there is nothing stopping them from doing so.
What is the drawback?
Well, anytime you depend on a component, you limit the flexibility of that component. In other words, that component cannot change without thinking about other components that are dependent on it. And this is the beginning of one big spaghetti that will take hours of their time if they have to keep building on top of something. To understand the impact of this little change, read the following article:
Now, once the same code which was free from cross dependencies starts to feel like an unorganized web of criss-cross dependencies.
After 18 months…
The code base grows a lot bigger more and more teams have started working on the same code adding different functionalities. But they observe something – the effort they are putting to change a single line in the code is taking more than an hour. Because now that single line of code is breaking 10 different components all across the package.
This is a code smell called “Rigidity”.
Rigidity literally means “the inability to change or be changed to fit changed circumstances”.
This specific code smell slows the development the most. Because now developer don’t only change the piece that requires change but fixes every other dependent module.
This is the time when developers start talking about the Domain Driven Design and creating clear boundaries. And since there are multiple teams working on the code base now, it becomes very difficult to refactor anything without changing someone’s else code.
And then they all come together and start talking about micro-frontends and other design philosophies.
Now all teams come together because they all face the same two problems:
Now their monolithic codebase has grown too big, making it harder to manage. The application may have multiple features and functionalities that need to be maintained, and as more developers work on the codebase, the number of lines of code can quickly become unmanageable.
It can also become difficult to make changes to the codebase without breaking other parts of the application, which can lead to bugs and other issues. Overall, the larger and more complex the codebase becomes, the more difficult it is to manage and maintain.
As code grows, the backend also faces similar problems. The way backend solved this problem has become a buzz word that I’m sure you might have heard of. It’s called “Microservice Architecture”. Microservices architecture can break the monolith into smaller, more manageable pieces.
But the frontend codebase often remains the same, leading to issues with scalability and maintainability.
How to Solve Above Problem?
It is not feasible to start with micro-frontend from the get go. Because you are not sure of all the domains, also it will slow your team development velocity by a significant factor. But after certain time when you can clearly see your domain boundaries, it is always better to move them into their own micro-frontend before the team size grows too big.
As you can see, the code base was in very good order when the first team left. That was the time to take a step back and think, should we break this project before proceeding further.
The new structure would have looked pretty clean with micro-frontends.
Let’s try to understand the same with the help of a diagram:
In the code journey example above, you could see that developers started off with a pretty good modular structure with clear boundaries. And if they would have stopped and first moved different packages to different micro-frontends, they would have never fell into the current situation of code rigidity.
But now, they will first have to remove all cross module dependencies and then move each module into a micro-frontend. The efforts have increased but once they achieve this micro-frontend architecture. Each team can go back into working the same manner that the first team started with.
So, I hope you see the benefits of micro-frontends.
I will write another blog in the succession of this one where I plan to discuss at the code level how it looks like.
Hope you liked the article, don’t forget to share your views.