In Defence of Monoliths

The first Microservices talk I attended was a year and a half ago. My first reaction was “why is that something new?”. Then I realized it is already getting overhyped, so I listened to some more talks, read a bit more articles, so that I can have a good reason not to like the hype.

What are microservices is probably defined here or by Martin Fowler, or at any of the first google results for “microservices”. It is basically splitting up your functionality into separately deployable modules that communicate with each other in order to complete a business goal, and each microservice is limited to just a small, well-defined scope. Product purchasing is one microservice, user registration is another microservice, “current promotions” is another microservice, and so on. Or they can be even more fine-grained – that appears to be debatable.

And whenever I encounter a new “thing”, I try to answer the questions “what’s the point” and “does this apply to me”. And I’d like to point out that I’m not the person that rejects anything new, because things can be done “the old way”, but there’s a fine line between a good new technology/architecture, and hype. And besides, microservices is nothing new. I remember several years back when we had an application split into several parts that communicated with web services. When I joined, I refactored that into a single “monolith”, which improved response times by around 20%. It turned out we never needed the split.

And that’s why I’m going to write about – that you probably don’t need microservices. And Martin Fowler has phrased this very well:

…don’t even consider microservices unless you have a system that’s too complex to manage as a monolith. The majority of software systems should be built as a single monolithic application. Do pay attention to good modularity within that monolith, but don’t try to separate it into separate services

There, done. Now go build a monolith. But microservices advocates wouldn’t agree and will point out all sort of benefits of a microservices architecture (or will point out that your system is too complex, so you have to use microservices. And pay them for consultancy). So let’s examine a few alleged advantages that microservices have (for example around 30:57 of this video). The question I’m going to ask is – can this easily be done in a monolith? (I have to clarify, that a “monolith” is what is generally perceived as the opposite of microservices – e.g. one codebase, one deployment.)

  • modeled around the business domain – absolutely. You can structure your packages and runtime dependencies around the business domain.
  • culture of automation – that has nothing to do with the architecture – you can automate the deployment of any application. (We are doing an automated blue-green deployment for a monolith, for example).
  • hide implementation details – that’s what object-oriented programming is about. Your classes, and your packages, hide their implementation details and expose interfaces. Microservices bring nothing to that (except the network overhead). You can even still have multiple projects, built as dependencies for the main project.
  • decentralize all things – well, the services are still logically coupled, no matter how you split them. One depends on the other. In that sense, “dcentralized” is just a thing that sounds good, but in practice means nothing in this particular context. And is maybe synonymous with the next point.
  • deployed independently, and monitored independently. That alone doesn’t give you anything over a monolith, where you can gather metrics (e.g. with statsd) or get profiling and performance information about each class or package.
  • isolated failures – now that’s potentially a good thing. If one module “fails”, you can just display “sorry, this functionality doesn’t work right now” and handle the failure. A monolith, however, doesn’t have to fail completely either. It is the details of the failure that matter, and I’ve never seen any detailed explanation. A server goes down? Well, you have a highly-available cluster for that, regardless of how your code is structured.

Some more, loosely defined benefits like “easy to modify” and “easy to understand” are claimed. But again, a well written, structured and tested monolith can be as easy to understand and modify.

Basically, a lot of commons sense, software engineering, continuous integration/delivery and infrastructure management best practices are claimed to be a bonus of microservices, while in fact they work perfectly fine with a monolith.

The ability for a graceful degradation is possibly an important aspect of microservices, but again, you can handle it with a monolith as well – it would require a little bit of extra code – e.g. feature if’s that are toggled in case of failures. But it’s nothing compared to the extra effort you have to put in place in order to get a working microservices application.

And that’s a lot – you have to coordinate your services. You have to decide what to do with common data. And the usual suggestion is “duplicate it”. If two microservices need some common data, they can’t just use the same shared database – each microservices has its own database, so it has to duplicate the data. Which sounds easy, unless you have to keep that data in sync. Which you always have to do. And when you have to keep duplicated data in sync accross 5 microservices, the overhead possibly exceeds any possible advantages.

Another big problem are transactions. You either don’t need transactions (in which case – lucky you), or you end up with (if I may quote Martin Kleppmann) “ad-hoc, informally-specified, bug-ridden slow implementation of half of transactions” (here’s the whole talk about transactions).

The microservices communication overhead is also not to be underestimated, including the network overhead and all the serialization and deserialization. And my controversial advice to use a fast, binary format for internal communication, rather than JSON/XML, is rarely seen in practice.

So, I would again recommend to follow Martin Fowler’s advice and just stay with a monolith. Do follow best practices, of course, including:

  • Modularity – separate your logic into well defined modules (will be easier with project jigsaw), define your class public interfaces carefully, and use loose coupling within your codebase
  • Continous delivery and automation – automate everything, deploy often
  • Be highly available – make your application horizontally scalable, be as stateless as possible, and make failures undetectable by end users

But don’t believe when someone tells you these best practices are features of the microservices architecture. They aren’t, and can be done pretty easily in a monolith, without the side effects – you don’t have to think about keeping duplicated data in sync, about network overhead, about writing a half-assed two-phase commit mechanism, about coordinating the services, etc. And if you really think you have to do “microservices”, be sure to have pretty good reasons for it.

P.S. I gave a talk on the topic, here are the slides

