Latest / DevQuestions with Tim Corey / 303. Why Monoliths Beat Microservices Almost Every Time
Transcript
- 0:00SPEAKER_01: People often talk about microservices as if they're a replacement for monoliths.
- 0:05SPEAKER_01: But that isn't the case.
- 0:06SPEAKER_01: In fact, a microservice architecture is almost always the wrong choice.
- 0:12SPEAKER_01: Yes, they absolutely have their place, but microservices often make an application worse, not better.
- 0:19SPEAKER_01: So let's talk about why in today's episode of Dev Questions.
- 0:31SPEAKER_00: Welcome to the Dev Questions Podcast with Tim Corey.
- 0:35SPEAKER_01: Software development is more than just writing code.
- 0:38SPEAKER_01: So let's talk about the rest of it.
- 0:40SPEAKER_01: Specifically, let's talk about why a monolith should be your default choice for new applications.
- 0:47SPEAKER_01: To understand how to make a decision like this, we first need to understand the goal of an application.
- 0:52SPEAKER_01: Our general goals are typically that our application is safe, performant, easy to maintain, and easy to understand.
- 1:02SPEAKER_01: Those are the four big guidelines for any application as long as it meets the goals of what our client or our company is looking for.
- 1:13SPEAKER_01: So you can have a safe, performant, easy maintained, easy to understand application that doesn't accomplish the goal of actually doing whatever you were asked to do.
- 1:23SPEAKER_01: But in terms of how an application should be built, those are our four pillars.
- 1:28SPEAKER_01: Make sure it's safe.
- 1:29SPEAKER_01: Make sure that when we capture customer data, that we don't give it away to malicious actors.
- 1:37SPEAKER_01: Make sure it's safe in that when we log in, we only see our data, not somebody else's.
- 1:43SPEAKER_01: That when we take in data, that we make sure that we save that data correctly so that it's not cross-saved with somebody else's data or mixed up or configured improperly so that the data doesn't go in correctly.
- 1:59SPEAKER_01: It gets garbled.
- 2:00SPEAKER_01: We don't want that.
- 2:00SPEAKER_01: That's safe.
- 2:02SPEAKER_01: So next is performant.
- 2:05SPEAKER_01: When we talk about performant, we talk about the idea that no user wants to wait for their application to be done processing.
- 2:12SPEAKER_01: This is why async is a big deal, because we can still use the UI while things are happening, potentially in the background.
- 2:20SPEAKER_01: So when it comes to performance, we want an application that doesn't take forever to run, that doesn't use up more resources than it has to.
- 2:28SPEAKER_01: Part of performance isn't just the pain that the end user goes through.
- 2:33SPEAKER_01: Part of it has to do with how much it costs to run our application.
- 2:38SPEAKER_01: This is very clear when you put your application in the cloud, where you pay for hard drive space, you pay for data transfer, you pay for CPU or processing time, memory usage.
- 2:52SPEAKER_01: And all of a sudden you go, whoa, these things are expensive.
- 2:55SPEAKER_01: Maybe I should make my application more performant in order to cut down these costs.
- 2:59SPEAKER_01: Now, if you run locally, you might not think about that until you start running into hardware limitations, where your server is maxed out on performance and can't go any faster, and you still can't get what you need to out of it.
- 3:14SPEAKER_01: That's when you go, oh, I need to buy a bigger server or more servers before you know it, you really have a problem, until you look at your application and go, hey, I could make this more efficient, and all of a sudden it would reduce the need of our server to run as hard and we can run more people on the same server.
- 3:32SPEAKER_01: Performance matters still.
- 3:34SPEAKER_01: Easy to maintain is about the idea that your application is absolutely going to break.
- 3:40SPEAKER_01: You're going to have bugs, you're going to have flaws.
- 3:43SPEAKER_01: And if it's hard to fix those things, if it's an environment where you're afraid to do anything, well, that's a problem.
- 3:51SPEAKER_01: You want to make sure that your application can be maintained easily so that when the inevitable bugs pop up, you can squash them quickly instead of taking forever.
- 4:02SPEAKER_01: And then easy to understand is when you come into the code base, this helps with maintenance.
- 4:07SPEAKER_01: You come into the code base and you look at the code and you go, yeah, I know what's going on here.
- 4:12SPEAKER_01: As opposed to, I have no clue, and it's going to take me a day just to figure out what's going on every time I look at a new bug.
- 4:21SPEAKER_01: Because if it takes forever to maintain, well, it's not easy to maintain, it also means your bugs aren't getting fixed as fast.
- 4:29SPEAKER_01: So those last two kind of go hand in hand, but they are different.
- 4:33SPEAKER_01: Easy to maintain and easy to understand.
- 4:35SPEAKER_01: So those are the four goals of any application, regardless of what the purpose is when it comes to business application.
- 4:43SPEAKER_01: Now, of course, games are a little bit different, but even so, performance, easy to maintain, easy to understand, and even safe.
- 4:50SPEAKER_01: You don't want your saved game on somebody else's account.
- 4:53SPEAKER_01: You don't want things cross-loading, et cetera.
- 4:57SPEAKER_01: So these are all important things for our applications.
- 5:01SPEAKER_01: So now we have the general goal in mind of any application.
- 5:05SPEAKER_01: Let's talk about what a monolith is.
- 5:09SPEAKER_01: So a monolith, all code is directly executed by the front end, even if it's in various DLLs.
- 5:16SPEAKER_01: So you can have different projects in a monolith, but all the code is executed by a front end.
- 5:22SPEAKER_01: Um, that front end talks to all the code.
- 5:25SPEAKER_01: Communication between various parts of the app are practically instantaneous because they're talking directly.
- 5:32SPEAKER_01: So it's just call a DLL.
- 5:34SPEAKER_01: That DLL calls maybe another DLL, but they just talk directly to each other.
- 5:39SPEAKER_01: Data is typically stored in one database, meaning you have a SQL database or a NoSQL database, but you have some database that stores your data.
- 5:49SPEAKER_01: And sure, you might have two or three depending on the types of data if you separate them out.
- 5:54SPEAKER_01: Maybe transactional data you store in one database versus um more customer data or whatever might be another.
- 6:02SPEAKER_01: But basically, you have one or two databases as your entire application to handle your entire application.
- 6:11SPEAKER_01: Also, code is typically written in one language.
- 6:15SPEAKER_01: Now, the front end is a bit different here because that's a whole other animal, right?
- 6:20SPEAKER_01: If if you have a.NET application, you expect your code to be all C sharp or other.NET language, such as VB or F sharp, but primarily it's one language, right?
- 6:29SPEAKER_01: Okay, C sharp if you're using C sharp, and that'll be for most of your code.
- 6:33SPEAKER_01: Now, again, the front end you'll have HTML, CSS, JavaScript, maybe even a framework or two, maybe even TypeScript.
- 6:41SPEAKER_01: So the front end gets a little messy there.
- 6:43SPEAKER_01: But otherwise, you expect one language.
- 6:47SPEAKER_01: Applications can be written with hundreds of thousands of lines of code or more.
- 6:54SPEAKER_01: This is very, very common.
- 6:55SPEAKER_01: In fact, one of the things people talk about is, oh, it's a big application, it has 20,000 lines of code.
- 7:01SPEAKER_01: That's not a big application, not in the real world.
- 7:04SPEAKER_01: When you actually get out in business and work with a business application, you will very easily find that you've got 20,000 lines of code behind one form.
- 7:13SPEAKER_01: That's unfortunately not an exaggeration and probably shouldn't be that way, but that's what you'll find.
- 7:19SPEAKER_01: And you'll find the actual application is more like 500,000 lines of code or a million lines of code or more.
- 7:26SPEAKER_01: Because applications get very, very large when you're dealing with actual business applications.
- 7:33SPEAKER_01: And then testing for a monolith can typically be done by just hitting F5.
- 7:38SPEAKER_01: You just run your application.
- 7:40SPEAKER_01: And even locally, you can do all of that with your one database, with maybe a caching service or two, but really it's just run this application and you're good to go.
- 7:53SPEAKER_01: That's what a monolith generally is.
- 7:56SPEAKER_01: Now, there's some exceptions, there's some edge cases.
- 7:59SPEAKER_01: We're not going to go into those, but that's a general look at what a monolith is.
- 8:05SPEAKER_01: Now, a microservice, a single microservice, isn't much different.
- 8:10SPEAKER_01: All the code gets executed by the front end.
- 8:12SPEAKER_01: Communication internally is done between methods, and it's practically instantaneous.
- 8:17SPEAKER_01: Data is typically stored in one database, code is written in one language, and applications are typically a hundred lines of code or less.
- 8:28SPEAKER_01: Yes, or less.
- 8:29SPEAKER_01: So that's a monolith.
- 8:31SPEAKER_01: That's a single, I'm sorry, a microservice.
- 8:32SPEAKER_01: That's a single microservice.
- 8:34SPEAKER_01: So it looks very much like a monolith except for the size.
- 8:37SPEAKER_01: But everything else, pretty much it's the same.
- 8:40SPEAKER_01: But where it's different is who executes the microservice.
- 8:44SPEAKER_01: For a monolith, it's typically the end user.
- 8:48SPEAKER_01: For a microservice, they typically interact with a messaging system that allows them to contact other microservices, usually indirectly.
- 8:56SPEAKER_01: So you would send out a message on a queue and then something else that you don't even know who is going to pick up that message in the queue and do something with it.
- 9:06SPEAKER_01: And that's where the big differences come in.
- 9:09SPEAKER_01: Testing typically requires a lot more setup.
- 9:12SPEAKER_01: This is where if you used Aspire, that really shines there because of the fact that it's able to wire up these various things together.
- 9:21SPEAKER_01: An action has to flow to multiple services over network boundaries.
- 9:27SPEAKER_01: So when the end user says, I want to buy this pair of shoes, that goes from one service to the next service to the next service to the next service to go through various parts of the purchasing process.
- 9:41SPEAKER_01: Different services have different databases.
- 9:45SPEAKER_01: In fact, that's almost a requirement of a microservice that has a different database than the other microservices.
- 9:53SPEAKER_01: If you have the same database, you're not really doing microservices, you're doing a distributed monolith.
- 10:00SPEAKER_01: And probably not well.
- 10:02SPEAKER_01: So that's not what you want.
- 10:03SPEAKER_01: You actually want separate databases.
- 10:05SPEAKER_01: That's more databases.
- 10:07SPEAKER_01: Different microservices can be written in different languages.
- 10:12SPEAKER_01: There's no reason why you can't write one microservice in Python, another one in C sharp, and another one in Java.
- 10:21SPEAKER_01: That's totally fine.
- 10:22SPEAKER_01: Because typically, different microservices have different teams maintaining them.
- 10:27SPEAKER_01: So whatever that team is comfortable with, that's what they can build that microservice in.
- 10:32SPEAKER_01: Because it's not about code talking to code, it's code talking across network boundaries to other applications using an intermediary, a messaging system.
- 10:43SPEAKER_01: Now, performance involves not just the code, but also the network and the various databases.
- 10:52SPEAKER_01: So there's a lot more distribution.
- 10:55SPEAKER_01: There's a lot more that can be scaled up or scaled down independently, but there's also a lot more hops to go through.
- 11:02SPEAKER_01: So let's talk again about our goals for our application, our general goals.
- 11:08SPEAKER_01: Number one is safety.
- 11:09SPEAKER_01: Well, hopping network boundaries, multiple databases, multiple sources of truth.
- 11:16SPEAKER_01: When it comes to safety, think that one through.
- 11:19SPEAKER_01: Because if your data, which is hopping across network boundaries, isn't secured between those two hops, or there's multiple hops, in there somewhere can be a man in the middle or a person reading those messages that could gather information that you don't want.
- 11:36SPEAKER_01: That's a problem.
- 11:37SPEAKER_01: That's an additional safety boundary to think about.
- 11:40SPEAKER_01: Having multiple databases meaning having has means having multiple different databases to secure.
- 11:47SPEAKER_01: You have to make sure that your security isn't just up to date and continually maintained on one database or one server, but on potentially dozens or even hundreds of different databases.
- 12:02SPEAKER_01: Then it comes to the safety of the data itself.
- 12:05SPEAKER_01: Remember, we talked about cross-cross-like mixing your data where I get data for somebody else.
- 12:11SPEAKER_01: Well, when it comes to having multiple databases, think about the source of truth.
- 12:17SPEAKER_01: Which database is the source of truth?
- 12:20SPEAKER_01: If two databases differ, which one is correct?
- 12:24SPEAKER_01: That's something that you're going to have to wrestle through and make sure that you do correctly.
- 12:29SPEAKER_01: Otherwise, the safety of your user's data is in jeopardy.
- 12:33SPEAKER_01: Now, performance.
- 12:35SPEAKER_01: Number two, let's talk performance when it comes to microservices.
- 12:39SPEAKER_01: Because we talk about how microservices can be very, very fast, 100 lines of code, they they spin up very fast, they have their own separate database so that they never have to wait on somebody else.
- 12:50SPEAKER_01: They're doing one task and doing it well, and can scale up and scale out, meaning we can have a more performance because of a bigger server for a model or microservice.
- 13:02SPEAKER_01: Or we can say, let's start up 15 of these microservices at once and have scale out where we have 15 running instead of one, which means that we can really increase the speed of that particular microservice.
- 13:19SPEAKER_01: However, network traffic is never as fast as direct code execution.
- 13:25SPEAKER_01: Remember in model lists, when one method wants to talk to another, it just executes it directly.
- 13:32SPEAKER_01: It's an in-app execution.
- 13:35SPEAKER_01: Well, network traffic will never be that fast.
- 13:39SPEAKER_01: It does not matter what type of cabling you have in your data center, it's never as fast as direct communication.
- 13:47SPEAKER_01: So there's going to be a speed decrease in communication between various microservices.
- 13:55SPEAKER_01: That's just a given.
- 13:57SPEAKER_01: But also, if you think about it, when service A says, I need this and sends a message out to the queue, well, that's a network hop, but it's also then going into a separate server, which is going to wait for something to pick up that message.
- 14:13SPEAKER_01: That may be instantaneous, that may take minutes if the service was down.
- 14:19SPEAKER_01: So that's another point of potential performance issue.
- 14:25SPEAKER_01: And then that next service has to pick it up and do something with it and potentially put it on the queue for somebody else or the initial service to pick back up.
- 14:34SPEAKER_01: That performance is never going to be as fast as if it was done directly inside of a library.
- 14:42SPEAKER_01: Number three is easy to maintain.
- 14:45SPEAKER_01: Well, it's not easy to maintain.
- 14:48SPEAKER_01: Let's put it that way.
- 14:49SPEAKER_01: Debugging is harder.
- 14:50SPEAKER_01: When you have a bit of data coming in from the front end and it goes from method to method to method to method inside of a monolith, you can trace that very, very easily.
- 15:02SPEAKER_01: It's very easy for the debugger to step through and watch that happen.
- 15:06SPEAKER_01: But when you're going across boundaries, that can be much, much more difficult.
- 15:12SPEAKER_01: Yes, every microservice is doing just a little bit.
- 15:15SPEAKER_01: And if you find out which microservice is a problem, it might be easier to solve.
- 15:20SPEAKER_01: But if multiple microservices add to the problem, well, now you've got a harder thing to debug because you're changing one service and then tracking down what that change does to the next service and so on in a cascading event.
- 15:34SPEAKER_01: And debugging across services is much more difficult than just debugging one service.
- 15:41SPEAKER_01: Now, it also involves more servers, more databases.
- 15:46SPEAKER_01: So trying to figure out, okay, the data is right in this database, but maybe it changes for that database and why does that happen?
- 15:52SPEAKER_01: Plus debugging why messaging might not be working or messages might be getting lost.
- 15:58SPEAKER_01: And then deployments are more confusing because it's not just do I deploy one microservice?
- 16:06SPEAKER_01: They should all be self-contained, and maybe that's okay.
- 16:10SPEAKER_01: But you make sure that if this problem affects multiple microservices, how do we deploy all of those together sometimes, but not other times?
- 16:19SPEAKER_01: That can make for a more confusing deployment scenario.
- 16:24SPEAKER_01: Now, number four, easy to understand.
- 16:29SPEAKER_01: For microservices, you literally need a map to understand the application.
- 16:34SPEAKER_01: And you might need to know multiple languages.
- 16:37SPEAKER_01: If you're debugging a problem that goes across microservices and affects multiple microservices, and maybe multiple microservices are adding to an overall problem, you might be debugging in Python and Java and C.
- 16:52SPEAKER_01: And then you might be working with multiple teams together to try and collaborate and figure out what's going on.
- 16:59SPEAKER_01: Plus, you're going to need to know multiple database types.
- 17:03SPEAKER_01: So if you use the Microsoft SQL Server, but this other microservice uses, let's say, MongoDB, well, that's a totally different way of thinking.
- 17:14SPEAKER_01: You'll need to be able to diagnose across those.
- 17:18SPEAKER_01: Maybe one uses SQL Server, one uses SQLite, one uses MySQL.
- 17:22SPEAKER_01: They're all SQL, but they're all a little different.
- 17:25SPEAKER_01: And so learning how to handle data across those, especially since their typing systems don't match up one-to-can can be a bit trickier.
- 17:35SPEAKER_01: And then debugging often includes complex setup work where you figure out how to get all these various databases up and running on your machine, or pull down just one service debugging locally, but then talk to the other services in production, but also not have any data leak back into production from your debugging.
- 17:55SPEAKER_01: There's a lot of work that goes into understanding how this all works.
- 18:01SPEAKER_01: So microservices, there's a lot more to it.
- 18:06SPEAKER_01: And the argument for microservices often revolves around improving performance, improving uptime and scalability.
- 18:13SPEAKER_01: And that can be true.
- 18:15SPEAKER_01: Remember that you can scale out one particular service as opposed to the entire system if one service is all that's needed.
- 18:24SPEAKER_01: Yet what most people forget is that there's a cost for these things.
- 18:28SPEAKER_01: So let's put this in a different perspective.
- 18:30SPEAKER_01: I live about 30 minutes from a major town, 45 minutes when traffic gets busy, which is typical actually.
- 18:37SPEAKER_01: Instead of driving, I could buy a helicopter and fly a town.
- 18:41SPEAKER_01: That would cut my travel time down to about 15 minutes.
- 18:45SPEAKER_01: But would that be a wise idea?
- 18:47SPEAKER_01: Almost certainly not.
- 18:49SPEAKER_01: I have an electric truck, but even if I were to pay for gas, it only cost me a few dollars to drive a town, more every day, unfortunately.
- 18:56SPEAKER_01: But a helicopter would probably cost five times that in fuel.
- 19:12SPEAKER_01: It gets even more complicated from there.
- 19:16SPEAKER_01: Now, if my time was worth tens of thousands of dollars an hour, it could make sense, but very few people's time is that valuable.
- 19:25SPEAKER_01: My time is not that valuable.
- 19:27SPEAKER_01: So the same is true with microservices.
- 19:30SPEAKER_01: You're creating this complex, expensive system to power your application just like that helicopter, but it doesn't really make sense.
- 19:38SPEAKER_01: Your break-even line is so far down the road that you'll probably never reach it.
- 19:44SPEAKER_01: Stack Overflow, even in its heyday, was a monolith.
- 19:48SPEAKER_01: Even when they were serving billions of page views a month, they still didn't need the benefits of microservices.
- 19:56SPEAKER_01: Architecting your application to support massive scale isn't forward-thinking.
- 20:01SPEAKER_01: It's short-sighted.
- 20:02SPEAKER_01: You are spending lots of resources to solve a non-existent problem.
- 20:07SPEAKER_01: Make a wiser choice and build a monolith.
- 20:10SPEAKER_01: Stop creating complexity without a solid reason why the complexity makes things simpler overall.
- 20:18SPEAKER_01: And you can't say that at some point we're going to need the scale.
- 20:22SPEAKER_01: Because at some point, that's a great problem to have.
- 20:26SPEAKER_01: But right now, what you need is to not have microservices.
- 20:31SPEAKER_01: What you need is just a monolith that works well.
- 20:34SPEAKER_01: You can make a monolith that's not performant.
- 20:37SPEAKER_01: You can make a monolith that has too many lines of code that do too few things that are too confusing and too hard to maintain.
- 20:43SPEAKER_01: That's on you, though.
- 20:45SPEAKER_01: What you can do is make that better.
- 20:49SPEAKER_01: What you really don't want to do is make a more complex situation where you end up with something that is harder to maintain, that is less performant, and it has no real recourse for making it better.
- 21:04SPEAKER_01: Okay.
- 21:05SPEAKER_01: So drop the complexity, focus on simple, powerful applications built as a monolith first.
- 21:14SPEAKER_01: You'll almost always, practically always, be making the better choice.
- 21:19SPEAKER_01: Don't think that you're an exception.
- 21:21SPEAKER_01: You're probably not.
- 21:22SPEAKER_01: All right.
- 21:23SPEAKER_01: Thanks for listening.
- 21:24SPEAKER_01: And as always, I am Tim Corey.
- 21:26SPEAKER_00: Thank you for joining us for this episode of Dev Questions.
- 21:29SPEAKER_00: When you're ready to learn to think and code like a professional developer, head over to IamTimCorey.com and enroll in a course.