Microservices: Rule of Twos
In a recent post we discussed how Microservice Architecture is fundamentally all about avoiding need for coordination and that all of its other often-cited principles are essentially derivatives of this fundamental goal. One such important, Microservices principle is: avoiding shared libraries and binary coupling. If you haven't already, you should watch Ben Christensen's wonderful talk on the subject.
Early in Ben's talk you will notice that while he is discussing a specific problem he is fundamentally addressing the same "avoiding coordination" issue, in context. Ben aptly explains that a decision of sharing binary libraries, frameworks, or platforms can lead to a gargantuan challenge when you eventually need to upgrade or replace that library in just one part of the product or organization. A task that should take a day or a short scrum cycle, at most, often turns into a multiple-months (sometimes: years) long project. As a matter of fact, such efforts are often entirely cancelled when stakeholders realize the magnitude of implications.
It's easy to follow the logic Ben explains and to buy into it. However, it can be much harder to come to terms with its seemingly "devastating" implications. Is sharing of libraries across microservices indeed evil? If it is then how about the "reuse of code" being one of the most efficient practices in all of software engineering? What about leveraging open-source? Seriously, how many closely-held engineering principles is Microservice Architecture going to take a swing at? Is this rebellious architectural style even worth it, if it's going to fight every. single. principle. that us, software engineers have come to appreciate, for decades?
Well, let's pause for a second, before we reject Microservices and start "hating" it, or before we throw away everything we believed in, prior to Microservices. The reality is: Microservice Architecture is not against sharing of libraries per se. What it actually is against, is creating death-grip dependencies that would compromise our ability to practice the principle of Avoiding Coordination. While sharing of libraries, can often lead to significant challenges – it doesn't have to. And sharing of libraries is not the only way to compromise Avoidance of Coordination, either. You can just as easily create costly lock-ins by choosing single media type (e.g. JSON-LD, HAL, Siren or UBER) for all your APIs. Or you can choose a single way of accomplishing fault-tolerance that can lead to the same implications. Examples are too many.
Fortunately, there's a fairly simple principle, and some team discipline that goes with it, which can be exercised in implementing Microservice Architecture that can avoid all kinds of dependency lock-ins, including the one described by Ben. This principle was inspired by many conversations with my dear friend and renown expert of distributed systems: Mike Amundsen. We have used this principle, in practice, very successfully at ReferWell – a healthcare technology startup, where we have implemented an innovative physician-facing software product using APIs First approach and Microservice Architecture. I lovingly call this principle: Rule of Twos and have come to appreciate it a great deal.
Rule of Twos
The basic idea is that your choices won't lock you-in if you constantly exercise your ability of supporting multiple choices:
For any critical component in your system, make sure you use at least two alternatives, in production, at the same time – even when you only need one. Also make sure that you have infrastructure to support the two alternatives as easily as you would use single one.
In practical terms, this leads to example guidances such as:
Make sure some of your critical APIs are written in Go or Scala, even if most of them are written in Node and you are exerting huge organizational efficiencies by standardising on a single, efficient, common programming language.
Make sure some of your microservices use a document or NoSQL database, even if most of them use traditional, relational one. In making this happen, you will develop capabilities that will be invaluable for you, in the long-term.
Make sure some of your critical APIs are communicating with a media type that is different from the one you use everywhere, e.g. if you standardize on HAL, use UBER for some other, important ones. In making such multiple-message-format support happen you may end-up implementing a powerful design pattern such as: Representor that will make your APIs long-lasting, evolveable and adaptable.
I won't bore you with more examples. I know you can find a lot of them in your own contexts, but the main point I would like to communicate is: sharing of libraries or standardizing on other organizational choices is not always and necessarily toxic. The dangerous part is not having a flexible infrastructure to accommodate any non-standard alternatives. Most importantly – you will not be able to support such alternatives unless you practice your openness to choices every single day!
This is where Rule of Twos comes in: even if you don't need alternatives today, do implement examples of them today, and tomorrow when you actually need it - you will be free to exercise free choice. Always implement critical decisions as two alternatives, from day one, and lay down infrastructure to seamlessly support them - that is the essence of the Rule of Twos.