Hacker News new | ask | show | jobs
by kemitche 637 days ago
I firmly believe that monolith vs microservice is much more a company organization problem than a tech problem. Organize your code boundaries similar to your team boundaries, so that individuals and teams can move fast in appropriate isolation from each other, but with understandable, agreeable contracts/boundaries where they need to interact.

Monoliths are simpler to understand, easier to run, and harder to break - up until you have so many people working on them that it becomes difficult for a team to get their work done. At that point, start splitting off "fiefdoms" that let each team move more quickly again.

7 comments

This 100%. Still depends on the specific context (e.g. what type of software are you going to build and run), but for a typical case, like web-based transactional self-service business platforms, this is where I arrived after more than 25 years in the industry.
I'm also an advocate of Conway's Law:

https://en.wikipedia.org/wiki/Conway%27s_law

Operationally speaking, it yields solutions aligned with your organization and thus are easier to support.

Plus, I see microservices as a premature optimization. Let it be shown the additional complexity is warranted and provides value.

Yep. Conway's law [1]:

Organizations which design systems (in the broad sense used here) are constrained to produce designs which are copies of the communication structures of these organizations.

— Melvin E. Conway, How Do Committees Invent?

[1] https://en.wikipedia.org/wiki/Conway%27s_law

You can run it backwards! Get a high level software design, then make the org-chart.
There's a book by Lex Sisney about that called Organizational Physics. Interesting read.
Yup. You ship your org structure.
This is also my experience. It's always that projects grow large in terms of people working on them and that is when you want to create independence between teams by reducing the amount of cooperation needed on a very defined boundary/interface between them.

Usually you start small and grow bigger, so there is only rare exceptions where it makes sense to merge microservices back into a monolith, except maybe for cases where going for a microservice architecture was a bad decision taken without actually having the above mentioned problem.

Kind of side steps the fundamental scaling issue though.

In a banking app there will be more requests for the account balance than there are logins, but logins will likely take longer.

Your argument is more around who is allowed to touch which and who is responsible when it breaks, but not around one of the core reasons to choose microservices.

If you don't want to scale the whole thing (why?), you can also deploy the monolith twice and route different API calls to different clusters. Slice and scale as much as you're willing, paying the price of deployment complexity and bin-packing the usage.
I've been there and done this and it's effective.

It's an imperfect solution for all of the obvious reasons. I think even the most junior engineer could point out the flaws. I won't insult HN readers by naming them!

But also: effective at some things for near-zero cost. It's probably more effective at "segregating your traffic so that Low Priority Service A can't starve out High Priority Service B" than overall scaling but, sometimes that's what you need.

    paying the price of deployment complexity
For us, there was essentially no increase in deployment complexity. There was a small one-time increase in network routing complexity. Essentially requests for "www.foo.com" were routed to one group of servers and requests for "api.foo.com" were routed to another group.

The alternative was decomposing a large monolith. A goal we also pursued. But what you describe was a valuable stop-gap as we undertook that multiyear effect.

You scale the whole thing. There's no issue at all.
To what end? To support that 1:10000 transaction that takes the most time and needs the most scaling? Just burn a wad of $20s, that will be easier.
Scaling a monolith is almost always cheaper than migrating to microservices and scaling that.

