Micro-services in a context of high complexity and low code quality
One of the most problematic things of a software developer is how to handle the increasing complexity of a given code base and the decreasing quality of it. Code which, when originally written, was readable and understandable, looks completely scary two years later, after different developers had to add features, while not having enough time to do as much refactoring, testing and documentation as they would like.
High quality code with low technical debt is something we all strive to deliver, but business constraints don't usually make this neither practical, nor possible. Deadlines and low budget make it difficult to ensure excellence in our daily work, and this, even when management is smart enough to think long and medium term, not very short term and to hire smart people and get out of their way.
I strongly believe that we can easily come with this increasing complexity and decreasing quality through the use of micro-services. Let me explain.
Object oriented programming, and more specifically interface-based programming is already doing a great job of isolating parts of the code base from each other, thus simplifying maintenance and reducing complexity. As a result, a developer who needs to work on a specific part of the code base can focus on a specific code block which interacts with other blocks through one or several interfaces. As soon as interfaces:
Are constrained enough, in other words do a great job of avoiding unclear edge cases with dubious behavior by documenting (preferably through code, for example through Code contracts in C#) all pre-conditions, post-conditions and invariants,
Don't exhibit the characteristics of leaky interfaces too much,
thinks work pretty well. In practice:
We sometimes have to change the interfaces; although, in a well-designed piece of software where the requirements don't change too much, such changes will be limited.
Interfaces are not constrained nor explicit in most languages. Not many languages have Code contracts, and even in C#, few projects actually use Code contracts, because they are difficult to implement.
Most interfaces leak.
Despite those issues, interface-based programming does more good than harm: it still allows to focus more than not on a very specific and small part of the code base, while not having to think too much about the outside code.
Unlike interfaces, public APIs go much deeper into the idea of constrained, isolated parts of the code base. The most important aspect is the way they handle changes in their public interface.
The public aspect means that as soon as the API is released, there is absolutely no way to change the interface. At least, you can't do any change you want. You can extend the interface with additional functionality, but you cannot change the functionality which already exists.
This formal approach is not that bad, since it makes the designers of the API think twice about the interface. In object oriented programming, an interface within a code base doesn't receive that much attention, because it is natural to think that such interface is easy to change later.
The separation between changing existent functionality and extending one is important as well. Some APIs are cleverly thought so that adding functionality can be done with ease.
Services (such as public APIs) present an additional benefit over the interfaces: they are deployed in isolation from their consumers. By this, I mean that if I have a storage service which is used by image sharing service, video sharing service and online document collaboration tool, I can host storage service on a local Windows Server for a while, then completely rewrite it and migrate it to a Linux-based cloud provider: both sharing services and the collaboration tool would be completely unaffected; their respective developers wouldn't even know that the storage service was completely rewritten and moved elsewhere.
And this is a huge, huge benefit. With interfaces, you see, you always have to keep in mind the environment. You can't radically shift to a different programming language or ecosystem, without affecting severely the components which interact with you through interfaces. With services, you can do this with ease.
Micro-services look promising when it comes to creating loosely coupled components forming a larger system. The care one should take in designing the public API and the isolation of every component which abstracts not only the actual implementation of the interface, but also its environment, makes micro-services a great tool for creating maintainable systems where different parts can be rewritten on demand with absolutely no effect on other parts of the system.
In a world where business constraints make it impossible to produce excellent code with practically no technical debt, micro-services might be the tool we need to prevent complexity and low quality from hunting our projects forever.