Latest / DevQuestions with Tim Corey / 230. Premature Optimization vs Careful Planning - How To Optimize Your Application
Transcript
- 0:00SPEAKER_01: When to optimize your application is an important thought to wrestle with as you build out your app.
- 0:06SPEAKER_01: On one hand, you should write good code that is performant.
- 0:10SPEAKER_01: On the other, you might prematurely optimize code that doesn't actually save you any time in the end, and it might actually cost you to do so.
- 0:19SPEAKER_01: What are some things to consider when attempting to build good code?
- 0:23SPEAKER_01: That's what we're going to talk about in this episode of DevQuestions.
- 0:34SPEAKER_00: Welcome to the DevQuesti podcast with Tim Corey.
- 0:38SPEAKER_01: Software development is more than just writing code.
- 0:42SPEAKER_01: So let's talk about the rest of it.
- 0:43SPEAKER_01: Specifically, let's talk about how to optimize your application code without doing too much.
- 0:50SPEAKER_01: So when should you optimize your code?
- 0:52SPEAKER_01: The answer is, of course, that it depends.
- 0:55SPEAKER_01: So here's the framework that I use to help me make decisions specifically in each application and every bit of code.
- 1:05SPEAKER_01: So number one, write good code, which of course makes sense, right?
- 1:10SPEAKER_01: But avoid things like boxing or unboxing variables, string concatenation in a loop, and using and make sure you use using statements in the proper way to dispose of resources when you're finished with them correctly.
- 1:24SPEAKER_01: So these are what I'd call good code.
- 1:27SPEAKER_01: Things that you say, yeah, I know that if I try and concatenate a million strings together in a loop, that would be bad.
- 1:35SPEAKER_01: So let's not do that.
- 1:36SPEAKER_01: We don't have to call that premature optimization to not write bad code or code we know is not going to perform it.
- 1:43SPEAKER_01: Now, at the same time, you need to think through not making any of these things rules.
- 1:48SPEAKER_01: For example, is string concatenation bad?
- 1:52SPEAKER_01: No, it's not.
- 1:53SPEAKER_01: And in fact, sometimes the best solution in your code is string plus string.
- 2:00SPEAKER_01: Yeah, I know.
- 2:01SPEAKER_01: That's the way everybody says, don't do this.
- 2:03SPEAKER_01: That's horribly performant.
- 2:05SPEAKER_01: Sure it is, if you're doing it 10,000, 100,000, a million times in a loop.
- 2:10SPEAKER_01: But if you're doing it in a one-off situation, it's so much better than trying to create a string builder and all the rest, because that actually adds more overhead and makes your code slower because you didn't think through your specific case.
- 2:25SPEAKER_01: Don't create rules, create guidelines, create guardrails, and try and write the best code that you can for a given situation.
- 2:34SPEAKER_01: So if you know something is not performant and there's a better way of doing things, try and do it that way, but within reason.
- 2:42SPEAKER_01: Know why it's not performant and know when it's okay to use that.
- 2:47SPEAKER_01: Okay?
- 2:48SPEAKER_01: So yes, that takes a lot of in-depth knowledge.
- 2:50SPEAKER_01: It takes a lot of understanding of how the code works.
- 2:53SPEAKER_01: This is the depth of your understanding of a language.
- 2:56SPEAKER_01: It's not just can I do something, it's when should I do something.
- 3:01SPEAKER_01: That's why I say learn a language deeply, don't just learn the surface.
- 3:06SPEAKER_01: Because the surface tells you concatenate strings together.
- 3:10SPEAKER_01: That's not good in all situations.
- 3:12SPEAKER_01: But then the next level down says never concatenate strings together.
- 3:17SPEAKER_01: That's also not necessarily a good thing.
- 3:20SPEAKER_01: So knowing how the how concatenation works, what the performance is like, will allow you to make a better choice because you know when and when not to.
- 3:31SPEAKER_01: And this is why, yeah, it's gonna take some work to get to know these things.
- 3:35SPEAKER_01: And if you don't know, don't try to optimize up front.
- 3:39SPEAKER_01: Okay.
- 3:39SPEAKER_01: So don't just go and watch a top 10 tips video on how to make your code more performant and then just start throwing stuff in at there at the wall to see if it works.
- 3:50SPEAKER_01: Because you don't know yet how your app is gonna perform, and that can actually make things worse, not better.
- 3:56SPEAKER_01: So write good code.
- 3:58SPEAKER_01: And if you don't know how to write code, good code, write the best code you know how to write.
- 4:02SPEAKER_01: Okay?
- 4:03SPEAKER_01: That's all you have to do.
- 4:05SPEAKER_01: Now, number two, write modular code.
- 4:09SPEAKER_01: So don't tie your logic to the UI, use dependency injection in most cases.
- 4:15SPEAKER_01: Don't create huge methods that do too much.
- 4:18SPEAKER_01: These are things that all, if you do them wrong, will create this one big ball of mess that's really hard then to figure out how to fix the performance on when you have a performance issue.
- 4:29SPEAKER_01: So write modular code, because then you can pull a piece out and say, oops, that's not a good piece.
- 4:35SPEAKER_01: Let's fix that piece and not have to fix the entire ball of mess.
- 4:40SPEAKER_01: Okay?
- 4:40SPEAKER_01: So make sure you write modular code that's can be pulled out.
- 4:45SPEAKER_01: Don't go too far.
- 4:46SPEAKER_01: Don't say, oh, Tim just said write microservices for everything.
- 4:50SPEAKER_01: That's not what I'm saying.
- 4:52SPEAKER_01: I am not saying that write code that's so disconnected that in order to do anything, you have to go through 15 systems.
- 5:00SPEAKER_01: That's also not wise.
- 5:02SPEAKER_01: Because that is a major performance hit, not just in the performance of your application, but also in the performance of your developers trying to debug your application.
- 5:14SPEAKER_01: Don't just do that.
- 5:15SPEAKER_01: That would be premature optimization.
- 5:17SPEAKER_01: So writing modular code means you can still write a monolith, but write your monolith in a way that pieces could be pulled off or changed, which is why depends on the injections in there, because hey, you've got an emailing system that's part of the monolith, but it's not that great.
- 5:34SPEAKER_01: We want to replace it.
- 5:35SPEAKER_01: No problem.
- 5:37SPEAKER_01: We all work towards the interface.
- 5:39SPEAKER_01: We can replace the implementation with a better implementation, and there you go, performance fixed.
- 5:45SPEAKER_01: So thinking through how to write your code in a way that's loosely coupled without going too far.
- 5:52SPEAKER_01: Don't go down the rabbit hole of having every design pattern ever applied to your system to make sure it's super modular.
- 5:59SPEAKER_01: Don't do that.
- 6:00SPEAKER_01: Okay.
- 6:01SPEAKER_01: Don't do anything to excess.
- 6:03SPEAKER_01: Okay.
- 6:04SPEAKER_01: Write things that are easy to pull apart, but not so complicated because of that, they add a whole lot of extra overhead.
- 6:12SPEAKER_01: There's a balance, which is why we say it depends.
- 6:15SPEAKER_01: Now, number three, get realistic real-world data.
- 6:21SPEAKER_01: Okay.
- 6:21SPEAKER_01: And what do I mean by this?
- 6:23SPEAKER_01: Well, at first, this might just be sample data that's at scale, meaning you're going to build an application, let's say to um, I don't know, you you want to have a ticketing system for getting people into the state fair.
- 6:37SPEAKER_01: Okay, so you're gonna build a ticketing system.
- 6:39SPEAKER_01: Well, then put 100,000 people in your database to start, not just one or two.
- 6:45SPEAKER_01: Because if you have 100,000 people, well, then you have a volume to work with.
- 6:49SPEAKER_01: So you may find out that, oh, we're pulling back all the people into a dropdown for selection.
- 6:56SPEAKER_01: Well, that's not gonna work because we now have 100,000 people in the dropdown.
- 6:59SPEAKER_01: If you had 10 people in a database, that'd be just fine.
- 7:02SPEAKER_01: So putting some kind of realistic data in your database will allow you to better understand how your application is gonna work.
- 7:10SPEAKER_01: Now, later, when you have an application that's actually up and running, my best recommendation is to take a copy of the production database and use that in your development.
- 7:23SPEAKER_01: Now, that may mean, it probably will mean, that you have to clean some things out.
- 7:28SPEAKER_01: For example, maybe you're storing sensitive information about a user, maybe their email address and their phone number.
- 7:35SPEAKER_01: Well, don't put those in your development database, but just change them.
- 7:39SPEAKER_01: So change them all to be 5551212 and call it good.
- 7:45SPEAKER_01: Or have a random number that gets assigned for a phone number and the same with the email addresses.
- 7:49SPEAKER_01: There's systems out there that will help you with this if you really need help.
- 7:52SPEAKER_01: But make sure that as much of the data as possible is the same because you want to see all those edge cases that have been generated by real-world users.
- 8:05SPEAKER_01: You want to find out all the people who put in a blank address one field and filled out address two.
- 8:12SPEAKER_01: You want to find all the people who put a zip code in that is longer than a zip code should be, or that's shorter than it should be, or where they put the phone number in the zip code field and the zip code in the phone number field.
- 8:25SPEAKER_01: Like you want to find all these things because you want real-world data.
- 8:30SPEAKER_01: This is important for understanding how to make sure that you write code that's going to handle these things and going to do so in a performant way.
- 8:43SPEAKER_01: So if you have code at scale that, or I'm sorry, data at scale, then when you test your code, it will more likely perform like it does in production.
- 8:54SPEAKER_01: This is also why it's helpful to have production level resources or similar to production level resources for your development.
- 9:03SPEAKER_01: So you can kind of test how it's going to work.
- 9:05SPEAKER_01: And maybe you're not going to get the same number of processors, same number of RAM, et cetera.
- 9:10SPEAKER_01: That's okay if you scale it down.
- 9:13SPEAKER_01: Because that way your development is going to be more constrained than production.
- 9:18SPEAKER_01: If it takes up 50% of the processing power in development, then it might only take up 20% of the processing power in production.
- 9:27SPEAKER_01: That's a good thing.
- 9:28SPEAKER_01: But it will also help identify the areas where you can improve things.
- 9:33SPEAKER_01: So now that you have things running in development, they've got some bottlenecks.
- 9:38SPEAKER_01: Figure out where those bottlenecks are.
- 9:40SPEAKER_01: Now, ask for what you need.
- 9:44SPEAKER_01: When working with data, this is number four, only ask for the data you need.
- 9:51SPEAKER_01: So this is one that really just burns me up sometimes.
- 9:57SPEAKER_01: People love data grids.
- 9:58SPEAKER_01: The developers are in love with data grids.
- 10:01SPEAKER_01: Gotta have data grid, gotta have a data grid.
- 10:03SPEAKER_01: I'm gonna put a data grid everywhere.
- 10:04SPEAKER_01: Where's the data grid control?
- 10:06SPEAKER_01: That's the thing that I hear a lot.
- 10:08SPEAKER_01: And so they build these web applications and say, well, I need to put all the user information in that data grid.
- 10:16SPEAKER_01: Why?
- 10:17SPEAKER_01: The user's not gonna see it.
- 10:19SPEAKER_01: So you're loading 100,000 records because the user is gonna see a data grid that's gonna have 20 on the screen at any one time.
- 10:28SPEAKER_01: Do you really think the user is gonna go through the however many pages it's gonna be to get to that 100,000th user?
- 10:36SPEAKER_01: No.
- 10:37SPEAKER_01: So why are you loading all that data?
- 10:39SPEAKER_01: Even worse is when you try to load all the user's data that you're not even displaying in that data grid.
- 10:48SPEAKER_01: For example, maybe you're loading employees.
- 10:51SPEAKER_01: You have an employee list.
- 10:52SPEAKER_01: Let's be reasonable and say you have only a thousand employees in your database, and you're loading that into the data grid a web application.
- 11:01SPEAKER_01: Again, don't do this, don't do this this way.
- 11:03SPEAKER_01: But let's say you download a thousand users or employees.
- 11:07SPEAKER_01: Well, you're displaying their employee ID number, their first name, their last name, maybe their email address.
- 11:12SPEAKER_01: That's uh four pieces of data.
- 11:14SPEAKER_01: How many pieces of data are you downloading?
- 11:17SPEAKER_01: Well, we've got to have their home address and their work address, or their home address and their um mailing address, and we've got to have their home phone number, you're gonna have their relationship, who their boss is, who their subordinates are.
- 11:29SPEAKER_01: We're gonna all of a sudden you've got a hundred pieces of data loaded for that user.
- 11:34SPEAKER_01: You might say, well, Tim, yes, but when the user clicks on one of those people, they need to see all the data for that user.
- 11:42SPEAKER_01: Well, yes, how many times are gonna click a user?
- 11:45SPEAKER_01: Are they gonna click all 1,000?
- 11:48SPEAKER_01: No.
- 11:48SPEAKER_01: They're gonna click two, three, four.
- 11:51SPEAKER_01: Well, then load those records when you need to and only download the four pieces of data you actually need.
- 11:59SPEAKER_01: So this is a massive performance difference.
- 12:03SPEAKER_01: When you're asking for just four pieces of information, probably from one table, it's going to have a much easier time getting that from the database than if you try to load multiple objects with massive amounts of data and then try and store that in the web client.
- 12:20SPEAKER_01: So just asking for what you need is in itself a massive performance change.
- 12:26SPEAKER_01: So when you're looking at how to make my how to optimize your application, look at what you're asking for for data and if you really need all of that data.
- 12:36SPEAKER_01: When you have, again, a data grid on a web application or even a desktop application, usually you show, let's say, 20 records, and then you have a next button.
- 12:46SPEAKER_01: Why can't you load two pages of information, not all 500 of them?
- 12:52SPEAKER_01: And then when they get to the second page, you load the third page behind the scenes.
- 12:56SPEAKER_01: And maybe you even load one page each side side.
- 12:59SPEAKER_01: That way, if they ever get a page four and they go, oops, nope, page three, they back up one, it's still loaded in memory.
- 13:05SPEAKER_01: Because networks are fast, databases are fast, you'll be okay even if you load the information as they asked for it, but you could even look ahead a page and behind a page and still be loading a whole lot less data than the entire 500 pages worth of data.
- 13:24SPEAKER_01: Now, number five in how to optimize your application is understanding your data access layer.
- 13:31SPEAKER_01: Again, developers often tell me, Tim, I don't need to understand how my data comes out of the database.
- 13:38SPEAKER_01: I just know entity framework.
- 13:40SPEAKER_01: Or I just know ORM of choice.
- 13:43SPEAKER_01: That's not good enough.
- 13:44SPEAKER_01: Or I have a person to do that.
- 13:47SPEAKER_01: Not good enough.
- 13:48SPEAKER_01: You need to understand your data access layer.
- 13:51SPEAKER_01: Know how, let's just say you're using SQL, know how SQL works and how to write good TSQL commands.
- 13:59SPEAKER_01: So avoid open-ended like statements.
- 14:02SPEAKER_01: So if you say, you know, where last name equals, you know, last name, like, you know, and then percent, corey, percent, that'd find all the people that have core in their last name and anywhere in that name.
- 14:14SPEAKER_01: That sounds great, but that's gonna be a massive issue.
- 14:17SPEAKER_01: He's gonna have to seek through every single row in your database, in your table, because of the fact that you started with a percent sign.
- 14:26SPEAKER_01: Well, if you started with Corey, which would only find things that were Corey or Corey and then something at the end of them, well, then that can really narrow down the number of records you're looking for.
- 14:37SPEAKER_01: So, yes, maybe a search of everything is important, but there's systems to do that.
- 14:44SPEAKER_01: And knowing how to do that well is really important.
- 14:47SPEAKER_01: And not doing that when you don't absolutely have to is important.
- 14:52SPEAKER_01: Knowing how the queries are actually written, even if you use an ORM like entity framework, is important.
- 15:00SPEAKER_01: So knowing that too many joins, that's gonna cause a problem as well.
- 15:05SPEAKER_01: Also, another thing.
- 15:06SPEAKER_01: So knowing about known performance issues and avoiding them up front will be really important.
- 15:13SPEAKER_01: And then knowing how to test your queries and test the performance is also important once you have real data.
- 15:21SPEAKER_01: Now, step six is where you perform preliminary testing.
- 15:26SPEAKER_01: Okay, so so far we've been kind of just looking at the application in a you know a high-level overview uh picture.
- 15:33SPEAKER_01: We're looking at making sure our data is pretty good, we're making sure that we're writing good code, we're making sure that you know we're doing the big picture things we know we can do that are always going to give us some performance improvements.
- 15:46SPEAKER_01: But number six is performing preliminary testing.
- 15:50SPEAKER_01: So we look for bottlenecks and other large issues.
- 15:54SPEAKER_01: Don't solve tiny problems.
- 15:57SPEAKER_01: One of the things I love and also is frustrating to me is when people find that performance graph, they go, ooh, I've got all these bottlenecks.
- 16:06SPEAKER_01: And some of them are, you know, there's one bottleneck that takes up, you know, three seconds of load time, and the next one takes up 0.1 seconds of load time.
- 16:13SPEAKER_01: They're like, I can fix that 0.1.
- 16:15SPEAKER_01: And you're like, cool story, bro, but that's not helping us because it doesn't matter if you make a 0.1 into a 0.05 if you've got a three-second bottleneck.
- 16:27SPEAKER_01: Find the big bottlenecks, fix those.
- 16:30SPEAKER_01: Because the little ones probably don't matter.
- 16:32SPEAKER_01: And it probably isn't worth the development time to try and optimize the little things when you can get the big things out of the way and fix those big issues and make them better.
- 16:44SPEAKER_01: Number seven is evaluate your application in production.
- 16:49SPEAKER_01: People miss this.
- 16:50SPEAKER_01: You want to re-evaluate your application once it gets into production because production is when things happen that you don't expect.
- 16:59SPEAKER_01: And you might have different hardware, you might have different setup, you might have different interactions that cause different problems.
- 17:07SPEAKER_01: So, what's a bottleneck in development might not be a bottleneck in production, or it might be a different comparatively bottleneck in production versus something else.
- 17:17SPEAKER_01: So identify what's actually happening in production.
- 17:21SPEAKER_01: Look for where the biggest slowdowns occur and address those.
- 17:26SPEAKER_01: Test against the data and then deploy the fixes.
- 17:29SPEAKER_01: Okay, so what you're looking for is real-world issues.
- 17:34SPEAKER_01: You might say, well, Tim, waiting until production is too late.
- 17:37SPEAKER_01: No, it's absolutely not.
- 17:38SPEAKER_01: This is the perfect time to find these types of issues.
- 17:43SPEAKER_01: You've already gone through six steps of pre-production, but now it's time to look in production.
- 17:50SPEAKER_01: Because what happens in development is not the same as what happens in production.
- 17:54SPEAKER_01: And you're gonna find new things and find new issues that are gonna pop up because of different data, because of the the user load because of a different server, whatever the case may be, evaluating your app in production is really important.
- 18:11SPEAKER_01: And then number eight, evaluate slow running queries.
- 18:16SPEAKER_01: Find out what's taking the majority of your SQL resources.
- 18:19SPEAKER_01: Again, I'm assuming you're using SQL Server.
- 18:21SPEAKER_01: If you're using something else like MongoDB, do the same thing there, but in Mongo.
- 18:25SPEAKER_01: So for instance, is it one slow query or one fast query that's running thousands of times?
- 18:35SPEAKER_01: This is really important because you can do a search on SQL and say, hey, what's my slowest running query?
- 18:41SPEAKER_01: But that's not gonna tell you the right information.
- 18:44SPEAKER_01: Because you might find a query that that runs for 10 seconds.
- 18:49SPEAKER_01: You might say, ha, I can fix that, I can cut 10 seconds out, or two minutes.
- 18:54SPEAKER_01: It might run for two minutes, and you're like, I can fix that, I can make it run in 10 seconds.
- 18:58SPEAKER_01: That'd be great.
- 18:59SPEAKER_01: And you know what?
- 19:00SPEAKER_01: That might be worth it.
- 19:01SPEAKER_01: But if that's running once a day because it's running a report and it's running at night in a batch job, really who cares?
- 19:08SPEAKER_01: Whereas you might have a query that runs in under a second, but it runs thousands of times a day.
- 19:16SPEAKER_01: And so cutting out 50 milliseconds can have a much greater impact from that query than from fixing the query that's running for two minutes.
- 19:29SPEAKER_01: So understanding the volume of a query execution as well as the time it takes and putting those two numbers together, which you can do in SQL, and you can do in other languages or other databases as well.
- 19:43SPEAKER_01: But knowing that will allow you to make informed decisions about what to optimize.
- 19:49SPEAKER_01: It's not just the slowest query, it's not the biggest query, it's not the query that runs the most times.
- 19:56SPEAKER_01: It's kind of a combination of all those things to figure out what's the thing that's taking the most server time, what's the thing that's causing us the most problems.
- 20:05SPEAKER_01: And from that, you optimize, you test, and you try it again.
- 20:11SPEAKER_01: And then next week, you do the same evaluation over again and run that same report and figure out now what's the worst performing query or set of queries and how can you optimize those.
- 20:24SPEAKER_01: So, number nine, continue to re-evaluate and compare your fixes against the baseline in production.
- 20:32SPEAKER_01: Development is not a good place to find performance issues, nor is it a great place to do final validation of a fix.
- 20:39SPEAKER_01: Do the best you can in development, but you need to evaluate your changes in production as well.
- 20:44SPEAKER_01: Don't just say, well, it worked in development, therefore it must work in production.
- 20:48SPEAKER_01: You need to also check in on it and make sure that your assumptions are still correct.
- 20:54SPEAKER_01: The changes you make to an application in the name of performance often make your application code harder to read and understand.
- 21:02SPEAKER_01: Make sure you're making the changes that are necessary rather than just making changes based upon an assumption.
- 21:09SPEAKER_01: Otherwise, you can make your application harder to maintain, and sometimes you can even make the performance worse.
- 21:16SPEAKER_01: That's why testing and validation is so important.
- 21:19SPEAKER_01: Thanks for listening, and as always, I am Tim Corey.
- 21:23SPEAKER_00: Thank you for joining us for this episode of Dev Questions.
- 21:26SPEAKER_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.