Monolith vs Microservices: Comparison

Learning curve

  • In a monolithic architecture, the learning curve is relatively straightforward and beginner-friendly. All feature files are consolidated into one codebase, simplifying the addition of new features - just create a directory/file with the feature name and start adding the code. This approach eliminates the need to worry about infrastructure, separate deployment workflows, or additional resources, as existing ones are utilized.
  • On the other hand, microservices are more suitable for engineers aiming to build scalable applications with diverse skill sets. Each new feature exists as a separate codebase, utilizing distinct resources, databases, and deployment processes. Developing within a microservices architecture demands a well-thought-out plan and individuals capable of building functionalities from scratch. To provide further clarity, each microservice can be equated to a separate mini-project.

Skillsets:

  • Due to its single codebase, a monolith necessitates the use of a single programming language, specific frameworks, and a singular tech stack. Furthermore, all team members must be well-versed in the common skillsets required for the chosen technologies.
  • In a microservices architecture, there is no such barrier. Each microservice can be developed in a different programming language such as Python, Node.js, Java, and more. The advantage here lies in the ability to choose the most suitable language and framework based on specific project requirements. Furthermore, developers are not restricted to a common language; they can code in their preferred language. Microservices promote diversity in teams and offer the flexibility to select the most appropriate tech stack for individual services, thus providing the best of both worlds.

Re-usability:

  • Indeed, in a monolithic architecture, the ability to reuse code across different features is a clear advantage, reducing development time significantly. Existing code for feature1, such as utilities or string null checkers, can be easily utilized in feature2 as needed, promoting code reusability.
  • In a microservices architecture, using a common utility file requires copying it into the respective microservice. This process can become monotonous and may introduce complexities related to code management.

Fault isolation:

  • In a monolith, each refactoring action or code merge poses a threat of causing a side effect i.e. breaking a feature somewhere else unknowingly. It also has lots of interconnected modules/services/files communicating with each other and has logs from all services in one place. In the case of bugs, it becomes a bit difficult to identify the exact problematic area.
  • In Microservice, because the codebase is small, owns just 1 feature, and has its own separate set of logs, it is relatively easy to troubleshoot bugs.

Security and intercommunications :

  • In a monolithic architecture, the fact that all services reside together allows for easy accessibility between them, thus aiding in avoiding the time-consuming process of troubleshooting inaccessible services. These internal calls require no networks and can function seamlessly. Upon closer examination, it becomes evident that the minimal external or exposed calls contribute to the heightened security of the application.
  • In a microservices architecture, the endpoints must be exposed or an API gateway needs to be established to route requests to the correct endpoint. Additionally, each of these calls will result in an increase in network requests, as the services reside in different instances. To maintain security, specific mechanisms need to be employed; otherwise, the exposure of services can potentially be exploited if not handled properly.

Resource sharing and costs :

  • Certainly, in a monolithic architecture, cost-efficiency is notably achieved as all features can share the same resources such as a single cache-memory, database, utilities, and libraries. Handling transactions in a database is also simpler within this setup, contributing to reduced overall costs.
  • In contrast, microservices require separate instances for each resource, leading to an increase in infrastructure costs due to the need for individualized resources for each microservice.

Deployment times:

  • In monolithic architectures, the build and deployment time tends to be high due to the need to deploy the entire application, including a large codebase, as a single unit. However, database migrations are relatively simple since they are done on a single database.
  • On the other hand, microservices are known for being lightweight, simple, and quick to deploy. However, if needed, database migrations must be performed on different microservices, which can make the migration process somewhat tedious.

Service availability and scalability:

  • In contemporary server setups, load balancers are commonly utilized to spin up new instances in response to significant spikes in network traffic. In the case of monolithic architectures, scaling up new instances can be slow and costly due to the large size and resource-intensive nature of monoliths.
  • Microservices can be scaled up and down more rapidly due to their smaller application size. This characteristic makes microservices particularly well-suited for services that cannot afford to experience downtime.

Application size/complexity/scalability:

  • Indeed, monolithic architecture is well-suited for small to medium-sized applications with limited complexity.
  • Microservices architecture is more suitable for large and complex products due to its ability to effectively handle the intricacies and scale of such systems.

Phew!! Finally, that was a not-so-brief competition between the heroes of our show and we got a bit confused on who won. Let's look into some successful microservices project and monolith project.

Successful Microservices Giants:

There are few companies that started with monolith and gradually grew up into microservices-based giants e.g.

Netflix:

In the early 2000s, Netflix operated a DVD rental service using a monolithic architecture. However, with the shift towards streaming as their primary focus and the need for greater flexibility and scalability, Netflix undertook a significant transition. They re-architected their system into multiple microservices, leading to improvements in deployment agility, scalability, and fault tolerance. Each microservice became responsible for specific business domains, enabling independent development, deployment, and scaling. This shift empowered Netflix to seamlessly introduce new features and scale its system, contributing to its success as a prominent global streaming platform.

