Separation of responsibilities? Easier to analyze because you only have so many inputs and outputs to a simpler system?
Debugging something that touches a lot of paths in a monolith can be quite nightmarish as well.
I guess what it means is that even if you can build a well modularized system, it will only stay well modularized if you use a network call to enforce it. Well, at least for most companies.
Conceptually, there's nothing keeping you from designing your codebase to work as both microservices or direct calls. I've certainly done it before - each service defined a Java interface, and codegen could hook that up as either a direct call or to route over some kind of layer.
Doesn't a function only have so many inputs and outputs too? Scope capturing/global variables aside.
Sure, and a microservices architecture to me implies a larger movement towards function-esque, idemopotency, analyzability and away from global state.
To me, monolithic architecture implies global shared state that is difficult to reason about.
How do I do a similar stack trace in microservices to understand the path that led to this state? I've used microservices at a couple of companies and their methods were effectively, look at the request id and trace it through this mass of log files for each microservice we are running. It was terrible and could take hours to compile the same information.
What tooling exists to solve this problem with microservices? Genuinely want to know.
Sure, if you are not writing a program using event-driven async style. Every monolith I've worked with has been in async style pretty much.
With global state in the monolith, this can become quite difficult to reason about. By contrast, with microservices, you can analyze the service as performing a small function with a single input and output without global state dependencies. This can be easier to debug.
I've worked on plenty of monoliths with async style code and have found the stack traces to be plenty helpful. It's never been an issue. At least in JavaScript you retain all relevant scoped data and can dump it all if you like. Debugging gets much more interesting when there are no obvious such errors, yet error conditions are present.
I'm still not seeing any equivalent microservice tooling.