Hacker News new | ask | show | jobs
by whimsicalism 1611 days ago
> What have you gained?

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.

4 comments

Most comments about this assumes a poorly designed monolith and a well designed set of microservices. A microservices architecture can be a rats nest too.

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.

Or something that can be spun up as a service or imported as a library
> you only have so many inputs and outputs to a simpler system?

Doesn't a function only have so many inputs and outputs too? Scope capturing/global variables aside.

> 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.

It’s been mentioned elsewhere in this thread, but the lack of microservices doesn’t imply a global shared state. There’s a big difference between a large service that has well isolated modules, and a large service where all the state is contained in one struct, for example. I feel this issue is pitched as a false dichotomy.
If I'm in Java, JavaScript, or Python and there is a code fault, the system provides me a stack trace of the call structure that lead to the error. If I catch the error I can output more related data as I deem necessary. This comes effectively, out of the box.

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.

> If I'm in Java, JavaScript, or Python and there is a code fault, the system provides me a stack trace of the call structure that lead to the error. If I catch the error I can output more related data as I deem necessary. This comes effectively, out of the box.

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.

It seems to me that you are comparing apples and oranges. If your code has no relevant state or coupling, a pure function. Sure, you can look at it in isolation. You could do the same in a monolith.

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.

Also, security. Each component has an identity and it’s own set of permissions so the custom emoji widget doesn’t have access to the payments system or the health data.
This would be true in a monolith as well unless configured otherwise. Functions don’t just start talking to each other; you have to explicitly connect them via a function invocation. I’m not sure how an architecture would change that.
It’s pretty straightforward. In a monolith everything is in the same memory space, so the payment system credentials and routines are available to the whole monolith. In a microservice architecture, one service can’t access the memory or routines of other services.
One function cannot access the memory space of another function any less explicitly than one process can access the memory of another process. Either way, you are just picking different contract enforcement mechanisms. The only way this would be helpful is if your whole goal is to make something (breaking the contract) as hard to do as possible. And in that case, I really have to ask, who are your teammates that you trust them so little as to literally enforce separation this way? No healthy organization should have to resort to this kind of civil war.
> One function cannot access the memory space of another function any less explicitly than one process can access the memory of another process.

You're mistaken. The operating system largely prevents cross-process memory access. Of course, CPU vulnerabilities can circumvent these protections, but even then your micro services can run on distinct machines while monoliths can't.

> The only way this would be helpful is if your whole goal is to make something (breaking the contract) as hard to do as possible

Yeah, that's the idea. "defense in depth".

> And in that case, I really have to ask, who are your teammates that you trust them so little as to literally enforce separation this way? No healthy organization should have to resort to this kind of civil war.

(1) not all attackers are internal

(2) some attackers are internal and many very profitable organizations do restrict employee access to secrets (and in many cases this is required by compliance).

> The operating system largely prevents cross-process memory access.

As do programming languages. Yes, a few languages like C allow you to do anything that's physically possible, but most popular languages including Python and JavaScript make leaking function-internal memory to the outside world either very inconvenient or impossible.

As for memory being shared among processes, it's not only not impossible but quite common in performance-sensitive applications. https://man7.org/linux/man-pages/man7/shm_overview.7.html

> many very profitable organizations do restrict employee access to secrets

For one, code is not secret. Secondly, when was this discussion scoped to profitable organizations? A very, very large chunk of HN readers work in startups.

> in many cases this is required by compliance

This argument weakens the entire stance, because it shifts the goalpost from "this thing is good" to "this thing is legally required." Let's stay focused. There are so many implicit assumptions you're taking as granted, I'm starting to doubt this is even an argument in good faith.