Amazon:

Amazon, a global e-commerce giant, faced challenges with the complexity and scaling limitations of its monolithic architecture as its business expanded and diversified. To tackle these challenges, Amazon transitioned to a microservice architecture, breaking down its monolithic application into numerous services handling specific functions like product catalogs, recommendations, and user reviews. This shift improved speed, enabled autonomous team operations, and facilitated seamless scaling and rapid introduction of new. These changes have enhanced the customer experience and played a vital role in Amazon's global e-commerce success.

Successful Monoliths:

However, there are few companies that stay rock-solid with Monoliths. E.g.

Stack Overflow:

Stack Overflow's choice to maintain a monolithic architecture can stem from various factors, including its historical development, the specific technical requirements of its platform, and the practicality of their existing architecture. It's important to note that the decision to maintain a monolithic architecture may align with the platform's unique needs, development processes, and team capabilities. The complexity of transitioning from a monolithic architecture to a microservices architecture can also influence this decision.

So, who wins?

  • Users will be happy if they can use the product as soon as possible the product needs to be launched early. Here Monolith wins.
  • Users will be happy if the services are stable as well as available all the time i.e. product needs to be well scalable. Here Microservices win.
  • Because none of the architecture addresses both the needs mentioned above, So, interestingly, we will consider it a TIE.
  • None of the architecture is superior to the other. As discussed earlier, each of them has their pros and cons. In the end, it completely boils down to various factors to consider, while choosing the right architecture. Until now, I have a good feeling that you guys have become familiar with the differences and will be able to decide. Still, let's together carve out a few points to remember for our future use:

- Stage of the project: Choose Monolith for the initial phase of the product. Once it is launched. Start migrating to microservices slowly.

- Speed of development or early launch: Choose monolith when you want to develop fast or launch the product as soon as possible.

- Team's tech stack: Choose monolith if all of the team members are well versed with a common tech stack. Otherwise, microservices.

- Complexity: Choose monolith if the project is small or medium-sized, and is not so complex for the team.

- Infrastructure: Choose microservices if different services of the product have different scalability needs.

My approach to choose the right architecture:

  • Deciding the architecture/tech stack of a project should be independent of the team's tech stack. This is because I believe that the team should be flexible enough to adopt new changes quickly. This also helps them to become versatile in their career and understand complex systems well.
  • If the project is small or medium-sized with linear scalability needs, I would choose monolith.
  • OR, I would start with developing the product on monolith architecture to target early launch. Once the application becomes stable, I would slowly shift into a microservices architecture.

How to break a monolith into microservices?

Now suppose we have an already existing and stable monolith, and we want to transition to microservices architecture. This will need careful planning and execution. We will discuss the steps below:

Identify potential services:

  • The most important and first step is to identify various functionalities that can be encapsulated as separate microservices e.g. we can break an e-commerce product into various services such as authorization-service, profile-service, cart-service, payment-service, orders-service, delivery-tracking-service, product-suggestion service, etc. Try to break it in such a way that each of them can function independently. This decomposition should aim for high cohesion and loose coupling between services. Please note that this step might lead to refactoring/re-writing some areas of the code to make it loosely coupled.

Define service contracts:

  • Clearly define the interfaces and API contracts that will be used for interservice communication. This includes defining APIs, data contracts, and interaction patterns, etc.

Database decoupling :

  • In a monolithic architecture, the application typically shares a single database. When breaking the monolith into microservices, it's crucial to decouple the database layer by either using separate databases for each microservice or implementing a database per service pattern. Personally, I would prefer to keep separate database instances for each of the microservices. But, if cost is a concern, I would create different databases for each microservice on the same instance e.g. user_db, cart_db, orders_db, etc. And each one of them will have their own tables.

Implement inter-service communication:

  • Choose efficient communication mechanisms for inter-service communication, such as RESTful APIs, messaging queues, or RPC. Ensure that services can communicate securely and reliably in a distributed environment.

Address infrastructure concerns:

  • Establish infrastructure for managing microservices, including service discovery, load balancing, fault tolerance, and tracing. Containerization using technologies like Docker and orchestration with Kubernetes can greatly simplify managing services. Also decide the memory/CPU and scalability needs for each of the service. e.g. Payment service needs to be more scalable than profile-service.

Incremental rollout:

  • Gradually roll out microservices into production, starting with less critical parts of the application. Monitor the performance and fine-tune the microservices before moving on to more critical components.

An example of how I did that in my case

