Hacker News new | ask | show | jobs
by hooby 905 days ago
Reading the responses/comments in here, a question arises...

Are things really that black and white as people here paint them - with monoliths unavoidably and necessarily becoming a tangled mess of spaghetti, and micro-services being the singular, only way to achieve clean separation, autonomy and partition? With microservices inevitably being an organizational nightmare that leads to problems in inter-team coordination, while monoliths automatically ensure that everyone always is perfectly the same page?

Because by my personal experience, you totally can code a monolith in a strictly modular fashion, with clean interface-based API boundaries between the separate parts. And if you put those "modules" into separate packages, the dependencies between them end up being managed and documented as well. Just like you totally can setup microservices and surrounding procedures in a way that actually increases the feeling of product ownership in the teams, and reduces the hassle of inter-team coordination - especially in larger companies.

Obviously there are ways to do monoliths right, and spectacularly wrong. And there are ways to do microservices right and wrong...

I see so many arguments here, that do compare one method done wrong with the other method done right - and then imply that this is a strong reason for choosing that second method.

I don't get it?

13 comments

Perhaps it is easier to understand when you remember that service is not a technical term? People provide service.

In the macro economy, service comes from other companies. If you integrate an LLM into your application, you may use the services of OpenAI. If you integrate payment processing into your application, you may use the services of Stripe. Microservices are just like services, except offered within a micro economy (think a single business).

In other words, it's a team separation technique. You let teams within a business organize as if they were separate businesses and let them offer services to each other as if they were operating different businesses.

How those teams design their software is ultimately irrelevant. The key aspect is keeping active communication between teams to a minimum, using published documentation and API contracts as the mode of communication between teams, just as we do on the macro scale. Getting another team on the phone should be as hard as getting Google on the phone – i.e. basically impossible, but also basically unnecessary.

This is done in large organizations to ensure that developers aren't forever bogged down in meetings. If you have 10,000 developers all trying to work on the same project the communication overhead will kill you. Microservices is a way to try to overcome that. Of course, it serves no purpose in a small company where an economy can't reasonably grow anyway.

HOW DO COMMITTEES INVENT? By MELVIN E. CONWAY

Walkthrough

https://youtu.be/5IUj1EZwpJY?t=688

Paper

https://www.melconway.com/Home/pdf/committees.pdf

Bartosz Milewski's take on the topic of decomposing complexity

Category Theory 1.1: Motivation and Philosophy

https://youtu.be/I8LbkfSSR58?list=PLbgaMIhjbmEnaH_LTkxLI7FMa...

With no or minimal communication how is it possible to build anything new? In real world if you need to build something new and it requires 10 different companies to do that, it would be virtually impossible.
Punctuated equilibrium.

A lot of customer value comes from companies that assemble ideas that have existed in isolation. Customers think they hate that, but the numbers say they are wrong.

Foods are an early example. Most are rearranging the same dozen ingredients in different quantities and orders of operation.

Vertical integration is not an all or nothing proposition. Companies can make pretty unique solutions by specializing a couple of pieces.

The same way the economy has always built something new. We have thousands of years of experience in doing that.

You don’t try to build something new with 10,000 developers, if that’s where you have become confused.

I think the confusion is because "something new" isn't necessarily a completely new, disconnected product. It could be something like adding multi-lingual support to an existing product. In that case, you often do need to coordinate across many teams to get the new feature implemented, which can be a huge pain in a micro-service architecture.
No more than it is a pain in a service architecture – being the exactly same thing, just in a different economy. As always, if a service doesn't do what you need, you build your own in-house. Just because there is a product on the market is that is kind of, sort of, but not really, what you need doesn't mean you must use it.
> You let teams within a business organize as if they were separate businesses and let them offer services to each other as if they were operating different businesses.

> This is done in large organizations to ensure that developers aren't forever bogged down in meetings. If you have 10,000 developers all trying to work on the same project the communication overhead will kill you. Microservices is a way to try to overcome that.

Microservices do work better in large organisations, but I don't think the boundary should be defined by teams, because large orgs perform re-orgs more often than you'd expect.

It really should be defined by product. When a company is reorganised, then you'd be passing around products and product groups and not individual microservices, to the new teams.

I'm not sure a team is defined by individuals who may come and go. Look at professional sports teams that have been around for a long time. None of the people involved in the team today are the same people who were there in the beginning. The people don't define the team.

I expect you will find that service, product, and team all mean approximately the same thing in this context.

A lot of factors come together whenever the term Microservie or Monolith arises:

- Monolith has implicitly replaced the word "Legacy" system

- Microservices were (/are still) the cool thing to do in a conference driven industry. They were championed by big tech because they desire granular and flexible deployment systems (whereas in smaller products your code just needs to run and everything else is just overhead). Having multiple instances of a MS can fix some performance issues as well.

