Hacker News new | ask | show | jobs
by throwaway894345 1421 days ago
On the contrary. An experienced architect defines the boundaries and then the microservice architecture enforced them in a way that a monolithic architecture cannot.
2 comments

On the contrary. Using libraries with clearly defined interfaces makes it easier to combine those libraries into a single executable, reducing deployment and debugging complexity. It also dramatically improves performance given the lack of intermediate encoding/decoding protocol code and TCP/UDP hops. Experienced seasoned Architects knows this and work hard to remove unnecessary distributed complexity from systems they design. While “beginner experts” always pick the most complex “solution” for the job at hand. The worst spaghetti code I have ever seen in my 30+ year career is a 20+ years old Microservices architecture. So I can’t wait to read the future “We had a problem so we chose micro-services. Now we have N distributed problems + K impossible to debug obscure timing issues” articles.
My 2c is that an experience architect would know that it would be ideal if they could do this and be right, but in practice they aren't likely to get those boundaries correct up-front and it takes time and experience working in the domain to see what those boundaries truly ought to be. Also, it's perfectly possible for monolithic applications to enforce boundaries as strict as microservices do via a "modular monolith" approach where the domain contracts are marked as public, but all the domain logic is marked as internal, and a framework of sorts enforces that each module only has access to either it's own schema in a shared database, or it's own separate database.

These days I think that's actually where everything should start off. With a good implementation of this you can transition relatively painlessly to a distributed system, and in the initial phases the simplicity of the build/test/deploy cycle massively lowers the overhead compared to a microservices approach while the strong isolation of domain logic gives you the ability to reason about changes to your domain not affecting other areas of the system.

Having worked in both small companies with terrible monoliths, small companies with terrible microservices, big companies with terrible monoliths and big companies with semi-decent microservices, and then implementing a modular monolith myself, I've seen enough to know that it's a solid, solid starting point. Most companies are pretty small, and the difference between an engineering org of 40 engineers trying microservices vs an engineering org of 4000 engineers doing microservices is on such a different level that people who haven't experienced both likely can't appreciate the data point that they're missing.

I love the strong isolation that microservices provide, and they can be super beneficial, but done at the wrong scale, the wrong level of technical maturity, or the wrong time and they're a huge drag. By the same token a monolith that grows too large for too long is equally a drag, but it's one that's exponentially more tolerable in an engineering org of 40 engineers than it is an engineering org of 4000 engineers. The key thing isn't which architecture you pick, it's the ability to be able to transition and adapt at the right point in time. Modular monoliths buy you this opportunity at a fraction of the price and for much less risk.

100% agree. You can write bad code using both Monoliths and Microservices. However Microservices are inherently more complex and slower than Monoliths (because of the added encoding/decoding and network overhead).
This is probably true if you have good engineering discipline in the monolith case such that your team adheres to good architecture. In the microservices case, as long as your architect is competent, you can draw sane lines between components and it's dramatically harder for IDGAF managers and developers to violate them. Everywhere I've worked, the microservices approach ends up being considerably simpler precisely because it makes it difficult to do things that are probably bad ideas in the first place (it's the same concept with static typing, by the way--a sufficiently smart team may not need explicit types, but in practice most teams aren't sufficiently smart, and the types function as rails to keep people from doing dumb things). Every place I've worked, the network and complexity overheads are paid for many times over by the simplicity and performance of preventing people from doing dumb things.

It's also worth noting that microservices are more secure and reliable as well, since there's no vulnerable component that has access to all of the secrets nor a bunch of components that ought to be non-critical but actually are critical because anything can bring down the entire application for all other components (e.g., someone makes a sync call in some leaf component that breaks the event loop for the whole application, with failures cascading across your replicas).

>It's also worth noting that microservices are more secure and reliable as well, since there's no vulnerable component that has access to all of the secrets nor a bunch of components that ought to be non-critical but actually are critical because anything can bring down the entire application for all other components (e.g., someone makes a sync call in some leaf component that breaks the event loop for the whole application, with failures cascading across your replicas

This is not inherently true. Whether it's true or not is down to your implementation. You can most certainly have reliability and security problems while doing microservices.

> This is not inherently true. Whether it's true or not is down to your implementation. You can most certainly have reliability and security problems while doing microservices.

Obviously I didn't claim "you cannot have reliability or security problems while doing microservices".

I think the critical aspect of whether or not you need them and hence whether or not they represent a good set of trade-offs, is on what dimensions are you optimizing for.

My previous employer was a small engineering org of around 40 engineers with a monolith of below average quality, trying to go in a microservices direction and not really having that pan out a great deal due to being unable to spare the engineering bandwidth to invest enough in managing the complexity while still having to deal with the monolith.

My current employer is an engineering org of around 4000 engineers that one two headed monolith that makes up the original product (web app + api share some code and are monoliths in their own right) and also very successfully uses microservices for everything else and there is a lot of them.

At my previous employer dealing with the monolith was fine even though the code was rather horrid, and we would release twice a week. The "microservices" I wound up building/working on there were not worth the overhead / complexity added at all and we would have been far, far, far better off investing in cleaning up the monolith to make it safer/easier to work in.

At my current employer dealing with the monolith is not fine. You have to book releases in a calendar, it's scary, it's giant, and despite multiple releases of it per day, there are so many other teams that book releases for it that you have to queue up to get your turn and it's done that way for safety. Yes, we also use feature flagging too. It's also vastly more difficult to know/find who you have to communicate with about changes to the monoliths because unlike a tiny engineering org, that information simply doesn't fit in your head. Here, the microservices we own are awesome in comparison. They are small code bases that are easy to reason about, our organization invested a massive amount in the technical maturity needed to operate, maintain, and be able to handle the complexity, and we have continuous deployment pipelines that mean we can safely release multiple times per day and when PRs are merged they are shipped all the way through to production in around 15 minutes.

So, when you say "more complex and slower" you have to ask yourself "than what?" There is no "one is better than the other", there is only "every tool is the right tool for some job" and "every tool is also the wrong tool for almost any other job it was not designed for" and your job is to understand exactly what tool your specific situation actually calls for.

This is why I like modular monoliths as a starting point, as they're essentially a multi-tool. A half-way compromise which is not as easy to use as a full-blown dedicated tool, but is versatile enough that if it's the only tool you have and you have no idea what job you're going to need to do, then you've probably made an adequate choice regardless of what the situation calls for in the end. And if you outgrow your multi-tool, you can much more easily swap it out for a set of it's dedicated equivalents.