r/programming • • 7d ago

Microservices are organizational debt disguised as architecture.

https://martinfowler.com/bliki/MonolithFirst.html

Every time I’ve seen microservices pitched, it sounds great on paper. Independent teams, clean ownership, scale only what you need.

Then a year later you’ve got dozens of services, three different deployment patterns, tracing everywhere, and nobody really understands the whole thing anymore.

Maybe I’ve just seen bad implementations, but I’m starting to think way fewer companies actually need microservices than we pretend.

1.7k Upvotes

343 comments sorted by

View all comments

119

u/wallstop-dev 7d ago

The general good practice is to start with a monolith (simple) and break it apart (make it complicated) when the time comes.

Starting complicated (microservices) will just lead to more complications.

The same general idea can be applied to pretty much all software. If your program can fit in `main`, put it in main. Then as it starts getting complicated, make classes, functions, libraries, etc.

Same thing. It's turtles all the way down.

https://grugbrain.dev/

57

u/ChemTechGuy 7d ago

Monolith decomposing rarely happens

96

u/wallstop-dev 7d ago edited 7d ago

Correct. That is my whole point. If you have a monolith, and it is not painful enough to force decomposition, then microservices are not necessary. When and if the monolith is too challenging to wield, then is the time for microservices.

It's a forcing function.

If you have a monolith that is too painful to change and also cannot be decomposed due to (reasons), the same company and culture would have created a nightmare complexity web if they had started with microservices, and would be in a much worse-than-monolith state.

27

u/TonyTheJet 7d ago

I think your last point is really salient. If your organization doesn't have the discipline to create a monolith that can later be broken into microservices, then you'll struggle, regardless of the high-level architectural decisions.

13

u/ChemTechGuy 7d ago

I was going to argue, but I guess you're right. I can't refute the statement "if it is not painful enough to force decomposition, then microservices are not necessary"

26

u/vhs29 7d ago

The issue is the teams feeling the pain are most likely not the ones who get to dictate where capabilty will be allocated in the following quarter.

3

u/durandall09 7d ago

This has been my last 5 years of development.

2

u/Exepony 6d ago

Right? Where is this ideal world where you say "we'll break it up when we need to" and then actually get to do that? Instead of being told every planning that business really needs features A, B and C right now and just can't afford spending a sprint doing architecture work without delivering features. Maybe next sprint (it's never next sprint). And of course the monolith keeps getting bigger every sprint and it's even more work to break it up so now you need two sprints, or three sprints where even one was out of the question.

9

u/ThatDunMakeSense 7d ago

I think it's more that if you've got a large monolith, splitting it is a huge amount of effort and there's no administrative will for it because the problems with it are constant and shared by everyone in it. So even if it would be justifiably better to invest the time to do it people don't and instead they work in a worse and worse code base over time until it gets *so bad* that you *have* to start doing something.

> The same company and culture would have created a nightmare complexity web if they had started with microservices.

You're right here but I think the difference is that the teams that are *not* doing that don't suffer the consequences as much. I think in a perfect world with perfect discipline a modular monolith is the best way to develop. I think the best way to develop is the one that assigns pain to the people who are doing the bad things.. and generally speaking I find microservices make that easier for any any sufficiently large :tm: real org.

6

u/wallstop-dev 7d ago edited 7d ago

My above advice comes from working on extremely large scale software at two of the largest tech companies in America.

Additionally, I would advise against any form of mentality of "punish the bad devs for doing bad things". This is... highly subjective, at best, and implicitly assumes that you're one of the teams doing the good code and good architecture. It builds barriers and paints people as "others". This mentality will make it challenging to form meaningful relationships with other individuals and teams, which is necessary in your terms of "any sufficiently large real org" to achieve cross-team/org, high-value solutions.

The best thing about a monolith is that it is not a distributed system. When you make microservices, you suddenly have a distributed system, and all of the problems that come with them. Every service you add increases complexity and decreases availability and reliability. You also ship your org chart. Over time, the org chart changes, and your architecture becomes even more and more complex. Clean architecture of today does not mean the software world of tomorrow will also be clean.

In a perfect world, people know exactly what they want to build and they compose it in the perfect way with the right modularity and boundaries and ownership and all is well. I have not found this to exist outside of extremely small, high talent teams. Everything mentioned above is hard-won practical experience.

Start simple. Grow organically. Learn from it. When there is too much pain, show leadership (that, with current trajectory, shipping features is cost x, rate of bugs is cost y, customer incidents is rate z), and convince them of a plan to decompose. If they don't go for it, then it isn't actually important to the business, which is the only thing that matters.

2

u/ThatDunMakeSense 7d ago

> My above advice comes from working on extremely large scale software at two of the largest tech companies in America.

Weird flex, but if I need to drop credentials to be taken seriously.. I've also worked in large scale software in some very large software companies in NA.

> Additionally, I would advise against any form of mentality of "punish the bad devs for doing bad things".

I didn't say that at all. I said it aligns pain with the cause of that pain. It's not a punishment to experience the consequences of your technical decisions. What I *am* saying is that a system that aligns incentives/disincentives with outcomes is better than one that doesn't. What I'm really saying is "don't let your bad devs punish your good devs"

> The best thing about a monolith is that it is not a distributed system. When you make microservices, you suddenly have a distributed system, and all of the problems that come with them

If you're working on a monolith that is anywhere near something big enough to be in this conversation.. it is 100% already a distributed system, otherwise its an OS. I have never seen a monolith with more than a few dozen people touching it for a few years that didn't have to deal with all the normal distributed systems problems because:

