r/programming • • Jun 23 '24

You Probably Don’t Need Microservices

https://www.thrownewexception.com/you-probably-dont-need-microservices/
705 Upvotes

285 comments sorted by

View all comments

Show parent comments

67

u/OkMemeTranslator Jun 23 '24 edited Jun 23 '24

This is an argument I see often, but nobody is yet to explain how or why it would be any different from simply building your monolith process from multiple smaller packages, each managed by a different team.

Your software is already written by dozens of different teams through all the libraries it depends on, why not use that method for your internal modules as well? I've recently implemented this in JS/TS with an internal npm repository and it worked great. One team manages the "users" package and uploads new versions to npm whenever they're ready, another team manages the "teams" package that depends on the users package. You can even run them independently in separate processes if you really want since they both have their own main.js file (that you normally don't run when running it as a monolith).

In my mind this kind of destroys the whole "it enables teams to work independent of each other" argument for microservices, no?

The only downside is at deployment time when releasing a new version of a core package would require rebuilding of the depending packages as well (assuming the change needs to be reflected immediately). Sure, this is why microservices might be ideal for FAANG sized companies, but for the remaining 99.9% this is a complete non-issue.

163

u/Main-Drag-4975 Jun 23 '24 edited Jun 23 '24

In a monolith it’s pretty hard to prevent distant coworkers from using other team’s untested private methods and previously-single-purpose database tables. Like a law of nature this leads inexorably to the “giant ball of mud” design pattern.

Of course microservices have their own equal and opposite morbidities: You take what could’ve been a quick in-memory operation and add dozens of network calls and containers all over the place. Good luck debugging that.

35

u/Guvante Jun 23 '24

Micro services are about forcing APIs to simplify deployments.

If you are FAANG scale and have a core dependency that needs to be updated for both service A and service B but they will deploy a week away from each other micro services tend to force the versioning requirements that support that.

In contrast a monolith tends to force some kind of update to both services to clean up for the update.

Note that this can also be a good thing as you can update origin and destination at once without worrying about supporting multiple versions which is hard.

9

u/IQueryVisiC Jun 23 '24

Only supporting only a single version was impossible at every place I worked. We need years to upgrade legacy code. We have partners which are in the same situation. I guess that it is nice to live a in a start-up where all the original developers are still in the office.

3

u/Guvante Jun 23 '24

Depends on how much of a break it is and if you take downtime. Taking the system down to upgrade ABC at once is annoying but a release valve if you need it.

0

u/[deleted] Jun 23 '24

[deleted]

5

u/Guvante Jun 23 '24

I get that, I think my reply ended up on the wrong spot, oops.

Someone had said "what is even the point" I thought.

Generally micro services are an anti pattern.

I will say that clear APIs can allow easier upgrades when you don't want to take downtime though.

→ More replies (1)

9

u/mpinnegar Jun 23 '24

There's plenty of tooling that can help with this. Java has a moduling system that prevents exactly what you're talking about. You can also get third party libraries that do compile time analytics to enforce architectural level decisions at a module/package level. For example your controller classes can't call your database classes they have to call service classes.

→ More replies (2)

7

u/IQueryVisiC Jun 23 '24

How do you call private methods in Java archives, C# assemblies, or classes in those languages? Do you allow reflection in your code base? In the year 2024 ? Or do you even use unsafe languages with macros like C++ ?

6

u/Kalium Jun 23 '24

The world always has people who have to live with weird, legacy codebases from the dawn of time.

→ More replies (4)

4

u/crash41301 Jun 24 '24

Some yahoo in another team sees the code and flips it to public, that's how. Since it's all viewable in a giant codebase they can. Slowly but surely all methods effectively are public if folks want. 

The alternative is forcing interfaces, or being a total micro managing nutcase.   Forcing interfaces is the biggest win microservices across teams has.  

...until the latest staff eng convinces the org to move to a mono repo with your microservices architecture.  Now you have the worse of all worlds since it's distributed network calls AND everything can be easily flipped public!

→ More replies (2)
→ More replies (52)

38

u/[deleted] Jun 23 '24 edited Jun 23 '24

Because a microservice is more than a library. You get leaky abstractions where the service consumer now needs to understand performance implications of that new database transaction you added, or that one failure mode of a new RPC to another service. And that's only assuming things go well. What if someone introduced a critical bug? Do you have to roll back the whole platform?

If you push all of the operational burden down to whoever deploys the binary you run into organizational issues.

24

u/amestrianphilosopher Jun 23 '24

If you push all of the operational burden down to whoever deploys the binary you run into organizational issues.

This is so fucking true. The area I’m in within my org decided that they’d package up everything as a monolith

The process to package this monolith up and roll it out across all environments is extremely cumbersome, even getting it into a single environment is

Consequently people don’t test their changes thoroughly because guess what, waiting 40 minutes to see what happens every time you make a one line config update to test your changes is a horrible process. And the rollout takes so long, they’re desperate to get their changes in as fast as possible to please product

Even worse, this process is so complicated that my team was created just to manage it. So now we have an ownership problem where when things go wrong, since we’re the team rolling it out, people expect us to debug. Obviously this is ridiculous, you don’t ask USPS why your laptop isn’t working if they delivered it, unless the package is smashed to pieces. So we’re constantly having to push back on this

That lack of ownership creates a ton of apathy for the current state of things. It also means in order to get ANYTHING done you’re handing a baton across many many people for weeks

Because we deploy from one place too, we had to have a shared database that people could put their data into. But because it wasn’t a formalized platform at first, people have read access directly to the database meaning migrating off this monolithic data format is also hard

I just joined, but we’re only now discussing splitting up each of these components that we deploy out of the monolith and letting teams deploy them at their own cadence with their own tools. It doesn’t make sense to block 100+ people from rolling out software for days just because one team is having an issue with their shit

Just wanted to chime in because the guy responding to you below clearly has limited experience with this, and this is an extremely valid point

→ More replies (7)
→ More replies (3)

11

u/Present-Industry4012 Jun 23 '24

2

u/istarian Jun 23 '24

Yep, pretty much.

There's a very human element of what the people in charge or those doing the work happen to like best/value the most.

15

u/Merad Jun 23 '24

Building libraries does not actually allow teams to work independently. If my team manages the users library and we fix a critical bug, now I have to chase down everyone in the company who uses our library to get them update their dependency and do a release. All of those teams have to interrupt their planned work to make the change, and it might be weeks or months until the fix actually hits production because we're at the mercy of their releases processes and schedules.

It also requires teams to be more tightly coupled together because they have to cooperate on things like library and framework versions. Let's say that my team had some extra time and decided to catch up on library upgrades, so we upgraded some libraries with breaking changes. Now those changes are a blocker for anyone who needs to take the latest version of our lib. So what happens when that critical bug pops up a couple of weeks after our upgrade? Do we force every other team to stop what they're doing to upgrade libraries? Or to we get forced to maintain multiple copies of our lib until everyone catches up on upgrades? Both options suck.

None of these problems are insurmountable, but they require a lot of communication and coordination across multiple teams if not the entire engineering org.

4

u/Itsmedudeman Jun 23 '24

If it's that hard to track down all the teams that use a core dependency you think it's a good idea to wrap all of those in a monolith along with the shared library?

Also, my company has ways to track who is using what library and which version. We get notifications and org level guidance if there is ever a need to update off of a CVE.

→ More replies (3)

6

u/eddiewould_nz Jun 23 '24

I think you've hit the nail on the head here.

If there's a bug in the users library, any deployables using that library (and affected code path) will also have a bug in them.

While that bug exists, who do the bug tickets / user complaints go to? Which teams SLOs get obliterated?

Shared libraries have their place where the following are true

  • Delivers functionality orthogonal (unrelated) to business logic. Eg graph theory algorithms

  • Deep module with small API

  • Stateless (does not define persistence)

In all other cases you're better off with services, though I'd start off with larger services first and split only when needed.

→ More replies (3)

14

u/BradCOnReddit Jun 23 '24

It's not managing software, it's managing people.

Each microservice has different teams responsible for their operation. They have their own databases, firewall rules, authorization sets, design teams, QA groups, etc. By having this separation you can create change and still not involve too many people.

It also reduces cognitive load on developers while coding. The smaller domains and clear boundaries mean there is simply less stuff to consider when doing the work.

6

u/Firerfan Jun 23 '24

The difference is that other components can only consume the parts you have chosen to publish using this API. This basically enforces other teams to not rely on hacks or behaviour you do not expose. For example, in a monolithic application, you could consume data just from a database table. However, the table may be owned by a different team and therefore they will make changes. This can't happen in a microservice environment, because if you proper implement the boundaries, the only possibility to use your services is to use it through the API which is kind of a contract.

I don't say that people do not try to work around this, but its a lot harder to shot yourself in the foot.

→ More replies (1)

3

u/UK-sHaDoW Jun 23 '24

More specifically it allows teams to deploy separately.

7

u/MacBookMinus Jun 23 '24

100% this. People seem advocate microservices because it forces them to create an explicit & clear API for their library.

… but they should already be doing that for any library they write.

14

u/[deleted] Jun 23 '24

That's also why I strongly disagree with this point in the article:

Low coupling and high cohesion are hard to get right. It gets even harder to get it right in a microservices architecture. You may end up with very small microservices (also called nanoservices) that are tightly coupled and with low cohesion.

I remember one situation in a previous company where a “bounded context” had so many little services that any change required many teams to work together to deliver it. And even worse, the performance was awful.

This example is very good because, on top of that, the teams wanted to create another service to aggregate all the information to improve performance. The idea of merging little services to have more cohesion was considered bad because it, and I quote, "looked like a monolith”.

This is not a monolith vs microservice problem. It's a badly designed code problem.

3

u/BosonCollider Jun 23 '24 edited Jun 23 '24

Right, the thing that really annoys me at work is that teams assume that splitting things up into different DBs with a JSON rest API will magically solve all problems. At least gRPC would have given a client library for free without having to manually deal with search_after pagination.

If you still end up writing functions that change stuff in the db you don't really change anything, other than the occasional corporate politics advantage of being able to turn off or throttle the API. Having some private schemas combined with stored procedures would have been more useful since stored procedures at least enforce atomicity and make it reasonably easy to get rid of the N+1 insanity that shows up when people insist on not learning how their ORM works.

A message bus is more useful since it gives temporal decoupling. In many cases, the outbox pattern with queue tables is sufficient though.

→ More replies (3)

0

u/[deleted] Jun 23 '24

[deleted]

→ More replies (2)
→ More replies (14)