Planning:

  • First, we identified and jotted down all the services available in our application, in an excel sheet.
  • Against each service, we had 3 parameters namely size (codebase size of the service), user traffic (how frequently is this service used?), and revenue or stability impacts (if this service goes down, how much does it impact our businesses). Each of them can have a score between 1 to 3.
  • We added scores to each of the above-mentioned columns and added them horizontally into another column named total_score.
  • Based on the scores, we arranged all of these in a sequence. This sequence will decide which microservice we will pick first to develop/build/release. If two services have the same score, we picked the one with a higher revenue impacts. Here is the final sequence as per the table--> Product service, Order, Shipment-tracking, Cart, and then user service.

Development:

  • Pick a service for development. But, before jumping on to create a microservice, we had to make sure that the service was as decoupled as possible from other services, and that its tests were as independent as possible.
  • We did development on the existing monolith to decouple it and deploy it to production. We monitored it for quite some time to ensure that nothing broke and the feature worked as expected.
  • Once we are confident about the decoupling, we extracted it into a mini-project and deployed it to our Amazon ECS.
  • Wherever a Database was needed, we created a new one on the existing instance.
  • We also did set up for logging, monitoring, etc.

Final Step:

  • Until now, our existing apps in the market were using the old monolith for all of their API needs.
  • We planned to route it to our newly deployed microservices. We did set up an API gateway for this.
  • And our frontend apps slowly migrated to using the API gateway.

Next steps

  • Once deployed, its very crucial to monitor how our BRAND NEW EFFORT aka mini-app aka microservice is performing.
  • Actively monitor via logs, network tracings, etc to gain confidence in our work. Tools like Prometheus, Grafana, and Jaeger can help us achieve observability in a microservices environment.
  • Figure out ways to improve and evolve the mini-app.

As we draw the curtains on the timeless clash between monolithic and microservices architectures, it's clear that there's no one-size-fits-all solution.

Each architectural approach brings its own set of strengths and considerations, aligning differently with the unique needs of diverse projects. So, in your quest for an optimized, resilient, and scalable architecture, it's essential to consider the specific requirements and aspirations of your project as well as the enterprise costings/decisions.

So, as you ponder on the architectural future of your project, here are a few action items to consider:

  • Evaluate your project: Reflect on the unique needs, scalability potential, and long-term vision of your project to determine the most suitable architecture.
  • Assess team skills: Consider the skill sets and expertise within your team, and how they align with the architectural demands of your project.
  • Plan the transition: If considering a shift from monolith to microservices, plan meticulously, identify potential services, and progress through a well-orchestrated incremental rollout.
  • Embrace continuous improvement: Once on the microservices path, embrace monitoring, refinement, and leveraging tools for sustained success.

Remember, the architectural future of your project is not just a choice between monoliths and microservices; it's an opportunity to pave the way for unparalleled performance, resilience, and future scalability.

Choose wisely, and embark on a journey towards an architecture that perfectly suits your project's unique needs and aspirations.

HAPPY JOURNEY!!

Heading

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse varius enim in eros elementum tristique. Duis cursus, mi quis viverra ornare, eros dolor interdum nulla, ut commodo diam libero vitae erat. Aenean faucibus nibh et justo cursus id rutrum lorem imperdiet. Nunc ut sem vitae risus tristique posuere.

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse varius enim in eros elementum tristique. Duis cursus, mi quis viverra ornare, eros dolor interdum nulla, ut commodo diam libero vitae erat. Aenean faucibus nibh et justo cursus id rutrum lorem imperdiet. Nunc ut sem vitae risus tristique posuere.

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse varius enim in eros elementum tristique. Duis cursus, mi quis viverra ornare, eros dolor interdum nulla, ut commodo diam libero vitae erat. Aenean faucibus nibh et justo cursus id rutrum lorem imperdiet. Nunc ut sem vitae risus tristique posuere.

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse varius enim in eros elementum tristique. Duis cursus, mi quis viverra ornare, eros dolor interdum nulla, ut commodo diam libero vitae erat. Aenean faucibus nibh et justo cursus id rutrum lorem imperdiet. Nunc ut sem vitae risus tristique posuere.

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse varius enim in eros elementum tristique. Duis cursus, mi quis viverra ornare, eros dolor interdum nulla, ut commodo diam libero vitae erat. Aenean faucibus nibh et justo cursus id rutrum lorem imperdiet. Nunc ut sem vitae risus tristique posuere.

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse varius enim in eros elementum tristique. Duis cursus, mi quis viverra ornare, eros dolor interdum nulla, ut commodo diam libero vitae erat. Aenean faucibus nibh et justo cursus id rutrum lorem imperdiet. Nunc ut sem vitae risus tristique posuere.

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse varius enim in eros elementum tristique. Duis cursus, mi quis viverra ornare, eros dolor interdum nulla, ut commodo diam libero vitae erat. Aenean faucibus nibh et justo cursus id rutrum lorem imperdiet. Nunc ut sem vitae risus tristique posuere.

Menu Bar
linkedin logo