This does not happen at all. When you break a system into subsystems, all the previous connections that get remapped to new connections between subsystems still need to happen, in order to solve the fundamental problem that the system solves — except now instead of just making the connection directly, there has to be a "cross-functional" meeting between teams and a complicated communication layer between the systems. And if somehow you find a breakdown that requires minimal connections between subsystems, then those connections wouldn't have existed in the original system either, and the N² problem doesn't exist.
Of all the things wrong with micro-services this isn't one of them.
The "complicated" communication layer is always either REST/JSON or GRPC.
Both of which are simple, easy to debug, proven and require nothing more than a simple discussion over an API contract.
It is, exacerbated by the over-use.
> The "complicated" communication layer is always either REST/JSON or GRPC.
Going over network, which is slower, unreliable, poorly typed. It's orders of magnitude more difficult to refactor a published REST API in comparison to e.g. Java interface within a monolith.
Microservices are generally far from easy to debug. In the best case scenario you have the whole stack locally and can put breakpoints, add logging immediately. But that happens rarely, debug port is often blocked (security), you can't easily modify the code to add some diagnostics without a complex CI dance etc.
But I've never worked on a monolith project that I could run entirely locally.
And this idea that APIs are slow, unreliable and unable be to be strongly typed is nonsense. This is 2023. We have plenty of tooling and techniques to make this robust.
There are few narrowly defined responsibilities which are handled by dedicated services, and it is often more awkward to get them working locally. But because they are so specialized, you need them only rarely.
> And this idea that APIs are slow, unreliable and unable be to be strongly typed is nonsense.
Network APIs are inherently slower and more unreliable in comparison to an in-process method call. Some typing solutions are there, but they are way more awkward (and in some ways weaker) than statically typed monolith interfaces.
What are some examples of monolith components that couldn’t be run locally?
Im guessing third-party integrations, but perhaps there are other things.
That includes databases, caching, auth, ML models, mock API endpoints etc all pre-populated with sample data then the actual application which for complex, JVM based ones can often fail to launch if there isn't enough memory to pre-allocate.
Many systems I have worked on all of that would simply not fit on a 16GB MBP.
There should be maybe one meeting where the caller defines what they need the service to return to them.
1. Merge the two services (and their data stores) into one and cause havoc downstream of either service
2. Burn through your network latency/throughput budget trying to reinvent a DB join across RPC boundaries (and god forbid if you can't batch multiple lookups in a single API call!)
Oh sweet summer child
turns out, they inarguably have a whole lot of experience at high levels in the biggest companies in the world. Either they're straight up lying, or it is in fact possible to resolve things with a single meeting. Why wouldn't that be the case?
The thing is in reality it's not that easy.
> Either they're straight up lying, or it is in fact possible to resolve things with a single meeting.
Something can be done in a single meeting but not everything. But you gave me only two alternatives without anything between so there is another useful link for you https://en.m.wikipedia.org/wiki/False_dilemma
snark aside, just because a logical fallacy exists in an argument, it does not mean the argument is untrue. My experience echoes jedburg's - services should maintain their own datastore and provide api access. This allows them to be decoupled and allows the service to alter their datastore as needed. When multiple services leverage the same underlying datastore, things can get overly coupled. This becomes increasingly important as organizations grow because the coupling means more overhead communicating and aligning around changes.
Jedburg is an expert because he has seen this happen in multiple companies. I have too, but I don't have the speaking/consulting breadth of experience that they have. Credentialing and appeals to authority work because we are squishy humans and we can't verify everything so we lean in on others. That does not mean that authorities are always right, but when their experience and advice mirrors other's experience and advice, there is something to it.
And for the record, in public companies (two of them), I have exactly seen this play out: a well defined api that does not allow others to muddle in their datastore leading to _single_ meetings where new functionality is discussed and agreed upon and later implemented. I have also experienced the pain of multiple teams sharing a datastore when the datastore now needs to be altered which led to dozens of meetings across several teams and a rollout plan that was measured in months and quarters due to coordination needs. I can firmly say that the latter is a much harder (and expensive!) problem to solve in each case I've experienced.
The team that needs data goes to the team that has that data and says "can you make an API that vends the data that I need?". Then they make it. And then maybe there is another meeting with a follow up, or perhaps an email asking for the data to be presented slightly differently.
But if you come to a team with a good use case and some examples, it shouldn't be too hard for them to create an API that vends the data appropriately.
This of course is all predicated on a good microservices architecture where you already have systems in place to handle API discovery and communication between services and proper data separation.
If the codebase is different you can force the separation not just ask nicely to keep the code well modularized.
for example: golang - multi-module workspaces javascript - yarn workspaces
With this setup, you can create modules a, b and c. And then add restrictions about which which modules are available to a given module
Just keeping things neat doesn't go nearly as far as a separate process on separate machines. Monolith might be better but I don't think it's a situation where you can have it all.
The blast radius is a single request, right?
In every likelihood it _is_ a separate process on a separate machine.
With micro-services if you screw up a deployment the service is down and if your platform is well designed the system will be degraded but not down.
With a monolith the whole system is down.
No, you are just at reduced capacity.
The moral of the story is not to roll out to 100% right away.
Bit hard to do schema evolution with a staggered rollout.
That said, loads of folks do microservices with a shared dB.
In theory, but when the getUser service is down, your app is probably broken as well.
If you have long running requests (something beside small rest calls like large data transfers) then they're all taken down.
Good CD is even more important if there are more services to deploy
How about moving things into different folders. Have you thought about that?
Why do you have to modularize it with a whole new repo, a whole new docker set up? Just use a folder bro.
At the point where you have to write an API call instead of simply joining to a table, you have introduced orders of magnitude more complexity, failure points, testing challenges, race conditions, coherency issues, code duplication etc into your system.
I don't mind stateless microservices too much. This forms more of a hub and spoke model (many services talking to 1 database). But the minute you bring separate state into the equation it's a chaos factory.
I'm a big fan of Postgres. I'll make a system rely heavily on complex joins. But I'd rather call some other team's API than share DBs with them.
The biggest one in web is regular entity databases vs. timeseries databases designed for analytics.
For this case entity data is just synced to to time series database every so often. Not super chaotic. It does make sense to divide services along those lines.
Though I would argue a folder still works for the web app part.
PS: I'm not saying using one DB with microservices, it's still with the monolith
If you have multiple things sharing one data store, having ownership over the structure of the data is very difficult.
Because this is the standard architecture you will find for most platforms.
You encapsulated data under one service and you find that it's actually utilized much more in another service. This happens a lot.
The longer you can centralize your storage the better and easier everything is.
Where I work we run a massive MySQL database that has a pretty simple sharding rule, so I don't believe that you need to split the database even at scale.
This thread is very painful, feels like CS101 coming in to tell us why we don't need relational databases and that we can just shove it all inside mongo.
Multiple databases can "work" but you get the problem I described above.
You know when you go to a company and eventually you see idiot practices or technical debt? Unfortunately this belongs to that category. You seeing it at dozens of companies doesn't validate anything except for the fact that bad patterns are common.
It also validates the existence of people who often do things poorly and don't know it (aka you).
Of course it "works". But you can do better.
Splitting data into several dbs when one db suffices is called "extra work". Good results can arise from "extra work". But the extra work wasn't necessary.
You have to think like this: Sure multiple dbs work. But would 1 db in it's place work just as well?
The top commenter here from Netflix one of the people who promoted the whole microservices thing is telling startups to go monolith for a reason.
Startups shouldn't start with microservices. I've worked for a few different ones, and I've always created a monolith while avoiding all kinds of premature planning. Doesn't mean I'd run things that way with 200 people owning systems with evolving requirements for 10+ years, or even two separate dev teams of 5 people.
Also if by "decoupling data and code" you mean decoupling data from code, that's not the goal. It's to decouple one team's data from another team's data, and to properly abstract it.
The only reason our mono-DB even lasted this long was because previously, very few requirements were changing over time. So when people say their big org is monolithic, I can believe it. It works in some scenarios, in most it doesn't.
Use a schema bro.
You can do this with two schemas. Think of it as folders in databases. Then give different permissions to users per schema.
It can also have multiple "databases" on a single running instance, each with separate quota and ACLs. So each team uses its own database. Only a few teams are special enough to need a dedicated instance.
It can be benefitial to maintain well defined interfaces at boundaries between certain parts of your code. It can also produce a lot of work and add complexity. But beyond a certain scale adding systemic boundaries and honoring them isn't something you should avoid.
Devs who do microservices just tend to go too far too early.
You can use folders to decouple code by making code private within that folder. Only publicize parts of the code with your languages version of exporting.
Or you can make it private behind an entire new repo and behind an entire service. Only publicize parts of the code through api interfaces like http and make everything 10x harder for the sake of decoupling code.
I say again, use a folder bro.
This will be my new go-to response when discussing microservices
On a theory level I would agree with you - I've just never seen that happen in practice.
They solve "people problem" by converting trivial technical problem into complex distributed system problem.
Well, now you have a _Problem_.
FT.com did a recorded seminar session on their microservices architecture and one of the benefits they extolled is if someone wanted to improve on a feature they could just make it all over again and replace the old microservice with a new one. No need to look at the last persons code, just blow it away like it never existed.
I gathered their site is actually a black box filled with hundreds of black boxes of microservices. All a mystery, they either work or they don't and if they don't they fail gracefully quickly.
At one point it was 35 engineers, but layoffs took it down to 7, at which point we started to get a lot more done. There was just so much less time spent keeping everyone aligned. So many fewer meetings, sign-offs, reviews, plannings, retrospectives, management meetings, etc. Developers had a lot more agency, so they just got stuff done. Technical debt repayment became 50% of our time, as we easily knocked out features in the other half of the time. We kept ruthlessly cutting complexity, so it got faster to add new features.
I'm sure some projects just need more bodies, but I think there's an upper bound to how much complexity can be added to a system in a given unit of time. Adding developers over a threshold will result in the same amount of features per week, just everyone does a little less and spends a little more time on communication.
Repeat up to thousands of developers where adding a single field takes months.
Years ago I did two back to back contracts for two different pharmaceutical companies. They were both about the same size but one had an IT group that was ten times the size of the other. You can guess which project was late and painful.
Microservices are about splitting the application into very fine-grained sub-applications. It's not about modularity in general, it's about making things as modular as possible (up to one function per service). That's why they are micro. Otherwise we'd just call them "services" and nobody would have any problem with that.
Assuming coupling is reasonable. If you have a "distributed monolith", you still end up with all the meetings because every microservice change risks breaking interfaces other people are using.
In the context of coupling, I'd argue the same applies to monoliths. Multiple teams can successfully work on a monolith given architecture where they're not constantly stepping in each other's toes (each team works mostly in their own modules/classes/packages)
And once you've split up your app into lots of independent microservices who owns the arrows between the microservice boxes? ( The actual app ).
I do agree that this is less of a problem for startups so it doesn't make sense to start with a complex microservice architecture. But in large organizations, especially corporates, it absolutely makes sense.
The coordination is still there because a microservice team does not live in a vacuum. They build services based on demand from other teams that typically build web apps, mobile apps, sometimes server-to-server.
Hence, the "independent" team now becomes a roadblock for higher order features.
A team of N engineers requires N² coordination. Large teams get mired in endless meetings, email, design reviews. Small teams are more effective, but struggle to maintain large systems.
Splitting a system into subsystems allows each team to focus on their piece of the puzzle while minimizing the amount of peer-to-peer coordination.
Yes, microservices add complexity and overhead, but this approach enables a large organization to build and iterate on large systems quickly.