Anti-Patterns Working with Microservices
The main problem with monolithic applications is that they are hard to scale, in terms of the application, but more importantly, in terms of the team. The main reason for a switch to microservices should be about teams, Tammer Saleh claimed at the recent QCon London conference when describing common microservices anti-patterns and solutions he has encountered.
With a small application and a small team there is no problem with a monolithic application, but as the application grows and with dozens, or more, of developers they can’t all work on the same code base. Moving to microservices enables us to make use of Conway’s law in our own favour.
Saleh claims that building with microservices is incredibly complex with a lot of ways that it can go wrong. The most common mistake for him is to just start with microservices. He emphasizes that for all systems you should start as simple as possible and slowly migrate to more complex solutions only when there is need, from a team or business perspective. He notes that the most important thing for a company is to get to the market and make money, not to explore interesting architectural patterns. His solution is instead to start monolithic and extract when necessary, and notes that microservices add a constant tax to development.
With spikes in traffic enough servers must be maintained to handle the peak loads, but with increased traffic and more servers eventually the database will be overloaded instead. One solution to this is to even out the load using queues, basically the queue is a buffer that smooth the traffic out. This makes the communication asynchronous with an increase in complexity since the request-response lifecycle is broken and clients are forced to deal with asynchronous responses. For Saleh this a necessary step though to avoid spending too much money on computing resources.
Services trying to connect to one specific service which is not responding is from an operational perspective a problem. It’s hard to do diagnostics and work on the service that is down because of all the requests coming in. With tens or hundreds of other services sending requests this can cause all sorts of problem. A solution is a Circuit breaker which prevents requests reaching the service that is down. When working normally the circuit breaker allows all traffic go through but as soon as it discovers a failure it will prevent further access until the failing service is responding again, at which time the traffic is allowed to pass the circuit breaker again.
With microservices comes a whole new set of testing patterns. One common pattern is mocking the services a service under test is consuming. A team testing a specific service will then instead of communicating with the consuming services write mocks for these services. In the end this means that each team will write their own mocks for all services they consume. This may create a lot of extra work and in Saleh’s experience a common solution is to let each team write a common mock for each service they are responsible for, resulting in one mock for each service. An improvement to this that he suggests is that each team writes language specific clients to be used by the consuming services. This client then handles the actual communication with the service and may also include a mock. One advantage Saleh sees in this is that a service has full control over the protocol used and may change it as needed. He also believes this to be a nicer interface.
In a blog post from 2015 Stefan Tilkov argued against always starting with a monolith, instead arguing that for a large enough system we should think about individual subsystems from the beginning.
In a presentation earlier this year Ben Christensen talked about the risk of using a client library, written by the service team, which is the only official way to access a service.
The tao of Microservices
Re: The tao of Microservices
I agree that starting with a monolith properly modularized is the way to start, unless you have a bunch of Architects, Consultants and legions of developers to develop the microservices framework from the start.
You can even leverage a MessageBus inside a monolith in order to ensure modularity and decoupling.
Service teams publishing client libraries is a bad idea
Using client libraries to talk to services defeats the isolation provided by microservices, increases coupling, and introduces strict version dependencies. Instead, rely on contract testing to detect incompatibilities.