In this blog, I explore the decision-making process behind the choice between the three main architectural patterns used by contemporary software applications:
We’ll look at the strengths and weaknesses of each approach to software development and the strategic priorities each best serves or could compromise.
Simplified, the accepted wisdom is:
But there are nuances to the above which we’ll dig into to give you a fuller picture of the considerations that should inform your decision between a monolith, microservices or serverless architectural pattern.
Can We Help You With Your Next App Development Project?
Flexible models to fit your needs! Quote in 24 hours
At the outset of any software development process, two core technical decisions have to be taken:
The choice of architectural pattern the tech stack will be applied to will, however, influence outcomes including:
We’ll take a closer look at how the three core architectural patterns of monolith, microservices and Serverless affect those outcomes to help you decide which best meets the strategic priorities of a particular application project.
But let’s first take a brief step back and define what software architecture is and why it is important.
A clear and concise definition of software architecture is:
“Software architecture defines how the components of a software system should be organized/assembled, how they communicate with each other, and what are the constraints the whole system is ruled by.”
From that definition, we can break software architecture down into three main components:
The granularity of the components that make up a software system is dictated by the architectural pattern. A monolithic architectural pattern, for example, means all functionalities run as a single entity, while a microservices architecture consists of multiple small components.
When it comes to the size of components, Serverless and microservices architectures are very similar. The difference is more about how and when components are activated and it’s possible to combine the architectures and create certain functions, usually those only occasionally requested (eg. an authentication service), as Serverless.
System interaction refers to the interfaces through which the components, or services, that make up an application communicate with each other eg. REST APIs. Non-Serverless microservices and Serverless diverge here due to the differences between how components are run and connected to the underlying infrastructure. In a microservices architecture, the software development team provisions the (usually cloud) infrastructure the components run on. In the case of Serverless, that is handled by the Serverless platform eg. AWS Lambda. In short, there are differences between how microservices and Serverless functions are deployed and managed.
The third component of an architectural approach is its quality attributes, examples of which include scalability, resolvability, development speed, technical skills required of the software development team, adaptability and resilience, etc.
Monolithic architecture, for example, offers qualities like faster, cheaper development. Scalability and resilience are among the most prominent qualities of a microservices approach. Potentially lower cloud computing costs and minimising the need for infrastructure specialists are qualities associated with Serverless, while high availability and convenience for iterative development are often held up as strengths of microservices.
Let’s take a closer look at the respective strengths, weaknesses and use cases of the monolithic vs microservices vs Serverless approaches to application architecture.
Monolithic architecture may not be trendy but the traditional approach to building applications by wrapping all functionalities into a single entity that combines front and back end is far from obsolete. There are still many use cases for monolithic apps.
This WordPress website, for example, is an example of monolithic architecture. We are a software and web development company specialised in cloud architecture and have built multiple enterprise applications using both microservices and Serverless. We have the inhouse resources to build our own B2B web application to a more sophisticated architectural pattern but chose not to.
Why? There would be no point in adding the layers of complexity and resources to build this site to a microservices or Serverless architecture pattern. This application serves content, like the blog you are reading now, and presents our services, case studies, job openings and tells you more about us as either an employer or service provider. It needs to be fast, have good availability and serves static content that is freely available to whoever wants to consume it.
That’s it. Which makes a monolithic architecture the natural choice. Anything else would be overcomplicating things. Apps based on monolithic architecture are typically faster, easier and cheaper to build as they don’t involve synchronising and connecting multiple moving parts.
Faster, easier and cheaper to build – in most engineering contexts, the best solution is the simplest way to reliably achieve the required result. Software engineering is no different and monolithic apps are faster, easier and cheaper to build than those based on more sophisticated architectures. So if a monolithic app will do what is required of it in a way that doesn’t compromise its chances of success, you should probably use a monolithic architecture.
Convenient testing and production processes – it is much easier to test a monolithic app than a microservices or Serverless app. You can start and test an application on the developer’s computer or staging environment and apply a standard deployment process to check on changes before putting it into production.
Simple infrastructure – monolithic apps use a single server for their front end, back end, and database, which simplifies infrastructure requirements. A content delivery network like Cloudflare can be added to improve speed, scalability, availability and security of monolithic web applications.
You only need to maintain one repository and search and find all functionality in one folder. You will also only have to develop and maintain a single test and deployment pipeline, which can significantly reduce costs as multiple pipelines can be expensive to create, customize, maintain in a way that ensures consistency across them all. All data used by the application can also be stored and retrieved from a single database.
Monitoring – a simple infrastructure brings the additional advantage of easier monitoring.
Straightforward transaction controls and data integrity – the fact that monolithic applications usually use a single database for storage means they benefit from straightforward transaction controls and different mechanisms and data integrity provided by a relational database (RDBMS).
Large codebase – monolithic architecture means a single codebase. If the codebase becomes large, it can be difficult for developers to read and understand which makes it more difficult to integrate new developers into a project.
Updates can be a challenge – updating a monolithic application means rebuilding and redeploying the entire codebase.
High level of dependencies – a single codebase means components are tightly coupled and changes to the logic of one module or service run a much higher risk of affecting the code and working of other modules. It’s hard to predict the implications across the app of even a small change in one place. Updates involve re-testing the entire codebase.
Lack of flexibility – a monolithic architecture both limits the range of technologies you can leverage and means the tech stack has to be consistent across the entire application. You don’t have the luxury of using a different, perhaps better suited, technology for just one or a few services.
Scalability – if you build a monolithic app that unexpectedly becomes hugely popular and needs to be able to handle much bigger and unpredictable loads, as well as be iterated upon and expanded, you’ll probably have to migrate it to either a microservices or Serverless architecture. Monolithic applications don’t scale well.
Monolithic architecture is usually best suited to smaller, simpler applications such as those that serve static content. A B2B website like this one is the perfect example. Another would be the quick, cost efficient development of an MVP, or software designed to complete a simple task.
As the digital economy and technology has grown and evolved, applications have grown in size and complexity. As already covered, a monolithic architecture can be a problem for apps as they scale as the codebase becomes unwieldy and difficult for new, or even existing, team members to efficiently read and understand.
And updates and iterations that release new functions and features become increasingly complex due to dependencies between services and the need to rebuild, retest and redeploy the entire codebase.
A microservices architecture splits applications up into groups of services. An eCommerce app, for example, could be broken down into the following microservices:
Microservices architecture is far from new as a concept. The microkernel approach of the 80s and 90s and service-oriented architecture (SOA) that became common in the 2000s also broke applications down into components. However, it was the rise of cloud-based environments and need to run applications in a distributed way across multiple servers that properly established the modern microservices approach.
As software systems became more complex, it was also increasingly important to spread risk by decoupling services in a way that means one microservice failing doesn’t take down the whole application.
Agile & DevOps teams and consultants
Supercharge your next cloud development project!
Iterative development – microservices-based software architecture is a good fit for modern agile software development processes based on iterative development. Breaking an already operational application down into loosely-coupled services means new features and functions can be more conveniently worked on and added without the risk of interrupting availability. It also allows different teams to concurrently work on different services and makes it easier to introduce new team members.
Flexibility – microservices introduces greater flexibility to the software development process on several levels. As already mentioned, one is that the architectural approach means different teams can develop different services relatively independently of each other. Another is that the smaller codebases are easier to read and understand, making it easier to introduce new team members.
Tech stack freedom – another big plus of microservices is that the architectural approach offers significant freedom in tech stack choices. In a monolithic architecture where services are tightly coupled, it is important to maintain consistency in the technologies used across an application. They should also be highly compatible with each other, which not all languages and software development tools are.
A microservices architecture’s loosely coupled services allow for much greater freedom in tech stack choices. The development team can, in theory, create individual services with very different tech stacks.
Practically, it will usually still make sense to keep the stack relatively consistent because it won’t be cost-efficient for a company to cover several different tech stack used in one application. But it does mean that if a particular service would significantly benefit from a technology not used across the app, it can be used without clashing with the main tech stack in a way it might in a monolithic architecture.
Availability – one of the biggest advantages of a microservices architecture is that loosely coupled services lead to a more robust application and higher availability. If one service fails, loose coupling should mean it doesn’t affect the rest of the application, which will continue to function for users.
Scalability – a further quality of a microservices architecture’s loosely coupled services is that the approach is well-suited to scaling apps. Its modular nature makes it easy to work on and then add new features and functionalities to a live application. While the upfront cost of investing in a microservices architecture is higher, it can be cheaper to scale than a monolith app due to a combination of the strengths already mentioned.
Improved performance – being able to split services and loads across multiple servers can enhance performance.
Complexity – a distributed system like microservices inevitably introduces additional complexity as multiple moving parts need to be synchronised in a way that allows them to work as a unified software system. If services are split across servers you will have to provision that multi-faceted infrastructure.
Testing complexity – while smaller codebases mean it is much easier to test individual microservices, having to test each service individually can add significant complexity as an application scales. The need to test and maintain communication between services adds a further layer of complexity.
Higher upfront costs – while trying to significantly scale a monolithic application can be a nightmare that quickly sees both direct development and opportunity costs mount up, a microservices architecture does involve higher upfront costs.
Each microservice requires its own set of developers (though it’s probably a single team will be responsible for multiple microservices), as well as a tailored source code management system, testing process, and scripts facilitating automated deployment. That all adds up to higher upfront costs.
DevOps requirements – a distributed system like microservices requires skilled orchestration, usually involving Kubernetes and other DevOps tools and processes. That means you will most probably need to employ or contract at least one DevOps engineer. That will add to costs and with demand soaring, DevOps specialists can be hard to recruit.
Opting for a microservices architecture instead of traditional monolithic is, boiled down, a trade off. Building a microservices-based application adds a premium to the upfront cost but in return offers greater operational independence and flexibility, which speeds up release cycles.
And for many modern apps, and organisations, the advantages of a microservices approach have become more necessity than luxury.
Microservices architecture is usually best suited to larger, more complex applications built for scalability and agile iteration, especially if cloud-hosted or cloud-native. You can read more about the decision making process between monolith and microservices in this case study on how and why we migrated the enterprise, multi-tenanted CMS of the world’s largest events company.
Serverless is closely related to microservices and the choice between the two is far from a binary one. In fact, many modern applications combine serverless and microservices. Like microservices, the concepts that underpin serverless have been around for a long time but have become a fixture in mainstream software development since the rise of cloud computing.
More precisely, serverless has taken off since the 2014 launch of AWS’s Lambda serverless framework. All the major cloud providers now offer serverless platforms and third-party frameworks like OpenFaaS and Kubeless mean serverless can be extended into Iaas and on-premise infrastructure.
In one sentence, the difference between microservices and serverless is that the former is a way to design an application and the latter a way to run all or part of an application. That’s the key to their compatibility. It’s possible to code a microservice and run it as a serverless function.
But let’s take a step back. Serverless architecture consists of a set of functions rather than services. The difference is that a service is permanently available whereas a function has a lifecycle – it is triggered, called, executed, runs and is then ‘killed’ as soon as it has done its job. Serverless functions only run when they are needed, potentially saving significant resources.
The other key difference between microservices and serverless is that in the case of the latter, infrastructure provisioning is taken care of by the serverless platform provider. The development team don’t have to configure the entire software system. Functions are uploaded to the serverless platform, the triggers that will launch them configured, and the provider takes care of the rest.
That means the development team only needs to focus on coding and doesn’t have to worry about infrastructure. Any remaining DevOps tasks can usually be distributed between the development team without the need for dedicated DevOps engineers.
The main difference between serverless and microservices is that microservices would normally be used for parts of an application that need to run constantly, like the frontend of a user application. Serverless works best for functions that are used occasionally and for a limited time. For example, a user uploading an image to an application would be the ideal scenario for a serverless function. It’s only done occasionally but is resource-intensive.
Serverless functions hosted on a serverless platform can also be used across multiple applications. For example, if an organisation had several applications which all featured user profiles for which profile pictures are uploaded, the same serverless function could be used across them all.
Serverless architecture is, however, less flexible than microservices. Serverless providers only natively support certain programming languages, frameworks and tools and while there are workarounds, they can be complicated and problematic. And every different serverless platform supports different technologies, meaning it’s not possible to run the same function with different providers without reconfiguring it to some extent.
Serverless tooling is also generally considered less mature than it is for microservices. For example, serverless monitoring and log configuration tools are limited.
While entire applications are built of serverless functions in a pure serverless architecture, in the majority of cases it makes most sense to run certain services as serverless – those that are not in permanent use and have a relatively short run-time, especially if that run-time is resource intense.
The future of serverless architecture is most probably as a technology that complements, rather than replaces, microservices.
Flexibility – breaking applications down into serverless functions offers similar advantages to flexibility as microservices does.
Cloud computing costs – calling and killing functions so they only run for as long as required can potentially lead to significant savings in cloud computing overheads compared to them being permanently available.
No need to provision infrastructure – the serverless provider taking care of provisioning the cloud infrastructure serverless functions run on means the software development team have one less thing to worry about and can focus on coding the application itself.
Scalability – as with microservices, the modular nature of serverless makes it easier for software development teams to scale applications as new functions can be worked on independently and then added incrementally to the live app.
Reliability and availability – again, as with microservices, an app being split into loosely coupled functions means there is a far lower level of dependence across functions than would be the case in a monolithic architecture. So if one function fails, the app should keep working otherwise. The provider taking care of underlying infrastructure also further reduces potential points of failure.
Complexity – on the one hand, the serverless provider taking care of the cloud infrastructure reduces the complexity of serverless architecture. However, being broken down into independent functions means it still consists of multiple, smaller moving parts making it more complex than a monolithic approach.
Tech stack limitations – serverless platforms only support certain languages, frameworks and tools, which means you are limited to coding functions using those supported by your provider.
Vendor lock-in – applications that are entirely serverless or include serverless functions are relatively tightly tied to the provider platform they were built for because no two are exactly the same and interchangeable. It’s far from impossible to reconfigure serverless functions for a different provider’s platform but doing so for a large, complex application involving numerous functions would be a significant undertaking.
Immature tooling – compared to traditional monolithic and microservices, it is often argued that the tooling available to support serverless development and maintenance is immature. A lack of good monitoring and log configuration support is regularly highlighted. However, with serverless architecture quickly gaining traction, tooling will inevitable mature over time.
It makes most sense to present serverless use cases at the function level rather than mentioning specific categories of applications. Services particularly suited to being run as serverless functions include:
As is almost always the case when comparing and contrasting alternative technologies and approaches to software development, especially when they are all well established and popular, there is no single right answer monolithic vs microservices vs serverless.
All three approaches bring their strengths and weaknesses and choosing one or the other is always a trade-off. And as we’ve seen, microservices and serverless are compatible with each other. It would even be possible to run an entire monolithic application as one big serverless function on a serverless platform, though it’s hard to think of a particular use case for that approach.
Traditional monolithic architecture will continue to be the most efficient solution for relatively simple applications like websites serving static content. And a microservices, or a combination of microservices and serverless for larger, more complex cloud-hosted apps for which scalability, flexibility and availability are priorities.
K&C - Creating Beautiful Technology Solutions For 20+ Years . Can We Be Your Competitive Edge?
Drop us a line to discuss your needs or next project