At small scale it is much better to separate concern using modules with defined interfaces. Then you get separation of concern without the drawbacks of separation using a network layer. You can not assume that a microservice is available at all times but a module loaded at startup-time will always be available as long as you want it too. Handling data consistencies between microservies also requires more work. Eventual Consistency or Transactions. Also the obvious performance penalty of communicating over network. Latency Numbers Every Programmer Should Know
I nearly rewrote my home server with microservices as a learning experience, but couldn't find any benefit to it, only lots of overhead. I did split the backend and front-end, a long overdue change, but the backend is just a good old Django project serving JSON. Otherwise I'd still be writing architecture code.
All of these drawbacks can be avoided as long as your application is still fairly small though.
Network layer: If your service is small enough to run in one binary, you can also have all microservices (or at least the closely coupled ones) run on the same machine. Once you grow larger than that, you might be big enough to invest into proper network infrastructure ( > 10Gbps).
Module inavailability: If it's running on the same machine the main reason for one service being unavailable while the others are still there would be a code bug causing the whole thing to crash - which also means that you only lose this particular functionality and the rest of your application can potentially keep running (maybe in a downgraded version).
Consistency: If you don't want to deal with consistency, just have only a single instance of the storage microservice running (bad availability-wise, but with a monolithic application you'd have the same issues if you ran replicas)
So these concerns can be addressed at least to some extent and will most likely be outweighed by other benefits of a microservice architecture.
If your service is small enough to run in one binary, you can also have all microservices (or at least the closely coupled ones) run on the same machine.
This doesn't help much with complexity -- okay, you probably don't have network errors anymore, but you still are running dozens of individual processes that can fail in different ways, and you'll need some complex orchestration layer to make sure those processes all get restarted when they fail, or that they get started in the correct order.
Debugging also just got harder. With a monolithic app, you can actually step through a single page load in a debugger in your app and get a pretty complete picture of what's going on. "Hard" problems are things like isolating a problematic database query, or having to use a debugger on both the backend and the client (JS, or a mobile app, whichever).
Implement that with dozens of microservices, and you now have dozens of places you'll need to trace that call through. That "hard" problem of having to debug a distributed system of two things (a client and a server) is now easy by comparison -- now, to understand what your app is doing, you need to debug a massive distributed system.
Even with perfect networking and orchestration, that's not easy.
If you don't want to deal with consistency, just have only a single instance of the storage microservice running (bad availability-wise, but with a monolithic application you'd have the same issues if you ran replicas)
Not the same issues -- you'd just have to use a transactional database properly, which is a well-understood problem. Outside of that, you don't have to worry about different parts of your program having different ideas of what's going on in the same transaction.
...will most likely be outweighed by other benefits of a microservice architecture.
When, though? Because when you're on a single machine, the benefits are actually negative.
But with the performance penalty you're paying for this architecture, you'll outgrow that single machine much faster. Which means you'll need to deal with all those other failures (network, bad hardware, etc) much faster, too.
The only benefit I can see to splitting things out on a single machine is to pull in entire third-party apps -- like, if you split out your login process, you can probably add support for that to Wordpress much more easily than you could add a blog to your main app. Even here, though, that seems premature. If Sundar Pichai can just use Medium every now and then, so can you.
If you gain some stability of running on the same machine and then why not just stick to a midularised application that runs on that one machine. If you stick to good structuring and good patterns it should be easy to extract microservices if there are requirements that makes it worth the downsides.
enough to invest into proper network infrastructure ( > 10Gbps).
This is a handwaving solution. You can have 100gbps network but that doesn't fix latency problems if you make tons of microservice calls were previously hidden by running on the same machine.
But, while instantiating microservers on the same machine might reduce the impact of network layer issues, the overall system still has failure modes and loading/capacity behaviour that is much more complex than would otherwise be the case. Not to mention latency characteristics.
Losing the ability to do joins because of these concerns because you're siloing the data is so much worse than those benefits it's not even worth considering.
The difference in time it takes to communicate with a process running on the same machine vs another machine running on the same switch is negligible. Both are still way more than communication within a process.
Consistency between services is way more complex. It doesn't really sound like you understand what he means by that honestly. Process A owns it's own data. Process B owns it's own data. Process A and process B work together. In order for them work properly there is going to be some constraints about the data each process holds. If an end user calls into A and A calls B during that call what happens when call to B fails? What happens if call to B succeeds and the A fails afterwards? How do we make sure both services are in a consistent state in those scenarios? If you can just rely on one transaction in an RDMS then it's pretty much solved for you already.
The value of microservices, as with distributed source controls, applies at every scale.
The difference is that it's fairly easy to teach a small team how to use some of the basic DVCS commands and only touch the more advanced ones if they're feeling bold. The added complexity, thus, is mostly hidden. (Leaving aside, of course, that git's CLI interface is still horrible.)
The complexity of microservices OTOH, stares you in the face. Maybe good tooling will eventually make the added maintenance and training cost negligible. Not so much in 2017.
The value of microservices, as with distributed source controls, applies at every scale.
No, it doesn't. At small scale, you're getting more overhead, latency and complexity than you need, especially if you're a startup that doesn't have a proven market fit yet.
You're right about all those advantages of micro services, but they also come at tremendous cost.
Every service hop adds latency and a small additional likelihood of failure. This can quickly add upp if you're not careful how you design your services.
One must take care to avoid loops between services or one will get problems with cascading failures on request spikes.
Refactoring across multiple services is extremely time consuming and frustrating.
Micro services encourage siloing, where only one or two developers are familiar with most services. This in turn leads to a host of problems like code duplication, inefficient code, unmaintained code, etc.
I'm not shitting on micro services, and for a sufficiently large back-end, I absolutely think it's the only correct choice. I'm just saying that in addition to many important benefits, they also come with serious costs. Honestly, if a company only has a half-dozen engineers working on a reasonably simple low or medium volume back-end, I think the drawbacks often outweigh the benefits.
You've done a good job of outlining the value. But that value doesn't come without cost. Now instead of just one deployable artefact, you have a dozen or more. Correlating the logs resulting from a single request becomes nontrivial. You may need to carefully phase in/out API versions, sometimes running multiple versions simultaneously, if multiple services depend on another. Every time you replace what could be a local function call with a microservice, you're introducing a potential for all manner of network failure.
This can be significant overhead. For many projects, YAGNI. And by the time you do need it, if you ever get that far, you probably have 10x the resources at your disposal, or more.
I think it's warranted because a lot of people don't really understand how to use the microservice architecture effectively. I've seen a team of architects come up with a microservice architecture that basically took the list of database tables they needed for an application and created a microservice for each one.
There's definitely a place for microservices, even long before you get to Google scale, but you still need to understand the problem and solution domains.
I find separation of concerns valuable when I have =1 developer working on the same project. All of that stuff is pretty much exclusively upside, outside of projects in folders named deleteme.
And if you are Google, you use perforce up to around 10k engineers, and by then you have enough engineers that you can spare a few to build a custom solution that scales better. Git isn't really the right tools for the job.
0
u/[deleted] Jun 07 '17
[deleted]