Decoding Microservices
A little over a year ago Elon Musk sparked a Twitterstorm discussing Twitter’s backend problems, specifically targeting the issue of “microservices bloatware.”
I’ll dive into the world of microservices, unraveling their significance, and addressing the complexities they introduce.
Breaking Down Microservices: A TL;DR
Microservices offer a revolutionary approach to application development by breaking down traditional monolithic structures into small, independent services. Unlike the conventional model where all services run on the same infrastructure, microservices empower each unit to operate autonomously. Despite their effectiveness in scalability and downtime prevention, the adoption of microservices comes with its share of intricate challenges.
The Anatomy of Services
Before delving into the “micro” aspect, it’s crucial to understand what a “service” entails. In the context of application development, a service refers to any independent component handling specific functions, whether on the backend or frontend.
Using the example of Lyft, let’s imagine you open up the app on your phone and request a ride. A lot of things are happening behind the scenes:
- Finding the drivers that are closest to you
- Ensuring those drivers are vacant or finishing up a trip
- Matching you with the optimal driver
- Deciding what price to show you
- Deciding what price to show the driver
- Creating an ideal route for the driver to navigate to you
These could all be described as services. This is of course illustrative — I’m sure I’m missing plenty, Lyft’s backend is hella complex.
Here’s a simple way to think about it: Imagine services as tasks, and a good rule of thumb (though not always perfectly accurate) is to consider them in relation to API endpoints. Each task, like finding drivers or deciding on a price, might have its own special endpoint: a sort of specific spot it connects to in the system.
In reality, some tasks may not have their own special spots (endpoints), or some might, but they mostly use other spots. For example, the price task might use a big machine learning model, while the match task could use a simple method or some “if” statements. The closest drivers task might just read basic data from a database. We don’t need to know the details, and it’s not important. What matters is that each of these tasks is its own service and plays a specific role in the backend.
Point of Clarity: Not only are API endpoints and their rules considered services; other parts of your application, like the part you see (HTML, CSS, JavaScript), can also work on their own. They might even be split into their own microservices. Additionally, databases today are often copied across many servers to make sure they’re always available.
Monoliths vs. Microservices: The Architectural Shift
Historically, applications were developed using monolithic architecture, where all services coexisted within the same infrastructure. However, as companies like Lyft faced challenges in scaling, uptime, and specialized infrastructure, a paradigm shift towards microservices emerged. Microservices offer a more flexible and scalable approach, allowing services to operate independently, thus overcoming the limitations of monolithic development.
Imagine if Lyft had built its systems in a monolithic way. In that case, all the different tasks like finding drivers, determining prices, and creating routes would happen on the same server or be duplicated across identical servers. It’s like they’re all sitting together, doing their jobs side by side, and they might even share the same server with the database and the part you see on your app.
Building things this way, in one big group of code, is called monolithic development. It’s easy to do and makes sense for many applications because you don’t have to worry too much about where your code goes. For small applications, it usually doesn’t affect how well it works. But as companies like Lyft grew and more people started using their app, they faced some problems with the monolith approach:
1. Scale: Some tasks are used more than others. If they’re all on the same infrastructure, you can’t make one task work better without making all of them work better.
2. Uptime: If one task has a problem and stops working, the whole system is affected.
3. Specialized Infrastructure: For things like machine learning or video processing, you need special tools like GPUs. Monolithic architecture doesn’t allow you to use these specialized tools efficiently.
4. Developer Experience: When all tasks are on the same system, making a change to just one task means you have to update and test the entire system. Plus, all tasks need to be written in the same programming language.
To overcome these issues, companies found a smarter way to build their services together.
In a microservices-based approach, each task is separated into its own independent piece. They can work on their own without depending on the others and communicate through something called an API endpoint. This diagram by Martin Fowler illustrates this concept well.
Building Microservices in Practice
In a microservices-based architecture, services operate as independent entities, communicating through API endpoints. This separation enables effective scaling, improved uptime, utilization of specialized infrastructure, and a more tailored developer experience. The shift from monoliths to microservices is akin to separating goods into different shipping containers for optimal efficiency, reflecting the diversity and independence of software services.
Complexity Costs and Ecosystem Considerations
While the benefits of microservices are evident, it’s essential to acknowledge the associated complexity costs. Managing a network of interacting services introduces challenges in authentication, communication, SLA agreements, and language disparities among development teams. No single solution works for both monolithic and microservices architectures. You should choose the one that fits your organization best, considering its specific constraints.
Elon Musk’s Microservices Commentary
Elon Musk’s Twitter commentary on Twitter’s backend sheds light on the ongoing debate around microservices. His remark about turning off “microservices bloatware” raises questions about the necessity and efficiency of incorporating microservices into a platform. The ensuing discussion emphasizes the need to strike a balance between functionality and streamlined architecture.
Conclusion
As Elon Musk’s Twitter commentary echoes through the engineering community, the debate on microservices continues to evolve. Whether your organization leans towards monoliths or microservices, understanding the nuances is key to making informed architectural decisions in the ever-evolving landscape of software development.