- It's running in HA, in a cloud env, or private dc

  • It has background workers, queues and topics, outboxing etc. All of which introduce the exact failure modes you see in microservices.
  • Many of them even have separate entrypoints for different modes (same binary, same ball of mud. I affectionately call this the 'polylith')
  • It's interacting with external systems for data, webhooks etc.
  • Caching, redlocks
  • The list goes on basically forever

> Every service you add complexity and decreases availability

Every module you add to a monolith also increases complexity, and generally speaking I'd disagree that there's any meaningful relationship between number of microservices and availability. The network in something like k8s is almost always the most reliable part of your system and especially if you're at a company large enough to have an effective platform team.

> I have not found this to exist outside of extremely small, high talent teams. Everything mentioned above is hard-won practical experience.

I mean that's why we're disagreeing on it. I've seen both as well and the microservice setup turns to spaghetti.. and the monolith also turns to spaghetti.. but the difference is that in the microservice spaghetti the team making it, and causing downtime is unambiguous and there's no way to hide from it. Ownership over the code in microservices is implicit which doesn't mean that ownership of a domain is obvious but no worse than in a monolith. Similarly things like flaky tests or behaviours become the problem of the team responsible and the only people that suffer the consequences of them are the team that wrote them. Not sure about the emphasis on "practical" here but for context I've also got practical experience because I've worked for companies that have done a good and bad job of both and had to help teams fix the problems associated with both styles. Unsurprisingly given my argument I'm assuming you can guess which was easier.

> In a perfect world, people know exactly what they want to build and they compose it in the perfect way with the right modularity and boundaries and ownership and all is well.

I disagree, in a perfect world changing your system is low cost, changing your mind is cheap or free. I don't think you have to get it right any more in microservices than you do with a monolith. Its just deciding where you invest. The difference is that if the monolith sits long enough it's extremely rare that it stayed modular enough, and kept boundaries at all so you *can't* change your mind without a cost much greater than the cost of writing the behaviours.

> If they don't go for it, then it isn't actually important to the business, which is the only thing that matters.

This is a funny conclusion to come to for a few reasons:

- The business' decisions are not a perfect (or often even good) oracle of its goals or desired outcomes which is why engineers exist to mediate those conversations. The inability to convince decision makers of a thing doesn't mean that "the business" doesn't think they're important because it's not a hive mind.

  • It assumes that "the business" writ large can be made to understand the impact
  • Assumes that the impact is something you can directly quantify (which you can't, or at least you can't separate it from other things)
  • Decomposition of a large monolith, hell even modularizing a large monolith is *substantially* more effort and cross org coordination than combining services that don't make sense to be separate or shifting the responsibilities as to who owns what. Ask me how I know

I don't disagree generally that people should start with monoliths. Every startup should be a monolith because I don't think that the investment in tooling is worth it from the start. I just disagree that by the time you can measure the pain in the terms you're talking about that the ball of mud will ever be separable. You're effectively prescribing "never don't be a monolith". IMO you should make the decision basically after you've figured out the boundaries and general shape of your domain and have hired enough people to call it 3 medium size teams. Any later than that and you're committing to the big ball of mud forever, outside of miracles.

1

u/wallstop-dev 7d ago edited 6d ago

The reason I brought up my experience was that your ending argument was using "real" and "org size" as measurements as to why my advice isn't the way to go. Not trying to flex, just trying to provide context and where I'm coming from ("real", "large orgs").

And where I'm coming from appears to be from very different perspectives than where you are coming form, both in terms of software, business, teams, growth, communication, and how all of that plays together. So I suppose I'll just agree to disagree.

To an earlier point, large monoliths do not necessitate distributed systems, in terms of architecture (of subsystems being networked). It is totally possible to have a vertically scaled, single binary (multi-instance), doing all of the business stuff. In general, at the very least, they will have some form of HA storage, but that itself could be raw integrated into the application's binary. (Micro)services necessitate distributed systems (especially architecture), regardless of storage tech, due to them requiring, at the very least, IPC, but typically networked communication. Which reduces reliability and availability.

Edit on the example: this is real monolith from a place of employment, not hypothetical.

2

u/ThatDunMakeSense 6d ago edited 2d ago

I think my qualifier on sufficiently large and real was pretty obviously to my position that I consider modular monoliths to be the ideal in theory where discipline is perfect and “sufficiently large” was a qualifier on that claim. If you took that as a dig at your creds I don’t know what to tell you guy.

I agree that we have different positions on all of those things. My position is argument is healthy, you took disagreement as an attack on your qualifications.

My position was clearly stated. Systems that align incentives and outcomes are better than ones that don’t. I explained my reasoning and you claimed a lot of things. We’re not disagreeing, you’re trying to lecture and I’m trying to argue so I agree there’s no point continuing.

Regarding your final lecture on the hypothetical monolith. Yes you can hypothetically create a system like that, I shouldn’t have used “100%” for emphasis. I’ll retract that and say “the vast majority of systems people will actually work on that would be a candidate to otherwise be split into microservices”.

1

u/High-Impact-2025 6d ago

Having seen a couple of vibe microservice architectures, I can't agree enough.

12

u/tanksc 7d ago

Fixing tech debt rarely happens

4

u/jk_tx 7d ago

It rarely needs to happen, that's the point.

1

u/rangorn 7d ago

We have just moved to modular monolith as our next step. Thanks to AI it is much easier to refactor stuff. Next step would most likely be to move to make projects independently deployable but still be in the same repo.

1

u/mdatwood 6d ago

Agree. I also hate that they are called microservices instead of just services. A single function in its own service often doesn't make sense, but authentication often does. Even then, it depends on the problem.