- People rarely talk about failing with their new (microservice) projects.

- Microservices can be a technical solutions for human problems. Large teams benefit from distributed deployments due to organizational reasons. They also force you to think more about modelling your responsibilities, which might be add odds with your company culture.

To me, Microservices are not an architecture pattern but a deployment strategy. Obviously, you want high cohesion and modules, which you might or might not get with Microservices. It comes down to understanding your problem domain.

If you have a modular monolith, you can still deploy it separately at a later point if the need arises. Even having a shared database is not a problem if your modules are only using the respective tables they own (this can be enforced with permissions).

> Monolith has implicitly replaced the word "Legacy" system

Indeed, one couldn't possibly recommend a Monolith in 2023/4. A macroservice on the other hand ...

Right. I'm off to polish my conference talk notes

That‘s why I think „monolith“ should only apply to the deployment strategy.

Building an application with „monolithic deployment“ is way closer to the truth than „building a monolith“.

> Because by my personal experience, you totally can code a monolith in a strictly modular fashion, with clean interface-based API boundaries between the separate parts.

This is my experience as well. In fact, that's exactly how we transformed a spaghetti mess monolith into something that was still a monolith, but clean and well abstracted. As things evolved, we even split some of the interfaces out into first-class web services that we hosted on different infrastructure. It was a near-trivial operation.

For me, adhering to Service Oriented Architecture has been the consistent them of keeping things manageable. Thinking about who "owns" data vs. who "uses" it, how data from these different domains is linked, and keeping things modular and logically distinct are the real keys. This applies on the whole spectrum from monolith to microservices, and makes for maintainable code where key functionality can easily be split out if needed.

I often have had the feeling that people tend to try to solve code-organisation issues with microservices. Sure you can code a tangled mess in a monolith, but adding TCP connections and Kubernetes to the problems you have probably won't make things better. Instead, rewriting some code to use whatever applicable design pattern probably will make it better.

An analogy that comes to mind is looking for a new house. Most people can't see through the current decoration and furniture of a house and imagine themselves living there in their own style. So real estate agents try to get rid of as many personal items and clutter.

It seems to me that many developers can't imagine how to refactor an existing messy monolith into a tidy monolith, but they can imagine an empty repository.

> Sure you can code a tangled mess in a monolith, but adding TCP connections and Kubernetes to the problems you have probably won't make things better.

Yes!

I call this "moving the spaghetti". That tangled mess in a monolith can easily be (and often is) replaced with an equally tangled mess in microservices. It's just that the mess will be in the connections between services rather than in the code itself.

I see the same thing in certain OOP camps -- removing the spaghetti code from the actual lines of code into the inheritance hierarchy instead.

It's much like sweeping the floor and leaving the dirt under the carpet. At first glance, it looks cleaner. But if you have to move the carpet, you see the truth.

My 2c: you can get things right, but most of the time you won't, for many reasons - technical, logistical, cultural or merely political. Sometimes you don't control these reasons.

So you are now left with managing risk. It's trade-offs all the way down.

> but most of the time you won't, for many reasons

This.

One thing that I noticed working in 3 different continents is that the learning/teaching on technology (CS?) is highly fragmented and it's quite hard to find some "common ground" in practices and methods and mesh all of that in a socio-technological place makes everything harder.

A weak generalization to illustrate the point: A [Continent A] developer will be more resource aware due to their natural lack of, an [Continent B] developer since it has more human capital available (better mentors, big ecosystem, huge amount of finance capital) will have more room for scaling ideas, a [Continent C] developer will have a better understanding of the intricacies due to their formal education and cultural aspects.

Placing all those people underneath a tech project and not having a program to terraform biases and level them to a shared and understandable set of expectations is the root cause of all this debacle.

> My 2c: you can get things right, but most of the time you won't

I'd argue you'll never get it right. You might get it right at the time, but 'right' will always change over a long enough timespan.

I think you're right to be skeptical that microservices are required in order to stop code becoming spaghetti.

I think of it as all about what kind of boundaries you choose to put in your code and where. https://hivekit.io/blog/mesolithic-architecture/ The blog calls it 'mesolithic' architecture, but I almost wonder if 'gabion' architecture might be better - separate small rocks held together by a metal frame, forming a single architectural piece.

The principles of good architecture do not require you to make your boundaries network or process boundaries, but they do require that you have boundaries and that you're careful when and how you cross them.

Bounded domains, as Evans calls them in DDD.

Indeed, how these domains communicate is rather irrelevant. It can be HTTP, injected objects, closures or functions, a message bus, an event store.

I've worked on a few actually good Rails apps (out of hundreds of abysmal to just meh ones). Some just put all he bounded domains in libraries (gems, railties), some by pure message passing, one through sheer discipline to not ever add that has_many that would cross a domain boundary.

