Blog>>Software development>>Microservices>>Best practices for microservices you should adopt

Best practices for microservices you should adopt

Microservices are all the rage these days. But the simple fact of implementing microservices architecture on your project doesn’t guarantee success. There are certain recommended practices that you should follow when it comes to microservices. Only then can you be sure that you will unlock the full potential of microservices architecture.

How microservices architecture works

Before learning in-depth about what you should keep in mind when adopting microservices, let’s quickly recap what they actually are.

Microservices architecture presupposes building an application as a bunch of loosely coupled independent components. Each of these components communicates with others and performs a single service dedicated to a specific business purpose. 

Microservices are quickly replacing traditional monolithic architecture, where the whole app used to be developed as a single block of source code. However, each specific characteristic of microservices has the potential to turn into an advantage or a disadvantage. That is why there are a number of best practices that will allow you to reduce the negative effects and make sure you enjoy all the benefits of microservices

Microservices best practices

There are recommendations for every step of building your project with microservices, from planning to maintenance. The exact number of microservices best practices may vary but we will take a closer look at the most popular ones.

Analyze the needs of your organization

Even though everyone seems to be switching to microservices, it doesn’t necessarily mean that your business requirements justify the complex implementation of this type of architecture. Some simple web applications can actually still be successfully developed as monoliths. It is important to only segment your app into microservices if that brings value

To make a more informed decision, don’t forget to involve all the stakeholders in the planning process. Various members of your team can offer important opinions, not only regarding the complexity of the transfer, but also the time and costs involved, and the effect it might have on your business.

Create dedicated development teams

Each single microservice basically works as a tiny app, so you need to have separate independent teams working on them. This will allow you to choose the best stack for each microservice and shorten the time to market. Each team should only be large enough to be able to work autonomously. However, it will take some time and effort to organize effective communication between teams. It can also become expensive pretty quickly, so careful planning is essential. 

If you have a monolithic architecture app and a team of developers working on it before switching to microservices, don’t rush to discard your team and start from scratch. You might need to involve some new DevOps specialists, but the cultural shift shouldn’t be too daunting for the developers who are already familiar with your business needs, especially if you help them to comprehend it.

Segment just enough

Your various business functions should not automatically become your microservices. It is important to define the microservices so that each of them fulfills a specific business purpose, but the number of microservices should not be too small or too large. If a microservice does too much, it stops being a microservice, so the goal you originally set when migrating to microservices won’t be achieved. However, if there are too many microservices, you will have a hard time organizing the communication between them, and the overhead might increase so much that the costs stop being tenable.

The best option here is to follow the single responsibility principle. When each service performs only one function, but does it efficiently, it ensures coherence of functionality and reduces response times and dependencies. As a result there is less chance the rest of services might fail if one of them goes offline.

The next step in the same direction here is adopting the domain-driven design model. This principle suggests basing the microservices on business domains, so that they reflect real user activities in a certain domain together with its internal models and details. 

Take care of microservices security

A distributed system is, by definition, more vulnerable in terms of security than a monolith, so making it fully secure requires approaching that question differently. It is recommended to involve the security team early in the process of designing an app, instead of just asking them to take a look at the deployment stage. An even better way is using cross-discipline teams. That is part of introducing the DevSecOps model, which suggests that any developer, not only the members of the security team, should take part in ensuring the safety of the project. 

There are more specific recommendations that will help you keep your microservices project secure. Using open-source tools is a common practice that allows to reduce costs, but it is important not to start depending on them in a way that might compromise security. Establishing certain guidelines and policies in your organization can make this process safer.

When you use APIs to enable your microservices to communicate with external resources, the security of this communication is also crucial. A popular solution for this is to establish a single point of traffic entry, an API gateway, that controls access to data using tokens for authorization and authentication. The API gateway can be additionally protected using a firewall.

Separate data store

There is no point in using microservices if they all store data in the same database. Having a single database means your app is not so different from a monolith, since the shared database becomes a single point of potential failure. A much better solution is creating a distributed database, where each of your microservices will have its own data storage. Each of your microservices might use a different stack, and in that case having separate databases especially makes sense. You can choose a database that suits each microservice’s needs best, and if other microservices need to access the data there at any point, they can connect to it via an API.

