Software architecture is the backbone of your product. It defines its performance characteristics, functional capabilities, and scalability potential. Each decision you make should lead to atomic improvements, not further complexities.
For almost a decade, there’s been a debate about using monolith vs microservices architecture. Each approach has inherent advantages and constraints that we explore in this post.
Monolith Architecture
Monolith applications are designed as a single, self-contained unit (code base, group of functionality, and body of funding). All components are tightly coupled, and changes to one component require rebuilding and redeploying the entire application.
Monolith architecture is a traditional approach to building new software applications. Experienced software engineers can build such an application relatively quickly, without thinking much about the application design, since all business logic is contained within the same unit.
Monolith can be a good choice for deploying a minimal viable product (MVP) or a proof of concept (PoC) due to ease of code management and lower deployment complexities (everything is released at once). This architecture can also be a good choice for internal business applications, with a low load and bounded set of features.
That said, monolith apps have limited scalability potential. Because a monolith app renders the interface, content, and data as a single package, you cannot scale its components individually (e.g., add extra data storage) without redeploying the entire app. For these reasons, many teams eventually broke up the monolith into microservices.
Advantages of a Monolith Architecture
- Straightforward application design. Small monolith apps are easier to develop and deploy because you work with a homogenous “block” of code. Tasks like “source code management”, “change tracking”, and “application monitoring” are relatively simple.
- Predictable performance. Monolith apps have a centralized, homogenous codebase and, often, one application programming interface (API) to perform the same function. Because the architecture is simpler, it’s easier to manage, monitor, and optimize for performance characteristics. Logging, handling, and cashing are also less of a concern.
- Faster debugging. Locating issues within a single-tiered application is easier since all code is stored in one centralized repository. End-to-end testing can be done faster.
Cons of A Monolith Architecture
- Scalability limitations. You cannot scale application sub-modules. Instead, you must scale the entire monolith by adding extra computing resources and a load balancer. Because you’re scaling the entire app, more hardware and software resources are required, leading to a higher total cost of ownership (TCO).
- Single point of failure. Since all internal app components are intertwined, a failure in one module can cause performance issues across the entire app. Any maintenance also concerns the entire app, resulting in planned downtime.
- Longer upgrade cycle. When introducing new product features, you can also add new bugs to the system, meaning any changes in the monolith require end-to-end testing plus redeployment of the updated application version. This process is labor intensive.
- Technology lock-in. Monolith apps are generally programmed in one language and use the same tech stack. Changes to the framework, infrastructure, or coding language affect the entire application, making it time-consuming and expensive.
Microservices Architecture
Microservices is a software architecture pattern of organizing larger applications as independently deployable and manageable sub-services. Each microservice performs a specific function and communicates with other microservices using APIs, allowing for faster and more flexible application development, deployment, and scaling.
The concept was first presented at the 33rd Degree conference in Krakow by James Lewis of ThoughtWorks. Back then, his team was challenged to design a new software product, totaling some 5,000 points in User Stories as per initial requirements analysis.
The system had to be “performant”, “modular”, “maintainable”, and “highly scalable” with a capability to support large data sets of billions of transactions — and it had to be PCI-DSS compliant.
The team could only deliver anything of this scale within the set timeframe by scaling their programming efforts. As you might have guessed, their approach was to create loosely coupled software modules rather than a monolith platform. At about the same time, Adrian Cockcroft of Netflix came up with a similar idea of “fine-grained service-oriented architecture (SOA)” as he was spearheading Netflix’s efforts to move to the cloud.
“This is the definition I had then of microservices, loosely coupled, service-oriented architecture with bounded context. If it isn't loosely coupled, then you can't independently deploy. If you don't have a bounded context, then you have to know too much about what's around you.
Part of the productivity here is I only need to understand this service and what it does, and I can successfully contribute code to the entire system. If you're in a monolith, you need to understand pretty much the whole monolith to safely do anything in that monolith.
Microservice architecture patterns rapidly spread across the development community and soon became the standard for building cloud-native applications. At present, 85% of companies are upgrading their apps to a microservices architecture.
By design, monolith applications are hard to scale as you need to rebuild and redeploy the entire product, a cost- and labor-intensive task. When one service (e.g., payment processor) needs more resources, you must provision extra virtual machines to the entire system.
In contrast, a microservice includes a set of self-contained processes that can run independently. Such bounded design allows you to scale each microservice separately by provisioning extra resources or making changes that don’t impact other application modules downstream.
Microservices also help teams build new product features faster by operating in parallel. Because microservices are loosely coupled, you can progressively iterate on different modules and release updates faster without hindering application performance or the efforts of other development teams.
Airbnb was initially designed as a monolith Ruby on Rails application. However, as the product grew bigger and more software engineers joined the company, the architecture pattern became unsustainable. To increase time-to-market, the engineering team had to deploy 200 commits to the monolith per day, but they were blocked for 15 hours per week on average due to reverts and rollbacks. Frustrated, the Airbnb team shifted to service-oriented architecture and microservices. Although the project took almost four years, it was well worth it. Page load times became 10X faster, and the team scaled up to 125,000 production deployments per year. Since 2019, over half of Airbnb’s services have been running on 7,000 nodes across 36 Kubernetes clusters (which includes 250 critical services).
Key Characteristics of Microservices
- Compartmentalization. Every microservice is an independently replaceable, scalable, or upgradable unit of software, performing a bounded set of functions.
- Domain-driven design (DDD). Microservices are organized by business capabilities rather than technical characteristics. The approach is based on Conway’s law, saying that every organizational system will reflect its communication structure. Microservices require a broad-stack software implementation, and cross-functional teams with a range of skills, including user experience, database, and project management, are needed for their development.
- Fault-tolerance. A microservice should have a high failure tolerance, both individually and in combination. A failure in one microservice must not cause a cascade effect over the entire system. With such a design, microservices can be upgraded independently, while the entire system becomes more resilient thanks to in-built redundancies, backup services, and failover policies.
- Containerization. The containerization process packages an application's code with all the files and libraries it requires to run anywhere. Effectively, containers allow microservices to share operating system and middleware components, which allows hosting multiple microservices on one server. When combined with CI/CD, containerization also increases app deployment times and frequency.
- Decentralized governance. In a microservices architecture, data management is decentralized between services. Each service can rely on independent conceptual models and data storage layers, plus exchange or consumer data from other services via RESTful APIs.
- Independently deployable. Each microservice can be deployed independently without affecting any connected microservices or the entire application.
Advantages of Microservices
- Diverse tech stack. By design, microservices can be written in different programming languages or feature different tech components. Effectively, you can combine multiple stacks without worrying about interoperability issues. The Backend for Frontend (BFF) pattern also lets you connect multiple custom front-end technologies with any application on the backend.
- High scalability and resilience. Microservices architecture includes redundancies. If one microservice fails, the request can be re-routed to a backup service. As individual microservices can be deployed and updated independently, there’s also lower downtime and higher scalability potential. The current state of cloud computing allows supporting a virtually limitless number of multi-tenant and stateless microservices.
- Faster, more frequent deployment. Transition to microservices goes hand in hand with DevOps adoption. Containerized deployments, combined with robust CI/CD pipelines, increase developers’ productivity and the frequency of new code commits (aka new feature releases). Mature DevOps teams can deploy new code changes multiple times per day with a 0 to 15% max change failure rate.
- Improved software quality. Instead of doing quality assurance (QA) for an entire app, your teams can frequently test independent services (and automate a large fraction of QA activities) during the build process. Fewer bugs creep into the production stage, resulting in greater product quality and stability.
- Greater security. Microservices are also easier to audit for security flaws. Depending on the criticality, different microservices can contain different security mechanisms. Instead of implementing a blanket data encryption policy, for example, which requires more development resources (and costs), you can only implement advanced encryption standards (AES) for services, processing critical data. Moreover, if some malware or an intruder penetrates your security perimeter, you can easily contain the threat to a single microservice.
Disadvantages of Microservices
- Extra operational complexity. Since there are more software units, your teams will need to oversee more moving parts — configurations, resource usage, governance, API management, etc. All of these require mature processes and a good degree of automation — the principles DevOps promotes.
- Performance management. Microservices can communicate via synchronous or asynchronous protocols. When most communications are powered by REST, there’s a higher dependency between individual microservices, making the system behave more like a monolith. Asynchronous communication is harder to implement and requires performance orchestration to minimize latency. But it reduces service interdependency and better isolates failures.
- Higher infrastructure costs. Each microservices has its own “bill” of infrastructure, monitoring, and testing costs. Without proper control, your cloud computing bill can spiral out of control.
- Standardization sprawl. Just because microservices allow you to combine multiple technologies, it doesn’t mean you should. Without some shared standards across teams, interoperability and performance issues may emerge when it comes to programming languages, logging standards, and monitoring.
Monolith vs Microservices: When To Choose Either Approach
Microservices have been touted as the “future-proof” architecture for almost a decade, with companies like Amazon, Google, Spotify, Netflix, and many others being among the adopters.
That said, monolith architecture still holds its merit. Some of the companies have recently migrated from microservices to monolith. Amazon Prime, for example, re-architected its audio/video monitoring service as a monolith and reduced operating costs by 90%, while also gaining increases in scalability and service resilience. InVision, Istio, and Segment are several others also reverted to monolith architecture. High operating costs, architecture complexity, and head-of-line blocking causing performance issues were cited as top reasons for going back to the monolith.
In other words, no software architecture is inherently better than the other. Each comes with its tradeoffs, which may not be apparent at the early stages of product development. For some products, monolith architecture does have an advantage over microservices because it’s easier to code, deploy, test, and maintain. In other cases, it may be too restrictive in terms of scalability potential or tech stack selection.
Itiel Maayan, Chief Architect at NetCom (core systems) division of AT&T, believes that the optimal software architecture choice largely depends on the type of project. Developers today have plenty of “middle ground” options like Modular monolith, where a bigger system gets split into multiple modules organized by capabilities, and Miniservices — separate, loosely-coupled application components built around the business domain rather than business domain features.
According to Maayan, it makes sense to use a modular monolith architecture (instead of immediately upgrading to microservices), when migrating legacy applications to the cloud. In this case, you can lower the project risk and costs, while addressing the underlying problems such as tech debt, security risks, or new business requirements. When building new products or re-architecting a legacy system, using a combination of microservices and miniservices can be a better solution.
To decide whether monolith or microservices architecture is a better fit for your project, consider the team size, structure, skillset, and operational capabilities. Microservices architecture requires several adoption prerequisites, including service mach, container orchestration systems, more advanced monitoring, and a strong DevOps culture. Monolith apps, in turn, may be simple to build and run at the early stage of the product life cycle, but they can later become a significant operational nuisance.
Monolith vs. Microservices Decision Matrix
Factors to Consider | Monolith | Microservices |
Business goal(s) | Re-host legacy system in the cloud to obtain extra resources Quickly launch an MVP to validate the demand Build a small-scale product for internal use | Introduce new features or technologies to an existing app Increase release speed and predictability Minimize planned maintenance downtime |
System design | Single, tightly coupled codebase with multiple interdependent functions. | Domain-driven, loosely coupled, independent software services, that communicate via APIs. |
Team skill set | Requires functional engineering team:
| Require cross-functional teams:
|
Infrastructure | Single on-premises or cloud server can suffice. | Any combination of distributed cloud architecture is possible, with serverless deployments. You require more effort to set up the tools and workflows for microservices. |
Scalability | If demand increases only in certain functional areas, you must scale the entire application. | Scale individual microservices, based on the application usage patterns. |
Maintainability | A single codebase makes debugging faster, but updates require re-deployment of the entire app. The release process is longer and more resource-intensive | Thanks to CI/CD, fewer bugs make it to the production stage. However, microservices require more monitoring. Remote calls can be slow and are always at risk of failure. |
Conclusions
Software architecture choices depend a lot on your business context. Sometimes going with a variation on a monolith application makes sense. Sometimes microservices are preferable as you’ve learned from this post.
Svitla Systems Cloud Engineering team would be delighted to analyze your case and recommend the optimal strategy. Our team has substantial experience in both modernizing legacy systems as microservices and developing scalable cloud-native applications.
Get in touch with us for a personalized consultation.