What is this about?
You may want to check our previous article to gather some insights about Microservices in general.
In this blog post, I’d like to present my very subjective view on 10 traits that when present, will make Microservices much pleasant to use by developers … and ultimately put a smile on their faces :)
Ten traits of Microservices that make developers happy
Recently I had a meeting with experienced architects - we wanted to define what Microservice means. The high level idea was to capture some fundamental values and guidelines and then map them to our solution and common understanding. A noble goal nonetheless. What we ended up with was mostly everything what Martin Fowler described in Characteristics of a Microservice Architecture. Simply, because they are very good and valid and ultimately there was no need to reinvent a wheel.
But as I said, these high level values that we got, they are crucial - without them, we won’t achieve what we desire: scalable, maintainable and clearly separated services - we won’t be in microservices world. Instead we will end up with monolith in disguise, not scalable, brittle, coupled.
During preparation for this meeting I also thought about my subjective, down to the ground, developer’s things that put a smile on my face and induce good feeling about overall solution. They are mostly technical and process/lifecycle related. I’m not gonna tell that they are mandatory, but in my book they are extremely important and I strive to implement and keep them at all times. So, here they are:
1. Unit and integration tests
This point shouldn’t really be here because we are all writing a lot of tests (right?). Still, I’ve seen some projects with bad and/or insufficient tests. I saw microservice-ish, big project in which integration tests took 20 hours to run, were flaky, brittle and not at all informative nor helpful. Unit tests were failing randomly and doing so many things that they become kinda integration tests. In Microservices world, a bug will bite tenfold. I can’t emphasise enough how important it is to have fast, clean and readable test harness. Only then I as a developer can commit my changes and be confident. So less stress.
2. Smoke tests
This is important but often neglected. When I spin up my new, shiny microservices I’d like to quickly get an answer if every major wiring and connections or services are up. I don’t want to chase the rabbit only to find out that one of my services did not run up or I’m pointing to wrong database. Smoke tests should not be complicated - just enough to test one or two happy paths or even just that some services respond to health checks and have expected versions. This can be a massive timesaver.
3. You build it - you run it
This is a motto invented in Amazon I guess. And this is special. We can think about it as going full DevOps. I had a pleasure to work in such work environenment in which we were driving our own changes through QA and staging into production by ourselves. Feeling pain points of deployment and monitoring allowed us to fix, tune and finally create something very much automated and just beautiful. I strongly recommend going this way. The only requirement for that is that this is probably for a bit more experienced teams.
4. Simplicity of local run and testing
If you can make your service easy to run locally with developer’s provided config you can be sure that it will be much thoroughly tested. I like to run my changes apart from soulless automated tests :). This point is hard to get because microservice does not live in vacuum - it requires other microservices, databases, cloud resources etc. If you can make mock-run config - go for it. It will pay back. If mocks are not feasible then maybe you can spin up other services by some elaborate script? And use development versions of external services. Also, Docker can be so useful here.
5. Simplicity and speed of deployment
So we have new change and it is tested locally…we want to deploy to QA or staging environment so other team members can check it. If this deployment is automated, you can make it from branch, it is easy to revert, do blue/green or canary release…then it will be done often and time won’t be wasted. Again - it brings confidence and reduces future stress.
6. Metrics, logging, monitoring
For us, developers, logging is especially important. Sometimes something will break. Can we get to error logs quickly? Are they aggregated? Or must we ssh into machines, one by one, maybe even through bastion hosts to grep our way out while trying to find something meaningful? Do we even have proper logs? Metrics and monitoring are usually the first sign that something is wrong. Having proper dashboards and alerts allows us to react fast and drill down into problem location with minimal time wasted. We, at Pattern Match, can proudly say that we mastered management of these 3 things and it always puts us ahead of any serious issues that may arise on production. And guess what…it makes our lives less stressful as well :)
7. Do one thing and do it well
Concise, coherent Microservice is a pleasure to work with. If I can’t grasp what Microservice does and how it does it in a couple of hours then I’m not very happy. For me this is a measurement of good Microservice size. I have found out that I’m much more productive, can jump between Microservices more quickly and produce better code when Microservice has such size. And I can work with dozens or maybe even hundreds of them at once. They became like these very likeable Unix/Linux command line tools. And that’s why I like to have proper markdown README file in codebase of each Microservice with description and service justification.
8. Simple communication protocols, easily debuggable
Microservices need to talk to each other. A lot. If we can make communication protocol simple then we make our debugging and analysis simple. Simple. Typical REST with JSON is usually sufficient. If we want something more async, like a queue, we can go with RabbitMQ or ZeroMQ. Just make sure that all the logic happens at Microservice layer and not on some middleware component (that may as well be a leftover from some older darker times).
9. Communicate failures clearly and precisely, degrade gracefully
Let developers know what happened. It will matter even more on production/live system but during development it’s just again massive timesaver. Provide special development config run mode that is more verbose and helps with tracing.
10. Technical tools matched to problem (polyglot, polyglot persistence)
Different Microservices are responsible for different things. In my opinion we should match our tools to problem being solved. It concerns programming languages, frameworks, databases. As I’ve already mentioned, Microservices are best suited for more experienced teams and such teams should have no problems switching techs, perhaps multiple times a day. Let the tech work for you instead of seeing each problem as a nail. Problem solved in something that suits it is simpler, clear to understand, to maintain and iterate.
Doing Microservices well can be very challenging. We, the developers, should sharpen up our tools and strive to get the best possible environment as we possibly can to create solid foundation for tackling this complexity. If you follow up these 10 guidelines I can assure you that development will be fun and stress minimized. The outcome of such environment? I hope that it will be nice Microservice ecosystem which can much easier reach and keep higher level values mentioned in introduction. Thanks for reading and happy coding!
We can divide and conquer your monolith using Microservices
Are you considering Microservices in your project? Do you want to discuss with experienced engineers if your case can be solved by microservices? Don’t hesitate and drop us a line, it’s free!Schedule a call with our expert