What Are Microservices?
The microservice architecture is a software architectural design that encourages developers to break down large applications into smaller, independent units. These units are loosely coupled, meaning that each unit communicates only with those parts of the system that need to interact with it. This makes it easier to change individual components without affecting others. It also allows teams to develop independently, which helps keep projects on track in larger more complicated projects.
In many cases, microservices allow for a simpler way of developing applications because development can be isolated to a single code base and problem domain. They are small services that can be deployed independently and tested separately.
This makes it easier to test each service individually before deploying with the larger application. Often, microservices make it easier to test and debug applications because each service is independent of others, although this is not always the case. When that is the case, it makes it easy to isolate problems within individual services without having to worry about how changes will affect the entire application.
At the same time, as I will show later in this article, there are many things that can be more difficult about the development process. Instead of being fewer, there are sometimes increased dependencies. There can be more difficult error handling, complexity due to interprocess communication and other issues to consider.
The alternative to the microservice is the monolithic application. A monolithic application is built from one code base and deployed as a single application. Often, developing a traditional monolithic application can be easier, but when? In this article, I’ll examine why to use microservices and when to use or not use.
Why Use Microservices?
From scalability to maintenance, here are 5 reasons why microservices should be considered for your next project:
Efficient scaling of individual components
Quicker and more frequent releases to production
Improved application stability
Easier, more focused, problem domain
Flexibility in the use of technology
Scaling
Harnessing the power of a cloud-based platform is an excellent way to expedite deployment and management for your microservices. After all, these platforms enable companies to rapidly scale their computing resources in reaction to wildly varying user demands–the perfect complement for microservice architectures.
You can opt to not use the cloud whatsoever, and microservices don’t demand it either. Nevertheless, when you combine cloud computing with microservices, you get access to superior agility and flexibility since you no longer have to build out any infrastructure. This aids in minimizing costs while hastening your ability to respond promptly to evolving needs at the same time.
Through automated processes, cloud resources can expand in real-time, rapidly and in a cost-effective manner. To increase performance when demand grows, workload is distributed across multiple servers. This ensures that your system can handle large influxes of requests without faltering. Over-provisioning of resources can be avoided by scaling resources up and down, helping you to save money.
By utilizing microservices, your can tailor the scaling of an application to your specific needs. With this approach, companies have the option to scale just those parts of the system that require increased resources to handle the load. Any element that is receiving heightened loads can be quickly scaled up to meet the demand, without requiring you to over-scale other parts of the application that are less active. Again this can help to reduce costs.
Easier Deployments
Microservices make it easier to release software updates more frequently. Because microservices are independent of one another, updates can be released for individual services without having to be dependent upon another area of the application. This helps reduce the risk of introducing new bugs into the other areas of the application. Software bugs can be more isolated into a specific area and therefore also more easily located.
Depending upon your application, it may also be possible to perform a rolling update. This means that microservices can be deployed gradually, rather than all at once. This allows for a more controlled release of an application update and reduces the risk of downtime or all of your users experiencing an application bug.
In performing a rolling update, you might pick a cohort to update and then monitor how the application is running with each cohort before rolling out the update to the entire application. This is how the risk of bugs is reduced, as only a certain percentage of users are being affected at any one time. You would likely be part of the cohort and ensure proper operation, via testing, before completing the update.
Improved Application Stability
Microservices are designed to cope with failure by allowing other services that are functioning correctly to take over. This means that if one microservice fails, the whole application does not break. This is important for SaaS applications as they are expected to stay up, and perform optimally, at all times.
By using microservices and cloud-based platforms, companies can deploy microservices across multiple servers to provide redundancy. Companies can achieve a consistent level of performance even if there has been an unexpected failure, by routing requests through other functioning microservices.
Focused Software Development
Using microservices will allow developers to remain focused on a single problem domain. Developers can concentrate on completing specific microservices without having to worry about the other areas of the application, enabling them to work more efficiently. An example of this might be an application for a delivery company that computes the shortest delivery path for delivering products. This might be its own microservice, whereas the part of the application that takes orders would be separate.
Each microservice is isolated so that changes within one microservice will not affect any of the other microservices. This makes it easier to onboard developers and testers, as they don’t need to be familiar with the entire codebase.
Technology Flexibility
Microservices can be developed in different languages and use different frameworks, allowing developers to pick the best technology stack for each microservice. This gives companies the flexibility to choose which technology best meets their needs for a given problem domain, as well as making it easier to hire developers with a particular skill set.
In the same way that companies can leverage microservices to migrate legacy applications to the cloud, a SaaS product owner can decide to break down a larger component into smaller ones, or decide to switch from a monolithic MVP to using microservices in the scalable production version. When making this change, a company may decide to switch languages for a particular problem domain.
For example, an application might choose to have microservices written in Python for a machine learning component, while the web application REST services might be written in Java or Kotlin.
Microservices also have flexibility in deployment. They are ideal for cloud computing architectures that favor the use of containers, like Docker and Kubernetes. You can easily make use of serverless computing and deploy on GCP using Google Cloud functions or on AWS using Lambda or Fargate, or other similar service.
Examples of Using Microservices
A Delivery Company
One example of using a microservice is what I mentioned previously, a delivery company. A delivery company’s business application would likely be quite a bit more complicated than I am depicting, but imagine the two key components being the order entry and billing system, and the system that determines the routes that the delivery people will take when delivering packages.
These two components could be built as microservices and deployed separately. In such an architecture, you would likely also have the web application itself as a separate application that orchestrates between the two microservices and is likely not a microservice itself.
In our contrived and simple example, that would be overkill. Instead, we would want to have the main application and just a single microservice that would perform our routing duties.
Making changes to the routing microservice would be isolated from the rest of the application. You can use common web application components for building the main application. For the routing application microservice you can choose the language and frameworks that work best for building a robust routing engine.
A Social Media Platform
Another example to consider is developing a microservice for a social media platform. Here, you might have a microservice that is excellent in computing suggested connections based upon your existing network. This microservice would need to be optimized for graph traversal. The microservice would likely work with a graph database and the main application would be working with a NoSQL document database or relational database.
There would certainly be a need for the main application to also make use of the graph database, to get the connections a user has. Rather than focus on all the relevant technical details, the main point to grasp is that the application could be separated based on responsibilities.
An Online Education Platform
Another example of microservices would be an online education platform. In this type of platform, microservices can be used to provide personalized recommendations based on the student’s progress, likes and dislikes. This could be a separate recommendation engine that functions on its own, not being part of the main application.
Example Wrap-up
In these three examples, can you can see how microservices can be applied differently? From a delivery business, a social media company or an online education platform and more, microservices provide a number of potential benefits. By breaking down the application into microservices you can create more manageable and scalable solutions. The flexibility in microservice architectures also allows for each microservice component to be written with languages and frameworks that best serve its purpose.
What Not to Like About Microservices
Complexity
Smaller applications or smaller development teams may find microservices add unnecessary complexity and find that their use actually slows down the development process. As the number of microservices increases, so does the potential for barriers to accessing information easily. Complexity for developers increases with the number of microservices. Application designers need to account for things like fault tolerance, CPU and network latency, discoverability of services, and potentially different message formats, to mention only a few of many considerations.
Testing Complexity
I stated earlier that testing can be easier with microservices, but sometimes it is exactly the opposite. One reason for this is because an application using microservices typically needs more tests than if it were just one larger application. Each microservice must be tested for at least the non-functional requirements mentioned above, such as its ability to handle things like faults or latency.
If your application has a number of microservices, then using a framework that addresses the non-functional items will help minimize the testing burden. In that case, the framework can be tested separately and used for each microservice. When done in this way, the testing burden is likely reduced to largely only the application functional testing. Still, testing complexity can be an issue, especially in the case of handling distributed transactions, as I’ll discuss next.
Transaction Isolation
Another consideration when using microservices is that of transaction isolation. Transactions spanning multiple microservices can be particularly difficult since each microservice is typically unaware of the other microservice.
Consequently, microservices involved in a given transaction must be able to participate in a distributed transaction. With developing microservices that participate in distributed transactions, you may be required to coordinate development between different development teams, depending upon how your teams are structured.
Configuration Management
Particularly when deploying microservices in a rolling deployment type of approach, maintaining proper communications between the main application and a microservice, between potentially different versions, with potentially different communication schemas, can create additional complexity and potential for downtime due to errors. It might require the protocol for communicating to include some kind of versioning scheme so microservices can be aware of exactly which version they are running.
Any time you are calling a distributed service, you need to consider versions, so this isn’t necessarily only an isolated issue with microservices, but microservices by nature, require being called and thereby require version management of some kind. This therefore increases the complexity over having just a single monolithic application.
Another approach would be to make your microservices always backward compatible, which might allow you to drop the need for API versioning. At first, this might sound like a great idea, but it necessitates regular regression testing each time there is a change. This can be costly and reduce your ability to be agile.
Of course, you can improve on this by deprecating and eventually obsoleting certain generations of an API. This might eliminate strict versioning of the interfaces, but would require tracking API generations through some other means, such as in source code or through some form of documentation or cooperation between development groups, if in a larger team.
Networking Overhead
Microservices introduce networking overhead. Since microservices require being called over the network layer, that naturally introduces a latency and resource utilization that is not necessary in a monolithic application. The more microservices you have to complete a single transaction, the longer that transaction will take to complete. This is due to the networking overhead.
When Not to Use Microservices
It would be a mistake to think microservices are always the right solution for any application. The benefits microservices bring over monolithic architectures may be too small in comparison to the complexity microservices introduce. The type of project and the development team’s skills, size, and budget for development may all be factors in deciding whether to use microservices.
Microservices are usually not the best option for applications or products that have a small development team. In addition, projects with shorter timelines may find that microservices are at odds with the goal of completing quickly. When you take into account all the additional non-functional requirements of a microservice - additional design and testing - it will generally take longer to develop a microservice.
A significant exception to this general rule would be that microservices are necessary in applications that have a particularly complex aspect that is best handled separately, such as requiring a different language from the core application, or a necessity to scale separately from the rest of the application.
A great example of that was a product Software Prophets built. It required both of those aspects. The forecasting application we built required machine learning and many worker nodes to operate concurrently during a forecasting execution. We needed a different language and set of frameworks to perform the machine learning, and we needed the ability to scale independent of the main application. Although we may have lacked the other guidelines for when to use microservices, these two driving characteristics necessitated that a microservice indeed be used. The benefits far outweighed the costs.
Summarizing when not to use microservices, note that they can be a powerful architectural element for building scalable applications. They are especially useful when there is a need to scale or use different technologies. However, microservices bring with them increased complexity, additional development cost and overhead, which needs to be factored in when considering microservices.
If your project does not have the development team size, skills, budget for microservices or particular technical imperative, then it may be best to stick with a more traditional development approach. If there are significant intervening factors that favor the use of microservices, then the downsides may be outweighed in favor of using microservices. In many cases, even when using microservices, a hybrid approach may well be at play.
Whatever you decide, understanding the pros and cons of each approach, and having a seasoned software architect who can advise, is very important. Taking the wrong path can quickly burn down your investment dollars and bring about project failure. Although there’s plenty to like about microservices, using them because they seem sexy is never the right answer. Ultimately, it may create accidental complexity rather than aid in creating an agile platform for business growth.
If you need help choosing what’s best for your situation, please feel free in reaching out.