10 thoughts on “In Defence of Monoliths”

  1. Well, this simply isn’t true – “If two microservices need some common data, they can’t just use the same shared database – each microservices has its own database, so it has to duplicate the data.”

    You can still do with a HA database cluster.

    There are a few advantages of such an architecture vs monolithic one though:
    # In terms of monitoring, you can quickly identify paths of failure by just looking at which microservices fail
    # You can have automatic quick recovery by destroying failed services and spawning new instances of the same services
    # When tens or hundreds of developers are working on the same project, bad code is isolated in a micro-service which can quickly be re-written if need be
    # You can abstract yourself from underlying technologies

    As to the culture of automation – when dealing with so many services you have no other option but to automate. You’re not as strongly enforced to automate everything when building monoliths. True, you can still do. It’s rather the state of mind which the architecture enforces on the implementors.

  2. Hi, Stefan. I Actually agree on this one with Bozho.

    The problem here regarding what you said about that you could have a HA databse cluster with two or more microservices pointing to the same database is that it doesn’t matter if it is a HA databse cluster or just one database. The problem is that if two or more microservices point to the same (shared) database that would introduce a dependency there, which won’t allow those microservices to evolve independently from each other.

    That’s why the practice in microservices is that each one of them should have its own database, so that any change won’t affect other services. By doing this, you also introduce duplication if those services depend on the same or similar data model.

    IMHO, Microservices are good for very specific scenarios and they should be applied with very good reasons. But it is far from being that holy grail in the architectural world.

  3. For your average payroll application project, Microservice architecture is of limited benefit and could only bring some unnecessary complexities in terms of deployment and code maintenance and additional runtime overhead.

    But when the complexity of an application grows beyond a certain extent, there are some clear benefits. Though you can separate different domain boundaries using different modules, in practice one can easily break those boundaries.

    Another important aspect is scaling of individual services. In a monolith, usually you can scale only the whole application whereas in microservice world, you have more freedom to choose the scalability requirements on a per service basis.

    On top of all these, you have the “team owns the service” pattern where you have one microservice team taking care of the operational aspects as well.

  4. You missed one huge driver of this process. The payment models of the cloud providers. If only a small part of your monolith really needs to scale then microservices may make more sense. Do you stand up another HUGE server or just a couple small cheap ones?

  5. I tend to agree that it is a huge overstatement to say things like “the age of the monolithic application is over” and that it isn’t the solution for all problems. That said, I can imagine one additional point that would support the micro-services architecture that hasn’t been mentioned. In really, really large monolithic applications, even the ones that have correctly abstracted structures and interfaces, there is a tendency over time for developers to cheat. This is the developers’ version of chaos theory: in a monolithic application, developers will eventually find a way to break the design. APIs will be bypassed, new object relationships will be forced, and support/maintainability goes way down. This is partially due to the fact that a monolithic application is one large codebase and there are always ways to force a reference to a restricted object in the same VM or use an implementation instead of an interface. A micro-service should make this impossible. It is a different VM and the only interaction is via an API so in practice it should be cleaner and more compartmentalized, easier to replace, and easier to upgrade (can upgrade dependencies individually if needed).

  6. Hi,

    You’re article is reasonably well articulated in the defense of monoliths. But I think to take it seriously you need to tackle a couple of key points:

    1) Versioning. Service versioning has always been hard. For me, microservices make this MUCH easier.

    2) Coupling – You’ve implied coupling of microservices but in my mind, this is microservices done wrong. They should be autonomous and independent, that’s the value of SOA. With this core ‘coupling’ inference removed, a lot of the value of your article loses face.

    I’d love for you to rewrite this article with the previous points in mind because its a great conversation starter to challenging some of these emerging architectural fads and make people think about what really matters. At the end of the day, you shouldn’t make an architectural decision based on a fad, but on whats best for you software ecosystem.

    Keep up the good work.

  7. First, thanks for a great article, as I’m used when I come here.

    For the discussion, in my opinion, it really depends on what you call a monolith.

    Is that OK if it is your monolith that authenticates the users (and stores the user passwords, obviously with security failures) ?
    Is that OK if it is your monolith that displays logs in an admin console ?
    Is that OK if it is your monolith that sends mails and track if they were delivered to the end user ?
    Is that OK if it is your monolith that handles job queues ?
    Is that OK if it is your monolith that manages analytics ?
    etc ….

    To me, these points really highlight what is a monolith : a business service bloated with more or less technical responsibilities it should never have in the first place.

    With all the (good) practices you recommend, replacing a component of a monolith with a “microservice” that does the same job should be easy (and when this microservice is not maintained by you, the better)… but in my experience a monolith is never written with all these good practices.

    Without going “all in” for microservices, I think that the services you write today should only be business focused. The moment you are doing something “technical”, you should use a “microservice”.

  8. That’s a good way to look at it. Yes, some of these – analytics, log collection, complex email management, should probably be off-the-shelf solutions that you just install. But it again depends. For example, user authentication might very well be within the application itself, same goes for jobs. Sometimes many 3rd party software to maintain may mean more integration points and more bugs. For example that seems the wrong thing to do: https://github.com/fluxcapacitor/pipeline/wiki 🙂

  9. About auth and jobs, I used to think like you but I now have change my mind as modern solutions are quite easy to manage, way order of magnitude below developing it yourself…

    But for sure it depends on what you are doing

Leave a Reply

Your email address will not be published.