Microservices: Have you thought this through?

Regardless of the traction that microservices architectures have gained, there are technical aspects and essential considerations you should take into account before developing within a microservices structure.
After working with microservices, I have radically changed my way of thinking about software, scaling, and problem-solving. For the challenges I was tasked with, microservices were indeed the right solution. The latest piece of code I wrote was for the 50th microservice in a cluster, which is ready to expand to accommodate a few hundred more. There is a lot of buzz around microservices, and for good reason.
But what are microservices? According to microservices.io:
Microservices – also known as the microservice architecture – is an architectural style that structures an application as a collection of services that are, highly maintainable and testable, loosely coupled, independently deployable, organized around business capabilities.
To be more specific, microservices are autonomous deployable entities that cover only one aspect of your entire business. Each deployable entity is a complete server by itself – with its own necessary stack for running the operations specified.
A development team’s paradise?
If you have developed a large web application that includes all aspects of your business, such as user information, payments, and product catalogs, you will likely start breaking it into smaller components/servers. These servers would be deployed inside the same virtual network—a virtual network being an abstraction that enables communication between microservices deployed within it. You can easily create such virtual network configurations using Kubernetes and Docker Swarm.
Each server would have its own deployment cycle, development cycle, and scaling strategy. This has enabled development teams to:
- Have faster and simpler development cycles. A team can now develop a microservice as an independent component without worrying about the implementation of other components in the business application.
- Deploy parts of an application independently.
- Reduce downtimes. An exception in a server (or a memory leak) will only affect one part of the business, as the rest of the servers will continue to function.
- Enable independent scaling. If a specific domain of your business (such as retrieving user information or authorizing users) experiences higher demand, you can scale that part alone without affecting the rest of the deployed components. This is typically done by adding new replicas (horizontal scaling) or increasing the allocated memory/CPU for that specific microservice.
Anyone who has read multiple articles about microservices has probably encountered cost as a key concern. Well, cost is a complex issue, which we will tackle later. For now, let’s say that cost-effectiveness applies only to a subset of microservices implementations I have encountered during my career.
Problems in every paradise
So, should you start breaking down your monolithic server into smaller components now? You can. I would even say that you have likely already made up your mind about it. I am not here to change anyone’s mind (I love microservices, anyway). However, I want to share some of my thoughts about your routine if you decide to go down the microservices path—and to share some lessons I have learned the hard way.
DevOps
DevOps is an essential aspect of the development process—one that is often overlooked or forgotten. Some argue that developers should handle DevOps since they understand their development stack and resource needs. Others believe DevOps should be managed by dedicated and experienced administrators with access to 24/7 monitoring tools, responsible for all non-development aspects. In my opinion, the best approach lies somewhere in the middle.
Regardless of which side of the Developer/DevOps debate you align with, DevOps will be a challenge. No matter the size of your business or cluster, here are some key daily DevOps tasks:
- Ensuring versioning semantics are properly followed in all microservices within your cluster.
- Verifying that build scripts for your microservices function correctly. If you have 50 microservices and need to update a Jenkins file (or any CI/CD build step), you must manually update all scripts.
- Managing your Git repository. Typically, each microservice has its own repository. When your microservices exceed 30, managing source repositories, versioning, and CI/CD hooks becomes a critical yet tedious process. This complexity increases costs and administrative overhead.
- Tracking your namespaces. Depending on network policies, this can be challenging. You may have multiple Kubernetes namespaces, each corresponding to an environment (e.g., "dev," "staging," "production"). If you have 30+ distinct microservices, you could have 90 instances running at various stages of completion.
- Monitoring resource usage. Regularly check RAM usage, restarts, and whether allocated resources need adjustments. Determine the necessary number of replicas. Tools like Grafana will become invaluable.
- Monitoring logs. Console logs alone are insufficient for debugging microservices. Consider a scenario where a request reaches microservice A, which in turn interacts with microservices B, C, and D. To trace the “request journey,” you need to propagate a unique request ID across all services or implement advanced logging through a service discoverability mechanism.
- Managing Service Discoverability. Microservices, each operating as a distinct server, expose interfaces (typically REST APIs). In large clusters, service discovery mechanisms like Istio or similar tools may be necessary.
- Managing networking and network policies within namespaces to prevent security breaches that could compromise your entire cluster. If you're building your own cluster, you will also need to handle user access and bare-metal provisioning (adding more nodes to the Kubernetes cluster).
Given these responsibilities, I strongly recommend that companies—regardless of size—employ at least one dedicated DevOps specialist to manage these tasks and anticipate future challenges as the cluster scales.
Writing APIs
The chances are that if you are using microservices, you are using REST APIs to make those services communicate with each other. In terms of coding, that means that you must exercise extreme discipline in creating reusable rest interfaces for your microservices to interact.
As your microservices evolve, so will your REST APIs. You have to use proper versioning, keeping track of your microservices, and their business rules. If one call to your service means that you have to gather information from multiple microservices, then the maintenance and complexity of each one’s API is increased dramatically and will undoubtedly demand much of your time in the long run.
This process is vastly more complicated from monolithic architectures where you have the luxury of looking at the inputs of your functions in code. When developing a monolith, refactoring does not always mean that you are going to change the coding interface between different parts of your application. When developing a microservice, however, you will most certainly do.
Development
The API Gateway
In order to expose your apis to the public, you will want an API gateway.
API Gateways are a vast subject, but in short, the API gateway is the entry point to your cluster. Depending on what you need it to do, you would want it to perform one or more of the following tasks:
- Request routing & enriching requests from the outside world towards your microservices
- Load balancing / Request limiting
- API standardization between your frontend (whether it’s a web app, desktop app, or a mobile app) and your cluster.
- Manage your APIs
An API Gateway is not a component; it’s a pattern found in microservice clusters – and considered one of the core components of a Kubernetes based solution. If the API gateway doesn’t perform 100% correctly, all of your users will suffer the consequences.
There are solutions provided by major players in the Industry, such as Apigee on Google Cloud, Red Hat’s 3scale, Kong, and many others. Depending on where your service is hosted (and your budget), you will need to customize your deployments so that your services are discoverable by the API Gateway, and also to write code for the API Gateway itself. The process of maintaining and evolving your API Gateway is not something you want to take lightly, regardless of how simple it may seem at first. Your gateway implementation will grow with each API you expose to the outside world.
Code duplication
Each one of your microservices will need to request information from the database and will map this information to models. Those models will most probably be reused across multiple microservices, so you will either need to have to write numerous times the classes used, or you will need to put them in a library and reuse it across your repositories. Same goes with the technical business that your microservice will also need to adhere to (like security and other business-specific business logic.
Technology Stack
You have probably heard and understood that you leverage the microservices architecture’s capabilities to develop your cluster into different technologies.
This belief is true. An example of this is that you can have a performance-heavy part of your business and use Golang or C++ for this part while leaving the rest of your cluster with another technology.
As a general practice, however, you will want to develop microservices in a technology that follows the trends of the company you work for. Do not underestimate the task of maintaining your code as your cluster grows larger. You should follow the “good conventions always win” practice to keep services maintainable. You don’t need to choose the absolute best technology for specific parts of your business – you have to choose the one that can serve 90% of your tasks with ease.
Costs
One of the most important matters is this one.
The cost in microservices clusters has been the subject of debates in many places around the internet – and it is one of those that cannot be quickly answered without knowledge and analysis of your specific use case.
How you break your business into smaller components matters – and very much so. Imagine this: Under normal circumstances, adding just one little new REST API Endpoint to a monolithic microservice will not add any significant overhead to your server, since all your endpoints share most of the library dependencies and code. However, if you develop an entire microservice to accommodate this new small API, you will also add the overhead of a whole stack on top of it – that is perhaps a new JVM with a small system (alpine, for example) that will live inside your docker machine.
Some APIs are going to be used much more than others – and those need to be scaled independently while other microservices need much lower resources allocated to operate. This distinction and flexibility of independent scaling are what is going to save you money – so plan your cluster accordingly.
Based on this knowledge, you alone are always the best person to identify what will be the costs of your cluster – I am just going to say that I have seen clusters exploding in terms of resource usage by not analyzing how components are going to be broken down in advance. Proper analysis is the key before determining how to split your business model into smaller parts – and will affect your cost dramatically.
However, in your cost calculations, do not also forget to add the costs of the Human Factor. That is the human resources that you are going to need when maintaining your cluster, be it DevOps, technical coordinators, etc.
Conclusion
I have written so many things, and I still feel I have barely touched the surface. Microservices are such a broad concept that cannot be covered in an article such as this one.
Microservices are incredibly flexible and exciting. Even if you use technologies well-established in the monolithic world (like Java), you will find out that those are gaining traction again with the advent of frameworks like Quarkus and Micronaut.
But as it happens with any other technology, you should go into this with both eyes open, whether you are a developer or not. Careful planning is always the key – as is avoiding the common pitfalls that software trends sometimes force us to fall in.