This month has been an active one in debating the merits of microservices vs monoliths. The debate has mostly revolved around three perspectives:

Start with a monolith, move to microservices as needed. Start with a monolith, stay as a monolith. Start with microservices.

Martin Fowler has written a few times about the subject, including MonolithFirst and the MicroservicePremium. On Fowler's site, Stefan Tilkov has defended starting with microservices. Etsy is well known for defending start-monolith-and-stay-monolith approach, with Facebook cited as an example of a successful, large scale, monolith.

In general, all participants seem to agree on one thing: code being modular is important disirregardless of if a system is a monolith or composed of microservices. The Monolith First faction believes that the overhead of microservices gets in the way of delivering value. A good idea could be DOA because one has spent too much time orchestrating microservices. The Microservices First crowd believes that it's too hard for most developers to not create a big mess without the physical barriers of machines stopping them.

The problem with these perspectives is not that they might be wrong, but that they are too low resolution. Instead of examining what about these different methodologies worked, and why, they see that some percentage of their observations fit into their definition of success and take the whole thing. For example, in Stefan's article that suggests Microservices First, he says:

I strongly believe – and experience from a number of our recent projects confirms this – that when you start out, you should think about the subsystems you build, and build them as independently of each other as possible.

Stefan's observation is that building independent components is important. It actually says nothing about microservices. It's about building loosely-coupled components. Microservices, and everything that comes with them, happen to be how he believes people will be most successful accomplishing that.

The strength of microservices is the forced modularity, one can only call exposed APIs when the code is running in another process on another machine. The weaknesses are that it requires a significant infrastructure investment in orchestration, deployment, testing, repositories, etc.

Rather than having to pick one or the other, another option is the Microlith. With a Microlith, all of the code is in one executable, possibly one repository. However, the services themselves are not aware of this so they cannot depend on being tightly-coupled to other services. This means that services will still communicate with each other through, perhaps, a socket even when in the same process but this is a small cost for most systems.

One variation of how to achieve this is with three components:

A service that exposes a start function. The start function will take a configuration as input. A configuration file, this specifies the configuration for every service the user might run with the executable. A framework that allows services to register their start function with it, parses the configuration file, and starts services that the user specifies.

All good software comes down to the taste of those developing it, but the Microlith allows one to more easily reap the benefits of microservices but without the infrastructure cost.

By having all services compiled into a single executable with a single config file:

Orchestration is equally difficult to orchestrating a monolith.

Deployment is publishing one executable and a config file.

Testing is simpler, starting a system is done by forging the proper config file and running the appropriate list of services. One can test every variation from all services running in separate processes to all running in one process just with one executable.

As the system grows, splitting the system into microservices is easier, the code is already done one just has to stop running all services in one place.

The final point is the most important, as the goal is to make the initial development simpler, like a monolith, but be able to easily transition into services when the system has outgrown running as a monolith. The Microlith becomes the fourth option in the list of ways to approach designing a system: start with a microlith, maybe end with a microlith.

This framework will not suit all use cases, but its strength is that it extracts what is good from each extreme into a unified framework. By leveraging the build system, how to ensure loose-coupling between services is clear by simply not having a service in the list of dependencies for another service. Keeping the code modular can become a test rather than a matter of taste. The Microlith provides a clear mental model for how to maintain modularity with a minimal infrastructure investment.