Hacker News new | ask | show | jobs
by trentdk 2202 days ago
A major benefit to microservices (over monoliths) that I haven’t seen mentioned yet is testability. I find it hard, or improbable to achieve a healthy Pyramid of Tests on a large monolith.

For example: a high level, black box test of a service endpoint requires mocking external dependencies like other services, queues, and data stores. With a large monolith, a single process might touch a staggering number of the aforementioned dependencies, whereas something constrained to be smaller in scope (a microservice) will have a manageable number.

I enjoy writing integration and API tests of a microservice. The ones that we manage have amazing coverage, and any refactor on the inside can be made with confidence.

Our monoliths tend to only support unit tests. Automated end-to-end tests exist, but due to the number of dependencies these things rely on, they’re executed in a “live” environment, which makes them hardly deterministic.

Microservices allow for a healthy Pyramid of Tests.

4 comments

This is absolutely a fallacy. If you're testing a microservice and stubbing the other microservices, you aren't doing the equivalent of the high-level test on the monolith. You're doing something like a macro-unit test of the monolith with internal services stubbed.
I think there's a tendency to believe that a microservice written by someone else can be replaced with a stub that just validates JSON or something.

But in my experience, that thinking leads to bugs where the JSON was correct, but it still triggers an error when you run real business logic on it.

It's an easy trap to fall into because that microservice exists to abstract away that business logic, but you can't just pretend it doesn't exist when it comes to testing.

So stubs may be good for unit tests, but only if there are integration tests to match.

It's also useful if the team providing the service can provide a fake or a stub with interesting pre-programmed scenarios, this reduces the number of false assumptions someone can make, is sort of a living documentation (assuming people update their fakes). Something like contract testing (Pact etc) can also be useful, although I haven't seen it being used in practice yet.
Microservice testing come with version combination hell.

If you have 10 microservices, each of which can be on one of two versions, that's 1024 combinations. How do you test that?

Sounds like the services shouldn't be split up into 10 if there's that much dependency going on.

Like, services are an abstraction. If one service has to call all other 9 services, and the same occurs for the other 9 services -- then that's a monolith acting over the network.

Im yet to see a system that consists of other versions of code than ”new” and “current”. You test against changes only, what you described is some mess in deployed versions / versions management.
How is this different from what I'm describing?

"New" and "current" are two different versions.

In that you always test against only the versions you have deployed + new version of single service.

Which downplays your exaggerated 1024 cases to 1.

OK, but then you have a very controlled way of deploying each service.

Each team can't just deploy a new version of their microservice when it makes sense to them.

So your collection of microservices becomes a bit of a distributed monolith, losing some of the classic microservice advantages.

Or so it seems to me. I just read about this stuff, have never used it. Happy to be educated.

Its losing „some” adventages of startup grade microservices and gain maintainability adventages of „netflix/facebook” level grid... Depends whats your scale. Shipping shit fast is often not the best solution at that scale, doing it right is. And I have already explained to someone else in this thread why that approach is important.
This is true for moderately-sized microservices. If your microservices are too small, though, it's essentially impossible to write integration tests of the overall system as a whole--any such test would need to spin up N different dependencies.
So a solution has now caused problems which were solved 30 years ago
> I find it hard, or improbable to achieve a healthy Pyramid of Tests on a large monolith

I'd venture to say that this is a strong indication that You're Holding it Wrong

> a high level, black box test of a service endpoint

Then maybe don't do these kinds of high-level black box tests?

Because...

> requires mocking external dependencies

...if you're stubbing out everything, then it's not actually a high-level, black-box test. So no use pretending it is and having all these disadvantages.

Instead, use hexagonal architecture to make your code usable and testable without those external connections and only add them to a well-tested core.

See: Why I don't Mock (2014) https://blog.metaobject.com/2014/05/why-i-don-mock.html

Discussed at the time: https://news.ycombinator.com/item?id=7809402