Microservices are just one, very specific way to erect and enforce bounded domains. But there are many more.

I guess, though, that in practice, "monolith" means "lack of any decoupling or domain boundaries". At least, to many it apparently does.

So then there's still a large group of architectures that aren't exactly microservices (http communication, isolated runtimes and processes) but aren't monoliths either.

What would those be called?

To borrow some monolith examples from Monolith to Microservices (https://www.goodreads.com/en/book/show/44144499 - fantastic read)

"Single Process" - Can be module based (Shopify), where multiple module's interact, but must be combined for deployment still, these module's can even extend to the DB's. Can still have multiple instances for performance

"Distributed Monolith" - This is one to fear the most, we have multiple services, yet there is still shared DB's meaning we have to deploy things together.

Both are monoliths but one is less susceptible to ball of mud. It's a sliding scale, with trade offs for each (and many variations still in between those listed).

I think it's a lack of common terminology surrounding monolith / micro-service that creates this binary illusion.

In my opinion it's people problem. Lots of developers are focused on pure technicalities. They will focus on writing code and ignore why it's written.

As result of that their choice of architecture will be detached from business function. Also, as thinking about architecture isn't writing code it will be often afterthought based on shallow knowledge and superstitions.

Keeping that in mind - when you are talking to bigger team containing people with various seniority, it's often best to give them rigid set of rules, because they'll misinterpret or ignore something more complex.

And that's why black and white approach often works - it's far from ideal, but at least possible to actually execute in many of the teams.

But shouldn't it still be a rational decision?

The imho rational thing to do, would be to compare the pros and cons of each option and find a conclusion that way.

Comparing only the pros of one solution with only the cons of the other, seems a bit... disingenuous and an emotionally-driven line of argument.

And I personally just do not believe that making decisions with such wide-reaching implications as monolith or microservice based on emotional arguments, does lead to useful results?

The only thing that imho can come from this sort of arguing, is the kind of religious divide, evangelism and proselytism that ultimately only hurts both sides of the argument, because it makes people favor dogmatic choices over considering what's actually useful in the current situation?

>The only thing that imho can come from this sort of arguing, is the kind of religious divide, evangelism and proselytism that ultimately only hurts both sides of the argument, because it makes people favor dogmatic choices over considering what's actually useful in the current situation?

Of course :-) I'm not advocating for it in anyway. Quite contrary. That's just what I've seen - which sadly is, that tons of developers are not able to rationally discuss architecture and the just default to dogma.

Often someone dropped stone tables years ago and they just follow. If instructions they were given were simple and well understood on mechanical level, usually result is bit better than total chaos. Thus black&white approach.

The worst WTF garbage software I have seen in my 30+ years career is an old micro-services system maintained by a team at work.

It uses more than 100 micro-services to implement a system that would have been so much simpler if implemented as a monolith. With hundreds of scripts to try and manage the complexity. With frequent timing bugs (a single micro-service delay causing a cascade of timed out micro-services bringing the system down) and almost impossible to fix complex error scenarios that will make experienced developer heads spin.

In contrast, the systems I work on are million+ lines C/C++ monoliths that are orders of magnitudes simpler to maintain. With orders of magnitude fewer bugs in production. All implemented as a collection of well designed libraries.

IMHO the moment you split a monolith into 100+ micro-services, the number of failure scenarios dramatically increase because of the added complexity of message exchanges, message failures, queuing delays, circular dependencies etc.

In other words, if you don’t even have the skills to implement a monolith successfully, then stay the hell away from micro-services.

Monorepo, separate modules is the way to go. I just don’t think it occurs to people.

But I think for compiled languages people see the build steps as particularly wasteful, both locally and in CI. They forget about the integration test surface area or think it’s something to be paid for later (tech debt) and reason that if they are changing one module why should they have to pay to compile and test the whole thing?

A good tooling or ops person can ameliorate a lot of that. There are a lot of poor patterns that people ignore when build units are small and incremental, that you cannot ignore when they get large. And then there are tools that can auto start tests, which reduces the latency in code-build-test quite a bit.

I agree with your perspective here.

If you don't get something, perhaps it's that this is a bit of a holy war, and as with all holy wars, each camp tends to argue things from a very black-and-white perspective. As with most things, though, the truth is not so binary.

People forget to incorporate context with their arguments.

A team of 5 Devs should never create microservices.

A team of 5000 should have at least a few.

I think most people agree with your post but you are indeed missing the point.

People are discussing anecdotal "average" cases. Not what is possible, but what actually happens.

Of course anything can and does happen, and outliers are interesting, but you shouldn't assume they are equivalent to other cases just because they are possible. The distribution does matter and since it's not easily measurable people discuss anecdotes and opinions.