BEST PRACTICES FOR MICROSERVICES

Hansini Rupasinghe
Nerd For Tech
Published in
10 min readJun 8, 2021

Through this article, we will be discussing best practices that should be followed when implementing Microservice Architecture. The topics that we will be discussing are as follows.

  1. Design
  2. Hard Coded Values
  3. Logging
  4. Versioning
  5. Authorization and Authentication Mechanism
  6. Dependency
  7. Make Executable Contracts
  8. Fault Tolerance
  9. Documentation

1. Design

Ref: https://images.app.goo.gl/b35PjF4wqz9YYYMh7

● The Design should be Domain-Driven (DDD)

PRACTICE:

Always try to start fresh as much as possible. You may not need to write everything from the beginning. Rethink!!! And if there is something inside your service that is not relevant to your service, take that out. In that case, we do not need to redesign or rewrite everything, but we can do little tweaks, isolate and make services independent to your domain.

Example : Let us take a Rent A Car Application as an example.

Customer can represent 2 different services but in 2 different aspects. Customer Registration / Account Management service can contain a creation part. When you implement Support Service or other service, this same customer can represent in that module in a different aspect. The main scope of that service is customer support. In another service, the main purpose would be finance/account/payment etc.

● So, when you design a service and when you think of moving to Microservices, you need to make sure that you have a fresh domain-driven design.

● Rewriting a system depends on how much a company can afford.

Let’s say you have the same Rent A Car web application and you have following modules;

➣ Inventory Module

➣ Stakeholder Module

➣ Search Module

➣ Renting Module

➣ Finance Module

● It is a wrong practice to put the modules altogether and create one or two services and say “We use Microservices!”.

2. Hard Coded Values

Ref: https://images.app.goo.gl/gMvbHUkEWT7zdVqb6

PRACTICE:

You should not hard code foreign services’ addresses. You should use service discovery tools for that job.

Just assume, one of your services is calling another service.

Example : Before renting a vehicle to the customer, you may call another service to get customer history. Let’s name 2 services as Service A and Service B. Assume service A calls service B. In order to do this, you must have the address of service B. In that case, most of the developers hard code host name/URL/IP address on service A. Now service A knows where service B is.

The problem ➜ If your network team decides to change host name / network address, you will need to change the code and send another deployment. This is not correct.

How to Overcome There are many service discovery mechanisms and tools to discover another service instead of hard coding the routed service.

How Service Discovery Mechanisms work?

Assume service A is going to service B. And service C also needs service B at the same time. Now, service A sees that service B is offline. And the discovery tool can host some flag denoting that service B is offline. Therefore, service C is able to see that service B is offline before calling it.

✹ You can use any service discovery mechanism no matter if it is Proxy. But, if you do not use Proxy properly, it can inherit other services’ issues too.

3. Logging

Ref: https://images.app.goo.gl/HS7Cz2XoWmTkqXSK7

PRACTICES:

Fail Fast, Log Later! If something fails, come back immediately. But do not log.

Do not do too much logging because debugging will be hard .

Do not log from everywhere. Log only from the where you initiated the process. But, you must log the Stack Trace or some information to understand where this really happened. Then, you will not get misled by saying that a particular error happened on service layer when it has actually happened on a repository layer.

You need to log only once for a particular error and make sure you do not loose any information throughout this journey to understand what has happened.

You can generate and call a unique Correlation ID whenever you hit the initial service. Your web server too can generate a Correlation ID somewhere before you hit your service layers.

Ref: https://www.c-sharpcorner.com/article/logging-and-tracing-in-multiple-microservice-with-correlation-using-net-core/

✹ You can use a log appender (log interceptor) to send the log messages to some destination or medium and to write Correlation ID or whatever the ID you generated to your file along with the log statement. Log will include Date, Time, log level, ID and the log statement.

Examples for Log Management Frameworks :

📋 Log4j

📋 Logstash

📋 Splunk

📋 Elastic

✹ Logging is very important to troubleshoot any application problem and it must be maintained properly. Logs can be written in as a text file or console or cloud.

✹ It will be easy to track your service calls at any given time when logs are properly managed. You can search a service through logs and can find information such as ;

➣ Where it initiated

➣ What is the destination

➣ The path in between the initial point and the final destination

➣ Where the exception happens

➣ Where it comes back

✹ Make sure you have some kind of a log aggregation mechanism to search through logs.

4. Versioning

Ref: https://images.app.goo.gl/CwHoG4P6ADyMCHwd7

PRACTICE:

Use versioning for breaking changes

There are basically 2 most popular techniques used in versioning namely;

  1. Semantic Versioning
  2. Calendar Versioning

Semantic Versioning

Semantic Versioning is a good governing mechanism for version numbering that specifies which digit to be incremented if it is ;

➣ A Bugfix.

➣ A new functionality or change of functionality. (But if it is backward compatible, which digit to be changed)

➣ Breaking any existing functionality.

It can be known as a formal convention for defining compatibility using a three-section version number.

Format: <major>.<minor>.<patch>

Ref:https://images.app.goo.gl/tofmZzMV8gwQnFPZA

Major: You increment whenever your service is incompatible with previous service / version. (This is because it has functionally changed)

