Modular maximalists vs. service maximalists. On humourous path dependent conflations. And how they lead to repository explosions.
Here, a semi-authentic quote that definitely isn’t from me:
I don’t like mono-repos, because they are bad for modularity and micro-services are better.
The above encapsulates some common misconceptions, conflations and outright abusive (intelectual) shortcuts I’ve witnessed. It would probably not be so funny if it wasn’t also representative of some common thinking.
To be fair, the tech universe isn’t exactly trivial: it’s easy to think you understand something while being (at least partly?) wrong. If you’re wondering if it happens to me too, the answer is: Yes, Absolutely! Let’s point out to each other the planks we have in our eyes and learn out of it.
The Cast
- A Repository
- Modules (or not)
- Application(s) and Service(s)
A repository is basically a database. It provides you with a namespace where you can store blobs, which we’ll call files, and it happens to be good at versioning the content of said blobs.
Modules are (hopefully) small units of logic dedicated to solving a particular problem. Modules can be composed of smaller modules.
An application or service is something that you can run, which solves some kind of problem or answers some types of questions.
Astute observers may notice that a module and a service can be pretty similar. Emphasis added on can, because the fun really starts when someone mentally misfiles that possibility as a fact and assumes service === module.
Two Branches of a Plot
Once a service and a module are conflated together, interesting things happen depending on what you actually had in mind before the conflation: maybe you where a good developper never really concerned with services and focused solely on writing useful collections of small functions (what we’ll call a module here) to be reused by others.
On the other hand, you may have had a focus on services, not really caring about the implementation details but just providing useful applications to some users.
The minimalist view: a module is a service
Under this view, the (perfectly valid) observation that maximum modularity (at least in code) is good is translated in an almost one-to-one fashion to the service world.
A modular maximalist who is tasked with creating services may thus elect to make a lot of small services in the exact same spirit as more small functions beat a huge one.
This works well in theory before reality suddenly reminds you of network latencies, (de)serialisation costs, increased debugging difficulties and possibly difficult concurrency problems.1
To such a person, a mono-repo may seem frightening because how could you possibly manage modules independently and devs will be lazy and just write fat modules! This generally yields a collection of smaller repositories dedicated to individual modules, each wrapped in a service.
The maximalist view: a service is a module
Technically, a service is indeed a module. Though it’s probably composed of multiple sub-modules. Anyone too focused on just getting a service to work may (involuntarily or not) elect to ignore this.
Thus, a service maximalist who is exclusively focused on getting something that works out of the door may completely forget that code may actually be split into various reusable pieces, even though it ends up being shipped as a single unit.
Here, echoes of a monorepo may trigger the fear of how will we have multiple service in a single repository?!, and we end up with a repository per service.
The Conclusion
As disclaimed in my profession of faith, I’m a mono- or fat-repo fanboy, so without any regard for their other shortcommings, the (slightly exagerated) tendencies described above dissatisfy me intensely, because:
- they needlessly tend to increase the number of repositories
- they implicitly exclude the usage of fat- or mono-repositories for no good reason2
So, just to drive the point home:
- A single repository may host multiple services as well as multiple modules.
- A single module may be reused by multiple services.
The discussion around (micro-) services should not forcibly be bound to a single/multi-repo decision, and any argument that blindly ties together things as different as a repository, modules and services should not have any weight.
End
Services and modules always invoke a balancing problem. Here are my two cents on the subject:
It’s wise to minimise the number of services while maximising the number of modules. Ie, the code should be modular. The services probably should optimise for things that are more important than modularity (in the higher level context were the services run).
Finally, an (admittedly opinionated) conclusion:
A Monorepo ultimately encourages better modularity3, and is a good fit regardless of the number of services you build, unless you have specific reasons that make a fat-repo unpractical for you.
Follow-Up
14.6.22: You might be interested in the shape matters post, which is a follow-up to the present one.
-
I’m not advocating against having multiple services (irrespective of their size), but generally recommend not doing so only because you can… ↩︎
-
Because yes, there actually also are reasons not to put everything in a single repo. ↩︎
-
Essentially because the cost of reorganising things is much lower. More details on this another day. ↩︎