When you split it into microservices you are adding a bunch of infrastructure that did not need to exist. Your app performance will likely go way down since things that used to be function calls are now slow network API calls. Add to that container orchestration and all sorts of CNCF-approved things and the whole thing balloons. If you deploy this thing in a cloud, just networking costs alone will eat far more than your $20(if not, you'll likely still need more hardware anyway).

Sure, once you add all that overhead you now may have a service that can be independently scaled.

There's also nothing preventing you from splitting off just that service from the monolith, if that call is really that hot.

> Scaling a monolith is almost always cheaper than migrating to microservices and scaling that.

No - it depends on capacity needs and current architecture.

> When you split it into microservices you are adding a bunch of infrastructure that did not need to exist.

Unless you need to scale in an economical manner.

> Your app performance will likely go way down since things that used to be function calls are now slow network API calls.

Not necessarily. Caching and shared storage are real strategies.

> Add to that container orchestration and all sorts of CNCF-approved things and the whole thing balloons.

k8s is __not__ required for microservice architectures.

> If you deploy this thing in a cloud, just networking costs alone will eat far more than your $20

Again, not necessarily. All major cloud providers have tools to mitigate this.

To clarify: I'm not a microservice fanboy, but so many here like to throw around blanket statements with baked in assumptions that are simply not true. Sometimes monos are the right way to go. Sometimes micros are the right way to go. As always, the real answer is "it depends".

    > Scaling a monolith is almost always cheaper 
    than migrating to microservices and scaling that.

    No - it depends on capacity needs and current 
    architecture.
It does depend, but a monolith of any nontrivial size is going to require hundreds if not many thousands of hours of engineer time. That's a lot of money right there. IME often the experience comes down to something like $250K of engineer time vs. maybe $2K/month of server capacity.

Again, though: it does depend.

    To support that 1:10000 transaction that takes 
    the most time and needs the most scaling?
Done in the most naive way possible (just adding more servers to one giant pool) yes, it's as ineffective as you say.

What can be effective is segregating resources so that your 1:10000 transaction is isolated so that it doesn't drag down everything else.

Imagine:

- requests to api.foo.com go to one group of servers

- requests to api.foo.com/reports/ go to another group of servers, because those are the 99th percentile requests

They're both running the same monolith code. But at least slow requests to api.foo.com/reports can't starve out api.foo.com which handles logins and stuff.

Now, this doesn't work if e.g. those calls to api.foo.com/reports are creating, say, a bunch of database contention that slows things down for api.foo.com anyway up at the app level due to deadlocks or whatever.

There are various inefficiences here (every server instance gets the whole fat pig monolith deployed to it, even if it's using only a tiny chunk of it) but also potentially various large efficiencies. And it is generally 1000x less work than decomposing a monolith.

Not a magic solution, just one to consider.

Importing code paths that are never executed in a service is a security risk at best, a development/management nightmare on average, or an application crippling architectural decision at worst, especially when working with large monoliths. That is not a smart trade off - I would not recommend this pattern unless the cost to break off is so exorbitant that this would be your only choice.
For a monolith of any nontrivial size, you're talking anywhere from weeks to months of years of engineer work, and those are engineers who also won't be delivering new value during that time. And if other teams are still delivering new features into the monolith during that time, now that's more work for the team whose job it is to break apart the monolith.

So anywhere from tens of thousands to millions of dollars is the cost.

Whether you call that exorbitant is up to you and your department's budget, I guess.

Yes. It's better than burning £100ks by over complicating things and wasting your developer's time.

Most startups fail. You need to cover as much ground as possible while you have runway, not cock about with microservices.

You're basing your entire argument on the org in question being a startup, and missing the rest of the 98% of the market.
In the context of choosing monolith vs microservices first, it's a safe bet that we're talking about startups; in the other 98% of the market you don't have a choice because something already exists.
Can you be more specific? Because if you mean scale... meh.

For the most part, scaling a system is fungible across features. In particular, in a monolithic system, if I had to add another server due to logins, I just add another server. A side benefit is if logins are down but account balance checks are up, that extra server can pull that duty too. I don't need to say "these computing resources are only for this feature."

Isolating failures is way more important.

Microservices are indeed the best approach if you want to ship your org chart. That's all there is to it. Most of the technical justifications do not make sense, outside of very niche org requirements which aren't really all that common.

Take, for example, the idea that you can scale individual services independently. Sounds amazing on paper. However, you can also deploy multiple copies of your monolith and scale them, at a much lower cost even, and at a far lower complexity. In a cloud provider, bake an image, setup an ASG or equivalent, load balancer in front. Some rules for scaling up and down. You are basically done.

'Monolith' sounds really big and bad, but consider what's happening when people start using microservices. In this day and age, this probably means containers. Now you need a container orchestrator (like K8s) as you are likely spreading a myriad of services across multiple machines (and if you aren't, wth are you building microservices for). You'll then need specialized skills, a whole bunch of CNCF projects to go with it. Once you are not able to just make API calls everywhere 1 to 1, you'll start adding things like messaging queues and dedicated infrastructure for them.

If you are trying to do this properly, you'll probably want to have dedicated data stores for different services, so now you have a bunch of databases. You may also need coordination and consensus among some of your services. Logging and monitoring becomes much more complicated(and more costly with all those API invocations flying around) and you better have good tracing capabilities to even understand what's going on. Your resource requirements will skyrocket as every single copy of a service will likely want to reserve gigabytes of memory for itself. It's a heck of a lot of machinery to replicate what, in a monolith, would be a function call. Maybe a stack.

While doing all of that you need more headcount. If you had communication issues across teams, you have more teams and more people now, and they will be exacerbated. They will just not be about function calls and APIs anymore.

There's also the claim that you can deploy microservices independently of one another. Technically true, but what's that really buying you? You still need to make sure all those services play nice with one another, even if the API has not changed. Your test environments will have to reflect the correct versions of everything and that can become a chore by itself (easier to track a single build number). Those test environments tend to grow really large. You'll need CI/CD pipelines for all that stuff too.

Even security scanning and patching becomes more complicated. You probably did have issues coordinating between teams and that pushed you to go with microservices. Those issues are still there, now you need to convince everyone to patch their stuff(there's probably a lot of duplication between codebases now).

I think it makes sense to split the monolith into large 'services' (or domains) as it grows. Just not 'microservices'.

A funny quote I read a while ago on Twitter: "Microservices are a zero interest rate phenomenon". Bit of tongue in cheek but I think there's some truth to it.

I agree with you.

It seems people think "Big Ball of Mud" when they hear Monolith but they're not equivalent. Jut like I tend to hear "Spaghetti Code" when I hear Microservices. But again they're not equivalents. Both architectures are equally capable of being messy.

Agreed, in general.

But don't forget about the definition of micro :-) This is an optimization problem rather than a conceptual one.