The same goes for storing files. Different microservices have different requirements for file storage: some need to store large files, others, only small ones. The frequency of accessing the files in storage can also vary significantly. Choosing different storage and respective infrastructure for each microservice allows for more flexibility when you are looking for a storage provider, and consequently a better chance of reducing costs.

All in all, creating a separate data persistence layer for each of your microservices is a way to ensure your app is more stable and resilient and achieve better fault isolation. You can even configure and manage that layer as yet another microservice.

Adopt asynchronous communication

Although independence is one of the main principles that defines microservices architecture, effective communication between components is what makes a web app actually do its job. Microservices can communicate in two ways: synchronously and asynchronously.

Synchronous communication means microservices are tightly coupled. When the server needs to send a request to multiple microservices, it is done one request at a time. The server sends a request to e.g. Service A, then waits for a response, and only when the response is received will the server send the next request to Service B. It is pretty obvious that this chain of requests can easily be broken. If Service A is down and doesn’t respond, no further action will be taken, which means downtime for the whole app. 

Alternatively, when asynchronous communication is set up, the server doesn’t wait for a response from Service A, it just continues working, while Service A is responsible for sending the next request to Service B asynchronously, Service B will send a request to Service C, and so on. In this example, again, it appears if just one service is down, the work of the app will be disrupted. But this is not the only approach available for asynchronous communication. It is also possible to call microservices in parallel and use events to drive communication. You can set up a separate service to work as a message queue. All the microservices will listen only to the message queue and look for certain types of events. If a new event is published, and a certain microservice is subscribed to events of that type, it will result in a response. This reduces the impact that one service’s failure might have on other microservices. 

Manage containers smartly

More often than not, you can’t imagine managing and deploying microservices without using containers. It is understandable, containers provide an easy and efficient way of setting up independent environments with a predefined set of parameters where you can run your services. However, simply running a bunch of containers at the same time can quickly become overwhelming and complex, if you don’t use special tools to manage them.

A container orchestration platform, like Kubernetes, enables your app to remain highly available to users by taking over the management of scaling and load-balancing. It controls the deployment of containers and resolves issues with network communication.

Furthermore, since containers are built from images, that can potentially cause issues with security, so don’t forget to run scans regularly to ensure safety is not compromised.

Make changes responsibly

No app stays the same forever, so at some point changes to your API become inevitable. The important thing here is to ensure backward compatibility, so that the existing callers keep working. The best protection measure against changes that can break your API is to implement contract testing. Contracts are provided by your API consumers and contain the responses they expect from the API. In turn, you include contract tests into your consumer builds, which will help to prevent breaking changes. Since consumers also test the contract changes on their own, it reduces the time to production significantly.

In situations when it is simply impossible to make changes backwards compatible, it is recommended to support multiple versions of your exposed endpoints. In this way, the consumers will be able to choose whether to stay with an older version of your API or to move to the next one. Although, you have to make sure multiple API versions won’t become too expensive and complicated to maintain, so older APIs will have to be deprecated at some point. In that case, either reroute traffic to the new API versions internally or choose a different approach; for example, add a deprecation notice to the response the deprecated API originally provided.

Monitor your entire system 

One of the advantages of microservices architecture is that it can be easily scaled when needed. The result could be hundreds and even thousands of microservices, sometimes even tens of thousands, and with each of them something can potentially go wrong. If you want to address the issues in a timely manner to keep your users satisfied, monitoring the microservices becomes a must. There are multiple readily available solutions that allow you to strategically monitor the services’ availability, their resource usage, and potential networking problems. 

To facilitate analyzing errors and searching for underlying causes, you should also think about standardizing the logs format for all your microservices. A centralized advanced system for both monitoring and logging would be ideal. Some solutions also allow you to visualize the data received from monitoring, and that could greatly improve your business intelligence capabilities.

Conclusion

If you’ve made the decision to adopt microservices architecture for your project, keep in mind the above recommendations. Following these best practices for microservices is a guaranteed way to develop a resilient, highly available, and easily scalable solution that will make your product successful.

Manturewicz Maciej

Maciej Manturewicz

Director of Engineering

Maciej is a Director of Engineering with nearly two decades in the software industry. He started his career journey as a software engineer, and he gained experience on every step of the ladder before landing in his current leadership role. With a rich background in software engineering, Maciej possesses a...Read about author >

Read also

Get your project estimate

For businesses that need support in their software or network engineering projects, please fill in the form and we'll get back to you within one business day.