Minor: You increment when you have new functionality, but previous functionalities also remain as it is. If user wants, user can update. Otherwise the user can consume previous version.

Patch: You can increment the way you want. It does not reflect a much serious thing.

Calendar Versioning

Calendar versioning is a convention that is based on the release date instead of arbitrary numbers.

Ref:https://images.app.goo.gl/WZJW73xpTLPxs3r3A

The year and release number together reflects the features that a build contains.

Executables and container images will be marked with a build number and a status tag along with the year and release number.

The build number will change in order to determine the addition of bug fixes or cosmetic improvements within a release.

Apart from the above mentioned techniques, there is a concept called “Force Upgrade.

Let us understand this concept using an example.

Let us say you have a service called service A. This service A calls service B and you need to update service B. During this update, you are changing your database structure. Therefore, the service request would be different. When you push the new version of service B, your service A will break. Therefore, you can increment major version number and you can deploy this as a separate version.

Now you have two versions of service service B. But, your traffic still takes the previous version of the service B. Therefore, you can communicate with customer and inform that the functionality has been changed and ask the customer to upgrade to the newer version before a certain date since the existing server will get decommissioned after that date. Once all customers migrate into the new version, you can shutdown and decommission the previous version.

✹ You can use Elastic Mechanism too. Let us see how it works using an example.

Assume you have 5 consumers and you are running 10 instances of previous version. When consumers are moving to the newer version, you can increase the instances of newer version while decreasing the instances of previous version. With this way, you can manage your services without breaking any consumer.

5. Authentication and Authorization

Ref: https://images.app.goo.gl/VPepFTfXh3J1fNgf9

If each and every service tries to validate the user, it will add more latency to your round trip.

Example: When a user tries to allocate vehicles to customer in Rent A Car Application, it invokes 3 different services. Assume you are using an OAuth token to validate consumers and it takes 20 ms to validate an OAuth token. When this request comes and if it goes through 3 different services trying to validate OAuth token separately, then it causes trouble since you are adding 60 ms to your round trip.

PRACTICE:

Have separate identity validation service and direct whatever the request that hits your service layer to this identity service. You can direct to rest of the path if only if it is successful. We call this as “Force Forwarding”. You will be forcefully forwarding to a particular service, so that you can avoid some latencies.

✹ You can change this authentication and authorization process at any given time based on your algorithms or service discussions.

6. Dependency

Ref: https://www.aternity.com/blogs/monitoring-a-microservices-based-application/

PRACTICE:

You should avoid any dependency to your service and the service should be deployed independently.

Example: Let us say you have services A, B and C. When you want to deploy A and fail in deploying other B and C services together, there will be a problem.

✹ Therefore, independency is a must. We should be able to deploy Service A, B or C separately without worrying about breaking others.

7. Make Executable Contracts

Ref: https://images.app.goo.gl/dGF7cdqc8YLUQ4t48

Let us take a normal application. There are UI/UX mechanisms to make sure that the users like a certain website / application. Your service too has users who we known as consumers. It is a developer’s responsibility to make a service easy and to make the consumers happy. In your A, B and C service structure, if you are breaking some consumers when you deploy one of the services, then you have done a bad job.

PRACTICE:

You and your consumer can have a contract (May be an API Specification) and make sure that the contract is not broken at any cost. In order to ensure this, you can convert this contract to an executable. Some test cases/test scripts/ requests can be prepared. Whenever you make a build of a particular service, those tests will execute automatically.

✹ Continuous integration tools will execute those tests. If all those tests are passed successfully, you can make sure that you did not break them. If the tests are failed, you are 100% sure that you are breaking your consumers when pushing these to production. But you can fix that fast.

Examples for Continuous Integration Tools:

🔧 Jenkins

🔧 Buddy

🔧 Travis

🔧 GitLab CI

🔧 Codeship

8. Fault Tolerance

Ref: https://images.app.goo.gl/1ibtRFDxpyiDUUhw7

Wikipedia Definition:

Fault tolerance is the property that enables a system to continue operating properly in the event of the failure of (or one or more faults within) some of its components”

✹ Since you have multiple services when deal with microservices, you have multiple possibilities to have failures. And you need to manage them properly.

There are several patterns to achieve Fault tolerance in real life. They include;

🔖 Timeouts

🔖 Circuit Breakers

🔖 Deadlines

🔖 Retries

etc.

PRACTICE:

You need to have a strong fault tolerance mechanism. If one service is timing out, failing slowly or taking a long time to respond, then you need to make sure that you fail fast, so that you will not create a queue behind you while waiting for the service to respond.

9. Documentation

Ref: https://images.app.goo.gl/hCJUFtj7UNvChpwG9

PRACTICE:

You need to document whatever you do, because another person should be able to understand what you have written.

✹ You can use Swagger which is an Interface Description Language to write documents in a technical way.

Ref: https://images.app.goo.gl/w9HoJ7fp7NE3eeWU7

✹ Swagger will take care of converting whatever the technical terms you write on the Swagger console into a very nice and attractive documentation.

✹ Not only documentation, it provides users with a UI, so that consumers can refer to that and try out some services.

References

--

--