Microservices are an architectural approach to building applications. As an architectural framework, microservices are distributed and loosely coupled, so one team’s changes won’t break the entire app. The benefit to using microservices is that development teams are able to rapidly build new components of apps to meet changing business needs
What sets a microservices architecture apart from more traditional, monolithic approaches is how it breaks an app down into its core functions. Each function is called a service, and can be built and deployed independently, meaning individual services can function (and fail) without negatively affecting the others. This helps you to embrace the technology side of DevOps and make constant iteration and delivery (CI/CD) more seamless and achievable.
Think of your last visit to an online retailer. You might have used the site’s search bar to browse products. That search represents a service. Maybe you also saw recommendations for related products—recommendations pulled from a database of shopper preferences. That’s also a service. Did you add an item to an online cart? You guessed it, another service.
So, a microservice is a core function of an application and it runs independent of other services, but a microservices architecture is about more than just the loose coupling of an app’s core functions—it’s about restructuring development teams and interservice communication in a way that prepares for inevitable failures, future scalability, and new feature integration.
How is this achieved? By adapting the basics of a service-oriented architecture (SOA) to deploy microservices.
If breaking down apps into their core functions and avoiding the pitfalls of monoliths sounds familiar, that’s because the microservices architectural style is similar to the service-oriented architecture (SOA), an already well-established style of software design.
In the early days of app development, even minimal changes to an existing app required a wholesale version update with its own quality assurance (QA) cycle, potentially slowing down many sub-teams. This approach is often referred to as "monolithic" because the source code for the entire app was built into a single deployment unit (like .war or .ear). If updates to part of an app caused errors, the whole thing had to be taken offline, scaled back, and fixed. While this approach is still viable for small applications, growing enterprises can’t afford downtime.
Enter the service-oriented architecture, which structures apps into discrete, reusable services that communicate through an enterprise service bus (ESB). In this architecture, individual services, each organized around a specific business process, adhere to a communication protocol (like SOAP, ActiveMQ, or Apache Thrift) to share themselves through the ESB. Taken together, this suite of services, integrated through an ESB, comprises an application.
On the one hand, this allows services to be built, tested, and tweaked simultaneously—no more monolithic development cycles. On the other hand, though, the ESB represents a single point of failure for the entire system—so in a way, the effort to eliminate the monolith only created a new one: the ESB, which could potentially bottleneck the whole organization.
What's the difference? Microservices can communicate with each other, usually statelessly, so apps built in this way can be more fault tolerant, less reliant on a single ESB. This also allows dev teams to choose their own tools, since microservices can communicate through language-agnostic application programming interfaces (APIs).
Given the history of SOA, microservices are not actually all that new of an idea. However, microservices have become more viable thanks to advancements in containerization technologies. With Linux containers, you’re now able to run multiple parts of an app independently, on the same hardware, with much greater control over their individual pieces and life cycles. Along with APIs and DevOps teams, containerized microservices are the foundation for cloud-native applications.
Microservices give your teams and routines a boost through distributed development. You can also develop multiple microservices concurrently. This means more developers working on the same app, at the same time, which results in less time spent in development.
Ready for market faster
Since development cycles are shortened, a microservices architecture supports more agile deployment and updates.
As demand for certain services grows, you can deploy across multiple servers, and infrastructures, to meet your needs.
These independent services, when constructed properly, do not impact one another. This means that if one piece fails, the whole app doesn’t go down, unlike the monolithic app model.
Easy to deploy
Because your microservice-based apps are more modular and smaller than traditional, monolithic apps, the worries that came with those deployments are negated. This requires more coordination, which a service mesh layer can help with, but the payoffs can be huge.
Because the larger app is broken down into smaller pieces, developers can more easily understand, update, and enhance those pieces, resulting in faster development cycles, especially when combined with agile development methodologies.
Due to the use of polyglot APIs, developers have the freedom to choose the best language and technology for the necessary function.
If your organization is thinking about shifting to a microservices architecture, expect to change the way people work, not just the apps. Organizational and cultural changes are identified as challenges in part because each team will have its own deployment cadence and will be responsible for a unique service with its own set of customers. Those may not be typical developer concerns, but they will be essential to a successful microservices architecture.
Beyond culture and process, complexity and efficiency are two major challenges of a microservice-based architecture. John Frizelle, platform architect for Red Hat Mobile, laid out these eight challenge categories in his 2017 talk at Red Hat Summit:
- Building: You have to spend time identifying dependencies between your services. Be aware that completing one build might trigger several other builds, due to those dependencies. You also need to consider the effects that microservices have on your data.
- Testing: Integration testing, as well as end-to-end testing, can become more difficult, and more important than ever. Know that a failure in one part of the architecture could cause something a few hops away to fail, depending on how you’ve architected your services to support one another.
- Versioning: When you update to new versions, keep in mind that you might break backward compatibility. You can build in conditional logic to handle this, but that gets unwieldy and nasty, fast. Alternatively, you could stand up multiple live versions for different clients, but that can be more complex in maintenance and management.
- Deployment: Yes, this is also a challenge, at least in initial set up. To make deployment easier, you must first invest in quite a lot of automation as the complexity of microservices becomes overwhelming for human deployment. Think about how you’re going to roll services out and in what order.
- Logging: With distributed systems, you need centralized logs to bring everything together. Otherwise, the scale is impossible to manage.
- Monitoring: It’s critical to have a centralized view of the system to pinpoint sources of problems.
- Debugging: Remote debugging through your local integrated development environment (IDE) isn’t an option and it won’t work across dozens or hundreds of services. Unfortunately there’s no single answer to how to debug at this time.
- Connectivity: Consider service discovery, whether centralized or integrated.