WEBVTT 00:00:00.001 --> 00:00:05.000 Hello and welcome to Python Bytes, where we deliver news and headlines directly to your earbuds. 00:00:05.000 --> 00:00:11.260 This is episode 174, recorded March 18th, 2020. I'm Brian Okken. 00:00:11.260 --> 00:00:12.160 And I'm Michael Kennedy. 00:00:12.160 --> 00:00:18.000 And this week, this episode is brought to you by Talk Python Courses and the pytestBook. 00:00:18.000 --> 00:00:19.600 Yeah, yeah, it's brought to you by us. 00:00:19.600 --> 00:00:21.060 Yeah, us. 00:00:21.060 --> 00:00:21.980 More about that later, huh? 00:00:21.980 --> 00:00:26.660 Yeah. So we're doing something a little different. We're recording in two different locations 00:00:26.660 --> 00:00:31.080 because of... Actually, we always record in two different locations, but... 00:00:31.080 --> 00:00:35.400 But the locations are sometimes not the location, especially the location you're in. 00:00:35.400 --> 00:00:41.860 Yeah, I often record somewhere else. But today I'm at home because a lot of people are at home 00:00:41.860 --> 00:00:47.620 working remotely in home offices now because of... I don't even know how to pronounce it. 00:00:47.620 --> 00:00:49.420 I read it. COVID-19. 00:00:49.420 --> 00:00:56.620 Yeah. It is an insane time on so many levels. But I would say certainly... 00:00:56.620 --> 00:01:00.960 There's a lot of tech people out there who may be working from home for the first time. 00:01:00.960 --> 00:01:06.560 You know, I know there's a lot of large companies that feel like you need to go to be in the office 00:01:06.560 --> 00:01:13.080 and you need to do the work. And yet a lot of the tools that we use as developers are very suited 00:01:13.080 --> 00:01:16.660 to the situation that many of us around the world find ourselves in. 00:01:16.660 --> 00:01:20.400 Working from home, working asynchronously and whatnot, right? 00:01:21.000 --> 00:01:25.420 GitHub, Slack, email, Zoom, whatever it is. 00:01:25.420 --> 00:01:30.460 It's interesting to see the rest of the world scale up to kind of, you know, what we've been doing for a long time. 00:01:30.460 --> 00:01:35.740 We were lucky that our office was recently moved in July. 00:01:35.740 --> 00:01:40.860 And during the move, we tried to set everybody up to be able to remote work 00:01:40.860 --> 00:01:44.360 because some people had longer commutes than before. 00:01:45.160 --> 00:01:46.300 And it happened to be... 00:01:46.300 --> 00:01:49.260 I mean, it's just fortunate that we set that up before this happened. 00:01:49.260 --> 00:01:52.140 And I'm also very fortunate that I'm a software worker. 00:01:52.140 --> 00:01:53.580 There's a lot of people that... 00:01:53.580 --> 00:01:58.480 I mean, our work can continue for the most part with little interruption. 00:01:58.480 --> 00:02:01.240 But it's a harder environment. 00:02:01.240 --> 00:02:05.500 But a lot of people that are not technical workers can't do that. 00:02:05.500 --> 00:02:05.740 Yeah. 00:02:05.740 --> 00:02:06.920 It's such a bummer. 00:02:06.920 --> 00:02:09.220 You know, my daughter, she just got a new job. 00:02:09.220 --> 00:02:11.160 And she was supposed to start... 00:02:11.160 --> 00:02:12.600 Actually, she was supposed to start today. 00:02:12.720 --> 00:02:15.020 And they sent her a message, you know what, we're closed. 00:02:15.020 --> 00:02:16.460 We're closed indefinitely. 00:02:16.460 --> 00:02:21.360 And there's no reason for you to come and get trained to work here 00:02:21.360 --> 00:02:24.420 because who knows what it's going to look like in a month or two. 00:02:24.420 --> 00:02:26.060 I mean, that's the reality for a lot of people. 00:02:26.060 --> 00:02:26.560 It's rough. 00:02:26.560 --> 00:02:30.520 One of the reasons why we started talking about this morning is just to say, 00:02:30.520 --> 00:02:34.560 you know, to reach out to everybody and say, yeah, I hope everybody's doing okay. 00:02:34.560 --> 00:02:39.360 And yeah, let us know some stories if you want to share. 00:02:39.480 --> 00:02:41.620 Yeah, maybe some interesting tech angles, right? 00:02:41.620 --> 00:02:46.220 Like problems you run into or things you found that really worked or whatever. 00:02:46.220 --> 00:02:47.900 But yeah, everyone out there, be safe. 00:02:47.900 --> 00:02:54.080 It's not always fun, but just find a place to hole up and just wait this thing out and be safe. 00:02:54.080 --> 00:02:57.500 Yeah, that's a good idea for some extra things like related to that. 00:02:57.500 --> 00:02:58.820 I'll add one of these. 00:02:58.820 --> 00:03:02.340 On our add-ons at the end, I'll add one to that. 00:03:02.340 --> 00:03:03.000 All right. 00:03:03.000 --> 00:03:03.340 Super. 00:03:03.540 --> 00:03:06.100 Well, I want to start out by talking about community. 00:03:06.100 --> 00:03:10.900 I was partly thinking about this because of the coronavirus stuff. 00:03:10.900 --> 00:03:16.400 And a lot of people possibly have maybe two extra hours in the day because they're not commuting. 00:03:16.400 --> 00:03:17.180 Maybe. 00:03:17.180 --> 00:03:21.580 Or, you know, I'm sorry if you have a two-hour commute or an hour commute on each end. 00:03:21.580 --> 00:03:23.720 But you might have some extra time. 00:03:23.720 --> 00:03:29.540 So one of the things you might want to share and spend some time doing is beefing up documentation on open source projects. 00:03:29.920 --> 00:03:37.580 This actually was a great article called Documentation as a Way to Build Community by Melissa Mendoza. 00:03:37.580 --> 00:03:39.080 I think Mendoza. 00:03:39.080 --> 00:03:40.160 Sorry, Melissa. 00:03:40.160 --> 00:03:47.820 But it talks about how educational materials can have a huge impact and effectively bring people into a community. 00:03:47.820 --> 00:03:55.580 And beefing up the documentation story on open source projects can actually help bring more people to use it and help. 00:03:55.580 --> 00:03:58.520 I mean, it seems obvious, but it isn't really. 00:03:58.520 --> 00:03:59.860 And people aren't doing it. 00:04:00.220 --> 00:04:03.840 There's a lot of projects that lack in really good documentation. 00:04:03.840 --> 00:04:05.420 And there's a lot of reasons for that. 00:04:05.420 --> 00:04:08.220 And talking about the reasons I think is interesting. 00:04:08.220 --> 00:04:14.440 Decentralized development and a lot of projects start with just somebody scratching their own itch. 00:04:14.440 --> 00:04:17.080 And they don't need documentation for that. 00:04:17.080 --> 00:04:19.840 But it grows into other people getting involved. 00:04:20.640 --> 00:04:28.100 And a lot of people, it's more glamorous to add new features or fix a nasty bug and adding more documentation. 00:04:28.100 --> 00:04:30.120 Nobody really kind of knows how to do that. 00:04:30.120 --> 00:04:33.340 I think it's important and spending some more focus. 00:04:33.620 --> 00:04:38.200 One of the directions of this article says it was targeting a specific project. 00:04:38.200 --> 00:04:41.660 But I think it really can be really more than just this one. 00:04:41.660 --> 00:04:47.640 is splitting up the documentation into organizing it in four different areas. 00:04:47.640 --> 00:04:51.340 Tutorials, how-tos, reference guides, and explanations. 00:04:52.020 --> 00:05:01.020 These four areas and subsections of those can be targeted towards different people, targeted towards beginners or advanced people or somebody just looking something up. 00:05:01.020 --> 00:05:05.540 One of the great things about that is it makes it easier for somebody to jump in and say, 00:05:05.900 --> 00:05:10.000 Oh, there's like one little piece of things, how to do something. 00:05:10.000 --> 00:05:11.600 I can contribute to that. 00:05:11.600 --> 00:05:16.460 I might not know why it works, but I can contribute to how to and some tutorials. 00:05:16.460 --> 00:05:21.900 Whereas maybe some of the more expert people in the project can do some of the explanations of how things are working. 00:05:21.900 --> 00:05:28.360 And also a lot of teams kind of shift or some projects have the new people come in and say, 00:05:28.360 --> 00:05:29.160 Hey, you want to help out? 00:05:29.160 --> 00:05:30.260 Want to new write documentation? 00:05:30.260 --> 00:05:32.000 And I think that's a great thing. 00:05:32.180 --> 00:05:38.680 But then you've got documentation that's just filled with the beginner people that content from beginners that might not be, 00:05:38.680 --> 00:05:40.960 you know, from some of the experienced people. 00:05:40.960 --> 00:05:43.280 And so I think there's some good information here. 00:05:43.280 --> 00:05:46.520 And I think focusing on documentation might be a good thing. 00:05:46.520 --> 00:05:47.200 I like the article. 00:05:47.200 --> 00:05:49.000 I like the idea of it, right? 00:05:49.000 --> 00:05:51.440 That you can build a community. 00:05:51.440 --> 00:05:55.560 Certainly you can contribute to these projects quite easily in this way. 00:05:55.560 --> 00:06:02.040 Breaking it up into these categories is really clever because then you can definitely just sit down and think, 00:06:02.140 --> 00:06:03.860 wow, I'm going to write some docs for this thing. 00:06:03.860 --> 00:06:05.200 Well, that's pretty wide open, right? 00:06:05.200 --> 00:06:09.620 But I'm going to write a short tutorial, which I had to learn because I had to use this thing. 00:06:09.620 --> 00:06:10.600 And now I know how to do that. 00:06:10.600 --> 00:06:12.420 Why don't I generalize it and make a tutorial? 00:06:12.420 --> 00:06:15.440 That seems like a real easy way to get yourself on the contributor list. 00:06:15.440 --> 00:06:18.000 Beef up your resume. 00:06:18.000 --> 00:06:19.980 Say I contributed to this project, etc. 00:06:19.980 --> 00:06:20.580 I think it's good. 00:06:20.580 --> 00:06:28.580 One of the things I'd like to reach out to people, some of the beginner stuff, a great thing to do is while you're learning a project, isn't writing new content. 00:06:29.100 --> 00:06:38.600 But while you're reading documentation on a project, if there's typos, if there's just grammar errors, it may have been written by somebody that isn't native English. 00:06:38.600 --> 00:06:41.820 So you can help out by just fixing some of those things. 00:06:41.940 --> 00:06:50.900 And then also while you're going through things, if you stumble on something and it's difficult to follow the instructions, it might be that the instructions need to be modified. 00:06:50.900 --> 00:06:56.780 And why not just do like a pull request of modifying those instructions to be the way it really works? 00:06:56.780 --> 00:06:58.580 And I think that'd be cool. 00:06:58.580 --> 00:06:59.260 Yeah, that'd be great. 00:06:59.600 --> 00:07:02.240 Another area that might be interesting is to write tests. 00:07:02.240 --> 00:07:03.080 Yeah, definitely. 00:07:03.080 --> 00:07:07.920 A lot of projects lack tests or they're just marginally tested. 00:07:07.920 --> 00:07:12.960 And you're like, well, okay, I'm going to create this tutorial and I want to make sure the things I'm saying work. 00:07:12.960 --> 00:07:18.580 So let me add some tests to verify what I believe to be true and go ahead and commit that back to the project. 00:07:18.580 --> 00:07:18.980 Yeah. 00:07:18.980 --> 00:07:20.420 And modifying tests. 00:07:20.420 --> 00:07:22.300 If the tests are not readable, they should be. 00:07:22.300 --> 00:07:23.740 And maybe you can make them more readable. 00:07:23.740 --> 00:07:30.640 Yeah, I guess I kind of started thinking about that because documentation and tests feel a little bit like a form of documentation. 00:07:30.640 --> 00:07:31.460 Yeah, definitely. 00:07:31.460 --> 00:07:32.600 Yeah. 00:07:32.600 --> 00:07:33.560 Well, cool. 00:07:33.560 --> 00:07:36.860 Well, I'm pretty passionate about fast websites. 00:07:36.860 --> 00:07:40.520 As you probably know, I talk about trying to make websites fast all the time. 00:07:40.520 --> 00:07:42.240 Our website's pretty fast. 00:07:42.240 --> 00:07:43.360 Speed is important. 00:07:43.360 --> 00:07:46.440 Slow websites, push people away. 00:07:46.440 --> 00:07:46.860 They do. 00:07:46.860 --> 00:07:56.060 I think it was Amazon or somebody did a study saying like every, you know, 100 milliseconds latency of perceived latency to the user. 00:07:56.060 --> 00:08:02.320 And, you know, it has a very tangible, like whole number percentage drop in actual sales. 00:08:02.320 --> 00:08:02.680 Yikes. 00:08:02.680 --> 00:08:03.020 Yeah. 00:08:03.020 --> 00:08:04.860 Sales are not the most important thing necessarily. 00:08:04.860 --> 00:08:06.360 Maybe if you're Amazon, they are. 00:08:06.360 --> 00:08:09.680 But it just gives you a sense of like, well, 100 milliseconds. 00:08:09.680 --> 00:08:11.520 You can barely perceive that as a person. 00:08:11.520 --> 00:08:15.740 And yet as those things add up, right, it starts to really make a difference in behavior. 00:08:15.740 --> 00:08:16.100 Yeah. 00:08:16.100 --> 00:08:27.640 So I want to talk about this article, sort of riff on some topics covered in the article, more or less, called the Django Speed Handbook, Making a Django App Faster by Chabelle Mansour. 00:08:27.640 --> 00:08:37.240 Now, the title has Django and some of the examples are really about Django, but this actually applies to most websites and Python websites and whatnot. 00:08:37.240 --> 00:08:41.480 So if you do flask, I think this will still be super or super relevant. 00:08:42.200 --> 00:08:46.320 The first thing, though, that I want to point out is actually a Django thing. 00:08:46.320 --> 00:08:48.800 And it does appear at least in Pyramid as well. 00:08:48.800 --> 00:08:53.200 So there's this, in Django, there's a thing called the Django Debug Toolbar. 00:08:53.200 --> 00:08:57.460 And it lets you explore the different requests, see how long they're taking. 00:08:57.460 --> 00:09:02.540 You can even get in there and look at the ORM calls and what's happening. 00:09:02.920 --> 00:09:03.980 So that's pretty awesome. 00:09:03.980 --> 00:09:05.740 Like Pyramid has this as well. 00:09:05.740 --> 00:09:13.200 You can actually see the SQLAlchemy calls going to the database and the timing and how many database queries there even are on a given page. 00:09:13.200 --> 00:09:17.060 It's pretty ridiculous to be able to use that to analyze what yours. 00:09:17.060 --> 00:09:22.420 It's almost like you've attached a little debugger profiler all the time and it's just right there. 00:09:22.420 --> 00:09:22.980 That's cool. 00:09:22.980 --> 00:09:24.500 Do you have to turn it off then? 00:09:24.700 --> 00:09:30.360 Well, when you go into production, you don't include it in the setting, like the run settings for production, obviously, right? 00:09:30.360 --> 00:09:30.840 Got it. 00:09:30.840 --> 00:09:31.520 That would be bad. 00:09:31.520 --> 00:09:35.200 But some of those settings, even in the debug mode, you have to turn them on. 00:09:35.200 --> 00:09:42.420 I'm not sure about the Django one, but the Pyramid one, you definitely, like the profiler is not on by default because that'll slow it down a little bit. 00:09:42.420 --> 00:09:44.520 But you can click a box and then go do the request again. 00:09:44.520 --> 00:09:44.960 Okay. 00:09:44.960 --> 00:09:45.280 All right. 00:09:45.280 --> 00:09:48.460 So that's a real quick and easy way just to see what your app is up to. 00:09:48.460 --> 00:09:55.900 Then one of the things you really want to pay attention to, and this is going to be a bit of a theme on today's show, is talking to databases. 00:09:55.900 --> 00:10:13.960 So when you're working with an ORM or just talking to the database, specifically here, the Django ORM, but this is super relevant for like SQLAlchemy as well, is you want to be really careful of the so-called N plus one problem, which happens when you navigate relationships. 00:10:14.380 --> 00:10:27.760 So for example, if I have, let's say a category, I'm going to show a category of books and the category has a books relationship, or maybe there's some other thing like that. 00:10:27.760 --> 00:10:30.700 I get all the categories back and I want to tell you how many books are in each one or something. 00:10:30.700 --> 00:10:39.660 Like as you go through the things that come back, you end up doing one query for each property that you access on each instance of that object. 00:10:39.660 --> 00:10:44.400 So if you do a query that returns 20 things, you might end up talking to the database 21 times. 00:10:44.400 --> 00:10:55.380 It's a common problem in ORMs, but it also has an easy fix, which is why that debug toolbar is cool, because you could turn it up and say, well, turn it on and say, oh, look, why are there 24 queries on this page? 00:10:55.380 --> 00:10:55.600 Right? 00:10:55.600 --> 00:10:57.620 I feel like I did one, like, well, sort of. 00:10:57.620 --> 00:11:10.160 So you can use select rated related and prefetch related, and it'll basically join or pre-query those related objects together in one massive query. 00:11:10.160 --> 00:11:12.980 So you don't actually go back to the database N plus one times. 00:11:12.980 --> 00:11:13.720 Okay, nice. 00:11:13.860 --> 00:11:14.800 Yeah, and that's a big deal. 00:11:14.800 --> 00:11:19.960 And, you know, SQLAlchemy has a joined load and subquery that you can basically accomplish the same thing. 00:11:19.960 --> 00:11:28.640 So he's got a cool example of not a huge database, but doing, making, using these two properties in the Django ORM going 24 times faster. 00:11:28.640 --> 00:11:29.260 Oh, wow. 00:11:29.260 --> 00:11:29.760 Yeah. 00:11:29.760 --> 00:11:29.920 Right? 00:11:29.920 --> 00:11:35.160 I mean, it's basically not changing the code at all, except saying, you know, I'm going to use this related property. 00:11:35.160 --> 00:11:40.500 So just query that as part of the query instead of, like, doing, you know, however many queries you're going back for. 00:11:40.500 --> 00:11:41.700 Really, really nice. 00:11:41.700 --> 00:11:43.740 Related to that is indexes. 00:11:43.740 --> 00:11:48.400 So if you're not thinking about and using indexes, you should be. 00:11:48.400 --> 00:11:54.600 I mean, that's, like, easily a thousand times faster to do a query against a lot of data with an index versus without. 00:11:54.600 --> 00:11:57.040 And then if you've got these joins, it's even better, you know? 00:11:57.040 --> 00:12:02.280 So super important, but do be aware that indexes make writes slower. 00:12:03.540 --> 00:12:10.340 So if you have not, most websites don't write data like crazy, although some APIs do. 00:12:10.340 --> 00:12:17.820 So it's usually not as big of a problem, but just be aware that writes are slow or with indexes, but queries are much, much faster. 00:12:17.820 --> 00:12:28.960 Another thing they talk about, which is really helpful, is using pagination, pagination, where instead of saying, here's a thousand items, here's 50, and you can ask for the next 50 and the next 50 and so on. 00:12:28.960 --> 00:12:32.920 That's super easy to do with Django ORM or SQLAlchemy or anything like that. 00:12:32.920 --> 00:12:34.120 So that's a really good one. 00:12:34.120 --> 00:12:40.780 So does that often line up with, like, if you're showing, like, if your page only shows 50 things, only fetch 50 things then? 00:12:40.780 --> 00:12:41.720 Yeah, yeah, exactly. 00:12:41.720 --> 00:12:46.060 And it's super easy to put in the query string, like, page equals five, right? 00:12:46.060 --> 00:12:46.280 Okay. 00:12:46.280 --> 00:12:55.320 And then you just do a skip and a limit or whatever the ORM using has, like, for the skip and take type of thing, right? 00:12:55.380 --> 00:12:56.080 So it's super easy. 00:12:56.080 --> 00:12:59.260 You can compute it yourself, but it makes a big difference, right? 00:12:59.260 --> 00:13:11.580 Also, if you have long-running tasks, long-running things to do, make them either background tasks and, like, extra other processes or celery or something, or just use... 00:13:11.580 --> 00:13:16.240 If the person making the call has to wait on it, be sure to use async, right? 00:13:16.240 --> 00:13:18.020 So you're not blocking up everything. 00:13:18.020 --> 00:13:18.700 Yeah. 00:13:18.900 --> 00:13:27.660 Another super easy way to make things fast, and many of these things we're doing at pythonbytes.fm and the other websites, is to turn on gzip. 00:13:27.660 --> 00:13:34.720 So you can just go to, like, Nginx or whatever your web server is and say gzip the response. 00:13:34.720 --> 00:13:44.960 He's got a really simple example here where the response size of the page and the CSS and whatnot is nine times smaller by just adding the gzip middleware to Django. 00:13:44.960 --> 00:13:45.360 Oh, wow. 00:13:45.680 --> 00:13:48.340 I wouldn't actually add it to Django if this was me. 00:13:48.340 --> 00:13:52.760 I would add it to Nginx because that's the outer shell web server. 00:13:52.760 --> 00:13:55.760 Just let it do it, and you don't have to... 00:13:55.760 --> 00:13:58.560 You're probably not talking directly to the server-running Django. 00:13:58.560 --> 00:14:02.860 But anyway, somewhere along the way, gzip your content because that'll be big. 00:14:02.860 --> 00:14:10.600 Similarly, minify your static files and bundle them and cache them and all of those good things, right? 00:14:10.600 --> 00:14:10.960 Okay. 00:14:10.960 --> 00:14:13.860 There's some cool libraries that he talked about in there. 00:14:14.040 --> 00:14:15.660 I think it was called Whitespace. 00:14:15.660 --> 00:14:22.000 I'm pretty sure it's called Whitespace that they're using in Django to minify and bundle the files. 00:14:22.000 --> 00:14:23.980 So we don't use Whitespace. 00:14:23.980 --> 00:14:24.920 We don't use Django. 00:14:24.920 --> 00:14:31.200 We use WebAssets and CSSmin and JSmin, which are three awesome Python libraries to bundle that. 00:14:31.200 --> 00:14:45.860 So if you go and look at Python Bytes or Talk Python or any of those sites, you can see that there's a packed CSS and a packed JavaScript that has probably 20 CSS files that's smushed into one with those things and minified and whatnot. 00:14:45.860 --> 00:14:46.200 Okay. 00:14:46.200 --> 00:14:47.100 So that's pretty cool. 00:14:47.100 --> 00:14:49.600 There's two ways to measure page performance. 00:14:49.820 --> 00:14:52.660 One is like how fast is the server responding, right? 00:14:52.660 --> 00:14:56.220 But that's not the most important thing to the user. 00:14:56.220 --> 00:14:57.940 The most important thing is how does it feel to them. 00:14:57.940 --> 00:15:03.620 So Google has this thing called PageSpeed, which they're even using for measuring like your SEO ranking. 00:15:04.080 --> 00:15:05.760 So put your website into there. 00:15:05.760 --> 00:15:09.380 I have a link for Talk Python trainings ranking. 00:15:09.380 --> 00:15:16.880 I spent three days straight getting it from like 40 out of 100 to 99 or 100 out of 100. 00:15:16.880 --> 00:15:19.180 But it was quite the journey. 00:15:19.180 --> 00:15:20.660 So that took a while. 00:15:20.660 --> 00:15:23.820 You can both measure it for mobile and desktop. 00:15:23.820 --> 00:15:25.320 And it has slightly different rankings. 00:15:25.320 --> 00:15:32.520 Also, shrink your images with ImageOptim, which works for macOS and Linux. 00:15:32.620 --> 00:15:35.180 It doesn't work on Windows, but there's some really great options there. 00:15:35.180 --> 00:15:39.160 And it'll basically do completely lossless compression of your images. 00:15:39.160 --> 00:15:41.900 So they might be like 40 or 50% smaller. 00:15:41.900 --> 00:15:44.980 And visually, you could not, you literally couldn't distinguish them. 00:15:44.980 --> 00:15:45.460 Interesting. 00:15:45.460 --> 00:15:45.900 Yeah. 00:15:45.900 --> 00:15:46.260 Yeah. 00:15:46.260 --> 00:15:50.480 And then last recommendation is lazy load your images. 00:15:50.480 --> 00:15:58.300 This is not something I've really explored, but apparently Google Chrome images now support a lazy attribute. 00:15:58.300 --> 00:15:58.880 Oh, nice. 00:15:58.880 --> 00:15:59.160 Yeah. 00:15:59.160 --> 00:16:02.440 And then for things that don't support it, there's a lazy load JavaScript library. 00:16:02.960 --> 00:16:03.780 Basically, your images. 00:16:03.780 --> 00:16:07.780 You say, here's, as it scrolls into view, it'll download them. 00:16:07.780 --> 00:16:10.560 But if it's off the page and you never scroll, then it'll never load it. 00:16:10.560 --> 00:16:11.040 That's great. 00:16:11.040 --> 00:16:11.300 Yeah. 00:16:11.300 --> 00:16:11.780 Pretty clever. 00:16:11.780 --> 00:16:14.860 So this is just some of the things covered in that article. 00:16:14.860 --> 00:16:20.980 So if you're out there and you're like, I need to get my site to go faster, it cannot be three seconds per page load. 00:16:20.980 --> 00:16:21.720 That's ridiculous. 00:16:21.720 --> 00:16:23.580 Like, start looking through some of these things. 00:16:23.580 --> 00:16:25.540 It'll really help, especially if you're using Django. 00:16:25.540 --> 00:16:29.760 But even if you're using some other Python framework, I think it'll still be quite relevant. 00:16:29.760 --> 00:16:30.040 Yeah. 00:16:30.040 --> 00:16:32.180 Most of these are relevant to any web stuff. 00:16:32.180 --> 00:16:32.400 Yeah. 00:16:32.400 --> 00:16:32.680 Yeah. 00:16:32.680 --> 00:16:34.080 They're super, super general. 00:16:34.080 --> 00:16:36.380 Like some of the libraries, they talk about plug into Django. 00:16:36.380 --> 00:16:38.520 So it's kind of a little extra boost if you're doing Django. 00:16:38.520 --> 00:16:39.880 But yeah, this is relevant to everyone. 00:16:39.880 --> 00:16:40.260 Yeah. 00:16:40.400 --> 00:16:40.560 All right. 00:16:40.560 --> 00:16:41.060 What do you got next? 00:16:41.060 --> 00:16:45.780 Well, this actually came into as a listener suggestion from the author of the library. 00:16:45.780 --> 00:16:47.620 So this is like JIT podcasting, right? 00:16:47.620 --> 00:16:48.180 Yeah. 00:16:48.180 --> 00:16:50.200 It just came in this morning and I love it. 00:16:50.200 --> 00:16:52.760 It's from Conrad Hollis, I think. 00:16:52.760 --> 00:16:56.000 It's called D-A-C-I-T-E. 00:16:56.000 --> 00:16:56.940 Maybe DeCite? 00:16:56.940 --> 00:16:57.880 DeCite? 00:16:57.880 --> 00:16:58.480 DeCite? 00:16:58.480 --> 00:16:59.020 I don't know. 00:16:59.020 --> 00:17:00.140 But it's cool. 00:17:00.140 --> 00:17:03.700 It simplifies the creation of data classes from dictionaries. 00:17:03.700 --> 00:17:07.080 So when I first heard it, I'm thinking, okay, well, I love DIC. 00:17:07.160 --> 00:17:10.400 I'm using data classes like all the time now because I really like them. 00:17:10.400 --> 00:17:12.340 There's a lot of cool aspects of them. 00:17:12.340 --> 00:17:13.620 You can have default values. 00:17:13.620 --> 00:17:18.320 I really like that I can easily have exclude some of the fields. 00:17:18.320 --> 00:17:20.940 You can take them out of the comparison. 00:17:20.940 --> 00:17:25.960 So some objects can be equal even if they're not completely equal sort of thing. 00:17:25.960 --> 00:17:28.020 And I love that aspect. 00:17:28.020 --> 00:17:30.100 And there's a whole bunch of other cool stuff about them. 00:17:30.100 --> 00:17:31.800 So I'm using them more and more. 00:17:31.800 --> 00:17:36.520 But our data all over us that we get from databases and whatever, 00:17:36.860 --> 00:17:40.160 it often gets converted to dictionaries and not to data classes. 00:17:40.160 --> 00:17:45.340 So this is a little library that has basically it's one function called from dict 00:17:45.340 --> 00:17:48.600 that converts dictionaries to data classes. 00:17:48.600 --> 00:17:51.660 And my first reaction was I can already do that. 00:17:51.660 --> 00:17:55.560 If you do the star star or the double splat. 00:17:55.560 --> 00:17:58.580 Dictionary to keyword argument type of thing. 00:17:58.580 --> 00:17:59.020 Yeah. 00:17:59.020 --> 00:18:03.440 I mean, you can do that for simple data classes and simple dictionaries. 00:18:03.440 --> 00:18:04.560 That works just fine. 00:18:05.000 --> 00:18:06.700 But I looked into this more. 00:18:06.700 --> 00:18:11.400 And this from dict from to site, it allows you to do nested structures. 00:18:11.400 --> 00:18:18.660 So you can have a data class with another data class field and arrays of lists or tuples of data classes. 00:18:18.660 --> 00:18:24.180 And as some of the types, you can do unions in their collections, nested structures. 00:18:24.380 --> 00:18:32.680 It even has this thing called type hooks, which allows you to have a custom converter for certain types of data that come in. 00:18:32.680 --> 00:18:38.200 So his example is like if for all the strings, lowercase them or something like that. 00:18:38.200 --> 00:18:41.200 But you can definitely have that for certain types. 00:18:41.200 --> 00:18:41.980 It's pretty neat. 00:18:41.980 --> 00:18:42.700 Oh, that's cool. 00:18:42.840 --> 00:18:48.140 Or if you got like some kind of string that's a date time, you know, parse it out of an iso string or whatever. 00:18:48.140 --> 00:18:49.960 Yeah, that's a good example, actually. 00:18:49.960 --> 00:18:50.500 That's cool. 00:18:50.500 --> 00:19:04.400 So one of the things that messes you up on my example of just taking a dictionary and expanding it as arguments to a data class constructor is that it doesn't really work if all the names don't match up. 00:19:04.500 --> 00:19:12.260 But this one allows you to have if your data class only has a few fields, but your dictionary has like tons of stuff in there. 00:19:12.260 --> 00:19:15.340 By default, it just ignores the stuff that doesn't match up. 00:19:15.340 --> 00:19:25.620 And so if you've got like a name and an ID and there's names and IDs coming from the dictionary, but there's also like a whole bunch of other things like URL and stuff like that. 00:19:25.620 --> 00:19:26.680 It just ignores that. 00:19:26.680 --> 00:19:27.800 That's the default. 00:19:27.800 --> 00:19:32.780 But you can also turn on strict mode that says, no, I expect it to match up directly and I want a warning. 00:19:33.280 --> 00:19:37.280 And then there's a whole bunch of exceptions that get raised if something goes wrong in the conversion. 00:19:37.280 --> 00:19:44.080 And I'm just excited to use this because it's a really cool tool to convert data to data classes. 00:19:44.080 --> 00:19:44.680 It's nice. 00:19:44.680 --> 00:19:46.040 Yeah, this looks super nice. 00:19:46.040 --> 00:19:51.920 It's one of those things that seems to automate like the crummy part of programming, right? 00:19:51.920 --> 00:19:59.140 Like I'm getting this data submitted to me from an API or from somebody calling my API and who knows what they're sending me. 00:19:59.240 --> 00:20:02.280 But here's how like long as this thing lines up, right? 00:20:02.280 --> 00:20:02.460 Right. 00:20:02.460 --> 00:20:05.960 I tell it these fields are not optional or this type has to be such and such. 00:20:05.960 --> 00:20:07.140 If that works, then we're good. 00:20:07.140 --> 00:20:11.560 Otherwise, you know, tell them 400 that didn't work or the file couldn't be loaded or whatever it is. 00:20:11.560 --> 00:20:18.920 And there's definitely so Conrad made a point in the documentation to say that it is not a schema validation library. 00:20:18.920 --> 00:20:20.200 That's not the intent of it. 00:20:20.200 --> 00:20:23.420 It is really just intended for the conversion. 00:20:23.420 --> 00:20:30.900 So especially with external APIs, I think combining this with a schema validation is a good idea. 00:20:31.260 --> 00:20:36.260 But you could definitely go from schema validation to this and have data classes in the end. 00:20:36.260 --> 00:20:36.700 It'd be great. 00:20:36.700 --> 00:20:38.020 Yeah, it's a cool project. 00:20:38.020 --> 00:20:41.560 And I love how it leverages the brand new Python stuff, the data classes. 00:20:41.560 --> 00:20:44.000 Anyway, we should plug ourselves. 00:20:44.000 --> 00:20:44.340 Yeah. 00:20:44.340 --> 00:20:45.060 As sponsors. 00:20:45.060 --> 00:20:46.360 So. 00:20:46.360 --> 00:20:49.800 Well, we should definitely let people know about what we're doing, right? 00:20:49.800 --> 00:20:52.900 So you've got this book on testing or something? 00:20:53.080 --> 00:20:57.780 I actually kind of love that I had some feedback early on when the book came out. 00:20:57.780 --> 00:21:01.460 Python Testing with pytest is the book that I'm talking about. 00:21:01.460 --> 00:21:04.700 And it did come out in 2017, the end of 2017. 00:21:04.700 --> 00:21:09.440 And I got some really great feedback from people saying they really loved following the book on this podcast. 00:21:09.440 --> 00:21:12.740 And I apologize for the lawnmower in the background. 00:21:12.740 --> 00:21:17.400 If it goes through, I wanted to point out that I had a couple of people ask me, 00:21:17.400 --> 00:21:19.080 it came out in 2017. 00:21:19.080 --> 00:21:20.660 Is it still valid? 00:21:20.820 --> 00:21:23.860 And I want to take the time to say, yes, it is. 00:21:23.860 --> 00:21:29.720 The intent of the book was never to be a thorough, complete inventory of everything you can do with pytest. 00:21:29.720 --> 00:21:35.360 It was a quick, what are the like 80% of pytest that you're going to use all the time? 00:21:35.360 --> 00:21:38.640 And that will is the core of pytest and how to think about it. 00:21:38.640 --> 00:21:42.020 There is new goodies that have been added since 2017. 00:21:42.020 --> 00:21:44.320 And it's good to check those out. 00:21:44.320 --> 00:21:47.680 But you could run with what's in this book and still be very productive. 00:21:47.680 --> 00:21:48.640 Nice. 00:21:48.760 --> 00:21:51.360 It's definitely made me more productive and better with pytest. 00:21:51.360 --> 00:21:52.420 So it's great. 00:21:52.420 --> 00:21:52.720 Thank you. 00:21:52.720 --> 00:21:53.300 Yeah, you bet. 00:21:53.300 --> 00:21:57.100 And I also want to tell people about the courses that we have over at Talk Python Training. 00:21:57.100 --> 00:21:59.460 We've got a bunch of new ones we've been releasing. 00:21:59.460 --> 00:22:01.280 I do try to let you know when the new ones are out. 00:22:01.320 --> 00:22:07.980 But we've got like 120 hours of Python content over there on a bunch of projects that you can do. 00:22:07.980 --> 00:22:12.720 The 100 days of code courses all have like projects for every single day for 100 days. 00:22:12.720 --> 00:22:15.240 And yeah, so just check them out. 00:22:15.240 --> 00:22:17.820 We're going to release a couple new courses coming soon. 00:22:17.820 --> 00:22:19.800 And I'll be sure to let you know. 00:22:19.920 --> 00:22:22.700 But yeah, support us by checking out our work, right? 00:22:22.700 --> 00:22:22.980 Yeah. 00:22:22.980 --> 00:22:28.420 I want to tell people one of the things I love about the Talk Python courses is there's a lot of content there. 00:22:28.420 --> 00:22:29.900 And I'm a busy person. 00:22:29.900 --> 00:22:36.760 And sometimes it's overwhelming to me to look at a course to say it's like 12 hours of content on a course or something like that. 00:22:36.760 --> 00:22:38.060 Six hours or something even. 00:22:38.660 --> 00:22:53.620 And however, the way that you've got it set up with a whole like bookmarks into separate videos and different topics, it's the outline of the courses are so incredible that if you really need to just jump to the right place to learn something, you can do that. 00:22:53.620 --> 00:22:59.600 And even though you can just watch them in series and just watch the whole thing, you can do that, of course. 00:22:59.600 --> 00:23:04.880 But being able to jump around and go back and use it as a reference is a great thing. 00:23:04.880 --> 00:23:05.820 So thanks. 00:23:05.820 --> 00:23:06.440 Yeah, thanks. 00:23:06.560 --> 00:23:08.800 Yeah, we definitely work hard on making that a possibility. 00:23:08.800 --> 00:23:10.220 So I appreciate that. 00:23:10.220 --> 00:23:13.860 Now, do you know what the Python clock reads right now? 00:23:13.860 --> 00:23:14.860 Oh, I haven't checked. 00:23:14.860 --> 00:23:15.680 What does it read? 00:23:15.680 --> 00:23:20.180 It reads zero, zero, zero, zero, zero, zero. 00:23:20.180 --> 00:23:27.820 It's the Python clock has clock bell has told for the folks who have to convert. 00:23:27.820 --> 00:23:32.200 This next thing I want to share with everyone comes from LinkedIn and Barry Warsaw. 00:23:32.800 --> 00:23:36.880 Barry's been part of Python for a very long time doing a lot of cool stuff there. 00:23:36.880 --> 00:23:43.240 And he was on the team that helped LinkedIn move from legacy Python to modern Python. 00:23:43.240 --> 00:23:43.640 Okay. 00:23:43.640 --> 00:23:44.000 Yeah. 00:23:44.060 --> 00:23:48.320 So it's called how we retired Python to an improved developer happiness. 00:23:48.840 --> 00:23:57.960 So a couple years ago, 2018, LinkedIn started working on this multi-quarter effort to transition to Python 3. 00:23:58.360 --> 00:24:06.180 So maybe some of the lessons from here will help people out there for whom they haven't actually migrated all the way to Python 3. 00:24:06.180 --> 00:24:06.940 That'd be good, right? 00:24:06.940 --> 00:24:07.240 Yeah. 00:24:07.320 --> 00:24:16.000 So basically, they said they did an inventory and they found they have 550 code repositories they had to migrate. 00:24:16.000 --> 00:24:18.200 That's a lot of different projects. 00:24:18.200 --> 00:24:19.020 Yeah. 00:24:19.020 --> 00:24:21.020 And some of them depend on the others. 00:24:21.880 --> 00:24:26.900 So they said, look, Python is not the thing powering our main web app. 00:24:26.900 --> 00:24:28.060 I think it's Java. 00:24:28.060 --> 00:24:29.060 I'm not 100% sure. 00:24:29.060 --> 00:24:31.680 But anyway, it's not their main thing. 00:24:31.680 --> 00:24:38.340 Instead, there's a bunch of like independent microservices and tools and data science projects that are all using this. 00:24:38.380 --> 00:24:52.920 So their first pass at getting all those different things migrated was to say, we're going to have a bilingual philosophy for Python, meaning it'll run on 2 and 3 at the same time. 00:24:52.920 --> 00:24:53.240 Okay. 00:24:53.240 --> 00:24:58.900 And then once you get it there, the main problem that you could run into is I depend on a library. 00:24:58.900 --> 00:25:01.280 Like this is standard legacy Python. 00:25:01.280 --> 00:25:05.160 So I depend on a library that requires Python 2. 00:25:05.620 --> 00:25:11.100 Therefore, everything that I use that I build that depends on that library must also be Python 2, right? 00:25:11.100 --> 00:25:11.420 Yeah. 00:25:11.420 --> 00:25:17.260 So this bilingual thing that they did, this was to prevent that blockade, right? 00:25:17.260 --> 00:25:22.400 So anyone who wants to build new stuff on Python 3 could still use the libraries and do so. 00:25:22.400 --> 00:25:23.320 That was the plan. 00:25:23.320 --> 00:25:32.920 They actually had a whole team that oversaw this effort like across projects or across like thousands of engineers called the Horizontal Initiatives Program. 00:25:34.220 --> 00:25:38.040 So that was to like kind of across all these different projects address that. 00:25:38.040 --> 00:25:54.880 And then in phase one, first quarter of 2019, they went and they found the most important repositories, the ones that were, if you put them into a dependency graph at the bottom, and they said, we're going to port those to Python 3 first because they're blocking everything else. 00:25:55.120 --> 00:25:58.820 And then they kind of finished it off in the second half of 2019. 00:25:58.820 --> 00:26:01.940 So they basically said, all right, now we got the foundation done. 00:26:01.940 --> 00:26:05.440 We can start upgrading the libraries that depend on all these lower level bits. 00:26:05.840 --> 00:26:09.120 And then, you know, they said, looking back, you'll like this part, Brian. 00:26:09.120 --> 00:26:18.940 They said, our primary indicator for knowing that the migration was done, that we were all right, was that our builds passed and our tests ran and everything was okay. 00:26:19.180 --> 00:26:26.400 And then eventually they went through and said, all right, we're going to turn off the ability to run Python 2 type of tests in continuous integration. 00:26:26.400 --> 00:26:28.380 Now let's see what keeps working. 00:26:28.380 --> 00:26:29.000 Oh, yeah. 00:26:29.000 --> 00:26:29.280 Okay. 00:26:29.280 --> 00:26:29.620 Yeah. 00:26:29.660 --> 00:26:32.780 So one of the things you can imagine is important is having tests, right? 00:26:32.780 --> 00:26:36.160 Because if you don't have tests, CI, CD doesn't tell you a lot. 00:26:36.160 --> 00:26:38.140 It just does the CD part. 00:26:38.140 --> 00:26:40.360 Better for better or worse. 00:26:40.360 --> 00:26:42.100 Yeah. 00:26:42.100 --> 00:26:52.420 So they said, look, here's some guidelines for people, other organizations who are on similar paths, but earlier, they said, plan early and engage your organization's Python experts. 00:26:52.420 --> 00:26:59.420 Find and leverage champions in the affected teams and help them promote the benefits of Python 3 to everyone. 00:26:59.420 --> 00:27:06.660 Adopt this bilingual approach so people can at least begin, if they want to, go to Python 3. 00:27:06.660 --> 00:27:13.920 Invest in tests and test coverage, code coverage, because these will be your best metrics of success. 00:27:13.920 --> 00:27:23.040 And then finally, ensure your data models explicitly deal with this, what used to be one thing, bytes and strings in Python 2. 00:27:23.040 --> 00:27:25.400 And now is, of course, two totally separate things. 00:27:25.560 --> 00:27:30.920 Like that's, they said that was the really the biggest challenge that they ran into is that making that distinction correctly. 00:27:30.920 --> 00:27:31.320 Yeah. 00:27:31.320 --> 00:27:32.240 Those are hurdles. 00:27:32.240 --> 00:27:33.000 Are you guys all upgraded? 00:27:33.000 --> 00:27:33.420 Yeah. 00:27:33.420 --> 00:27:37.880 It was a library that we were using that didn't support Python 3 yet. 00:27:37.880 --> 00:27:45.980 The reasoning was the library talks to a DLL that has, you know, C++ strings or C strings. 00:27:46.520 --> 00:27:50.960 And old Python strings converted just fine, but they don't now. 00:27:50.960 --> 00:27:52.480 Unicode fancy ones. 00:27:52.480 --> 00:27:52.720 Yeah. 00:27:52.720 --> 00:27:53.540 Not so easy. 00:27:53.540 --> 00:27:53.820 Yeah. 00:27:53.820 --> 00:27:55.000 Yeah. 00:27:55.000 --> 00:27:56.180 Cool. 00:27:56.180 --> 00:28:02.180 So to wrap this up, they said the benefits they have from this whole process is they no longer have to worry about supporting Python 2. 00:28:02.180 --> 00:28:06.400 And they've seen their support loads decrease and decrease in a good way. 00:28:06.400 --> 00:28:08.100 Not, you don't have to support the old crummy stuff. 00:28:08.580 --> 00:28:12.020 You can depend on the latest open source libraries, right? 00:28:12.020 --> 00:28:14.240 A lot of libraries these days only work with Python 3. 00:28:14.240 --> 00:28:22.960 And they opportunistically and enthusiastically adopted type hinting in mypy to improve overall quality, which is pretty cool. 00:28:22.960 --> 00:28:23.360 Yeah. 00:28:23.360 --> 00:28:24.480 That is good. 00:28:24.480 --> 00:28:25.160 Yeah. 00:28:25.160 --> 00:28:26.800 I'm looking forward to this next one you got. 00:28:26.800 --> 00:28:30.340 This actually ties nicely because you brought up the Django speedups. 00:28:31.120 --> 00:28:33.740 And I probably should have talked about this right afterwards. 00:28:33.740 --> 00:28:34.940 But anyway, here we go. 00:28:34.940 --> 00:28:40.720 There was an article that I'm not saying I agree or disagree because I don't know enough about it. 00:28:40.720 --> 00:28:44.960 But the article was called the troublesome active record pattern. 00:28:44.960 --> 00:28:49.420 And I guess in, you know, like Ruby and stuff, we talk about that. 00:28:49.420 --> 00:28:51.960 They talk about active record more, I think. 00:28:52.260 --> 00:29:01.580 But in Python world, it's the object relational mappers, ORMs, like the Django ORM or SQLAlchemy is also an ORM. 00:29:01.580 --> 00:29:04.580 And those are essentially the same as active record. 00:29:04.580 --> 00:29:06.760 That's, I think, that's the same pattern, right? 00:29:06.760 --> 00:29:09.500 Well, certainly the Django ORM follows that pattern. 00:29:09.500 --> 00:29:15.800 SQLAlchemy, it has a lot of similarities, but its design pattern is technically called a unit of work. 00:29:15.800 --> 00:29:16.280 Okay. 00:29:16.280 --> 00:29:22.300 The main variation is like on Django or things like that is you go to the object and you call save. 00:29:22.300 --> 00:29:26.000 Whereas, so that happens on the individual objects. 00:29:26.000 --> 00:29:34.580 Whereas in SQLAlchemy, you make a bunch of changes and then there's this unit of work thing and you call save and it submits all the changes in one giant batch. 00:29:34.580 --> 00:29:41.840 But basically, but here's the interesting thing is like this whole article is like the troublesome active record pattern. 00:29:41.840 --> 00:29:46.920 But my reading of it really was the troublesome ORM pattern. 00:29:46.920 --> 00:29:51.800 And so, for the most part, it's kind of an immaterial distinction. 00:29:51.800 --> 00:29:55.040 Although, technically, design pattern-wise, they're not exactly the same. 00:29:55.040 --> 00:29:55.440 Okay. 00:29:55.440 --> 00:29:56.020 Okay. 00:29:56.020 --> 00:29:57.060 Well, yeah. 00:29:57.060 --> 00:30:08.400 So, the idea being like you just brought it up that the object, when you're referencing a bunch of objects and you have object save and things like that, there's a whole bunch of issues with that. 00:30:08.500 --> 00:30:23.160 One of the issues is if you want to query things about the data, not necessarily all the data, but things like if you've got a bunch of books, for example, and you just want to count the number of books, well, you might have to just retrieve them all. 00:30:23.160 --> 00:30:29.560 Or if you want to count all of the software testing books written by Oregon authors, you'd have to just ask me. 00:30:29.560 --> 00:30:39.880 Or you'd have to grab all of them and grab all the data and then search on, in Python, look for stuff in a for loop or something. 00:30:40.440 --> 00:30:52.480 The other problem was around transactions, because if I have a book item and then change something about it and then save it back in, there's nothing stopping some other process. 00:30:52.480 --> 00:30:58.520 You know, the read, modify, write doesn't work that well if you've got multiple readers and writers. 00:30:58.520 --> 00:30:59.780 And I was looking this up. 00:30:59.780 --> 00:31:03.740 SQLAlchemy has sessions, or you said there's a unit of work thing. 00:31:03.740 --> 00:31:05.160 Don't know if those are atomic. 00:31:05.340 --> 00:31:06.260 Yeah, yeah, they're the same, yeah. 00:31:06.260 --> 00:31:16.280 Okay, Django has an atomic setting, but I don't know if that's by default or if it always, or if you have to specifically, say, work with transactions. 00:31:16.280 --> 00:31:21.480 I did notice in some of the Django documentation that it does say that transactions slow things down. 00:31:21.480 --> 00:31:25.280 So you don't want to do transactions if you're just reading, for instance. 00:31:25.280 --> 00:31:32.580 And then the author of the article, Cal Peterson, mentions that REST APIs often have the same problems, 00:31:32.700 --> 00:31:37.120 and some microservice architectures have a similar sort of issue. 00:31:37.120 --> 00:31:40.840 It's just around REST APIs instead of the object model. 00:31:40.840 --> 00:31:43.880 You're reading tons of data when you don't need to. 00:31:43.880 --> 00:31:54.900 He brought up some solutions, at least for you can just directly use SQL or use some properties that do queries that are more like SQL. 00:31:55.460 --> 00:31:56.920 Doing transactions helps too. 00:31:56.920 --> 00:32:03.440 But basically, he was recommending avoiding the active record style access patterns around the REST APIs. 00:32:03.440 --> 00:32:11.460 He brought up that GraphQL and RPC style APIs are some solutions to the same problem in REST APIs. 00:32:11.940 --> 00:32:20.540 As somebody that's moving towards learning more about web development and working with ORMs, I really did want to bring this up and find out what you thought of all of this. 00:32:20.540 --> 00:32:20.840 Sure. 00:32:20.840 --> 00:32:21.680 It's interesting. 00:32:21.680 --> 00:32:26.260 There are a lot of good, valid points that Cal's making here. 00:32:26.720 --> 00:32:34.960 I feel like the focus should almost be, instead of the troublesome active record pattern is, you're using your ORM wrong, learn how to use it right. 00:32:35.720 --> 00:32:37.300 So let me give you some examples. 00:32:37.300 --> 00:32:45.840 So one of the challenges here that we see is, if you're going to create a record and you want to get it back, you have to get it back by the primary key. 00:32:45.840 --> 00:32:54.760 Maybe if you're doing exactly on just the straight ORM record pattern, but you can just do a query and do it like a give me the first or one item or something like that. 00:32:54.760 --> 00:33:00.720 There's a part where he's looping over stuff saying, here we're looping back to just get the ISBN off these things, right? 00:33:00.720 --> 00:33:10.360 You're pulling all the properties, like you're doing basically a select star from table just to ultimately, and a serialization of that result just to get like the ISBN. 00:33:10.360 --> 00:33:18.100 Well, in SQLAlchemy, I don't know Django ORM well enough, but SQLAlchemy, you can say only return these columns. 00:33:18.100 --> 00:33:22.580 I want just the ID and the title or the, I just want the ID and the ISBN. 00:33:22.580 --> 00:33:24.480 Don't return the other results, right? 00:33:24.480 --> 00:33:26.080 So that's an option. 00:33:26.080 --> 00:33:28.360 The N plus one thing we already discussed, right? 00:33:28.400 --> 00:33:33.820 You just use the subquery or the B filter select or whatever it is for Django and you can avoid those, right? 00:33:33.820 --> 00:33:43.160 So like, as you kind of go through these, you're like, okay, well, most of the time these problems are actually solved with some aspect of like a proper ORM. 00:33:43.160 --> 00:33:50.820 Now, the transaction one is really, I think, super interesting because it sort of often gets to the heart of this debate about ORMs. 00:33:50.820 --> 00:33:57.220 And you're saying, well, okay, here's this active record thing where it's not really leveraging transactions. 00:33:57.220 --> 00:33:59.780 We know transactions are good. 00:33:59.780 --> 00:34:02.940 And so this is bad because it doesn't do it. 00:34:02.940 --> 00:34:05.980 But in practice, it's not so clean as that. 00:34:05.980 --> 00:34:14.740 So for example, suppose I'm working on a web app and I have a grid, like a grid that was maybe could be loaded off of a REST endpoint, bring that into it, right? 00:34:14.800 --> 00:34:18.400 And I've got this grid and I can type in it and there's a button that says save. 00:34:18.400 --> 00:34:23.720 There's no way that it makes sense to do a transaction around that, right? 00:34:23.720 --> 00:34:28.060 I'm not going to transactionally begin loading the grid and wait for me to press save, right? 00:34:28.060 --> 00:34:30.380 That's going to lock up the database for every user. 00:34:30.380 --> 00:34:30.740 Yeah. 00:34:30.860 --> 00:34:33.360 Any scenario like that, like REST endpoint, right? 00:34:33.360 --> 00:34:40.400 If I've got a phone and I've got my mobile app and it hits the REST endpoint, pulls it down to the data and I hit a type on it and I hit save, right? 00:34:40.400 --> 00:34:41.900 You can't do that transactionally. 00:34:41.900 --> 00:34:45.840 Like it just, you would lock up the site like right away, right? 00:34:45.840 --> 00:34:47.160 So it doesn't make any sense. 00:34:47.160 --> 00:34:56.380 So there's just other patterns like optimistic concurrency is a super common pattern in ORMs that would work with Active Record or SQL Alchemist, you know, work beautifully. 00:34:56.380 --> 00:35:00.960 And the idea is I'm going to make some kind of version in that record. 00:35:00.960 --> 00:35:04.560 And when I pull it back, it's going to come with the version that I got. 00:35:04.560 --> 00:35:10.480 And when you hit save, you say, update this record where the version is the version I have. 00:35:10.480 --> 00:35:14.920 So if someone else has updated it, it increments that version and it says, no, no, there's no record. 00:35:14.920 --> 00:35:15.900 You can't update this. 00:35:15.900 --> 00:35:17.380 All right. 00:35:17.380 --> 00:35:22.360 So you basically say, ah, it looks like someone changed this behind you, like your grid and their grid. 00:35:22.360 --> 00:35:23.760 They hit save before you. 00:35:23.760 --> 00:35:25.900 So you got to deal with like syncing this back up, right? 00:35:25.900 --> 00:35:32.460 So there's a lot of times where it's, it would feel great to like have a transaction, but that transaction actually can't be used anyway. 00:35:32.460 --> 00:35:38.100 And ORMs have like nice built-in ways where you can easily slot in like optimistic concurrency and stuff. 00:35:38.100 --> 00:35:39.880 So that's my thought. 00:35:39.880 --> 00:35:41.500 I think this is an interesting article. 00:35:41.500 --> 00:35:49.820 It's definitely interesting to think about all the points brought up, but I often think that the tools have like clever, non-obvious ways to solve most of these problems. 00:35:49.820 --> 00:35:50.180 Yeah. 00:35:50.180 --> 00:35:50.200 Yeah. 00:35:50.200 --> 00:35:58.180 And I guess to be, I'll be a little bit on Cal side here that the tools have clever, non-obvious ways to deal with them. 00:35:58.740 --> 00:36:10.680 Maybe that's, that's an issue that all of our beginning tutorials on how to use Django or how to use SQLAlchemy or how to use other ORMs are just ignoring that stuff because it's more advanced. 00:36:10.680 --> 00:36:16.340 But people often just read the beginning tutorial and then go do a startup or something. 00:36:16.340 --> 00:36:17.100 Yeah, sure. 00:36:17.160 --> 00:36:20.580 And then you end up with your page loading like in six seconds and you don't know why. 00:36:20.580 --> 00:36:21.420 Yeah. 00:36:21.420 --> 00:36:22.820 Which is not great. 00:36:22.820 --> 00:36:25.720 Maybe we could teach people the right way to do it from the beginning. 00:36:25.720 --> 00:36:28.940 I do wish that some of these patterns were more built in. 00:36:28.940 --> 00:36:35.900 Like I wish optimistic concurrency was there by default in the ORMs and you've kind of got to like roll that yourself and whatnot. 00:36:35.900 --> 00:36:38.420 So anyway, it's a really interesting article to think about. 00:36:38.420 --> 00:36:45.640 And I think it dovetails nicely with my sort of performance one as well because it's, they're kind of two sides of the same coin a bit there. 00:36:45.640 --> 00:36:46.000 Yeah. 00:36:46.000 --> 00:36:46.720 Okay. 00:36:46.880 --> 00:36:47.080 All right. 00:36:47.080 --> 00:36:48.860 Well, I have the second side to your coin. 00:36:48.860 --> 00:36:49.720 That is the Dacity. 00:36:49.720 --> 00:36:50.660 Dacity. 00:36:50.660 --> 00:36:51.600 This is it? 00:36:51.600 --> 00:36:52.540 Whatever that one was called. 00:36:52.540 --> 00:36:53.420 Dacity. 00:36:53.420 --> 00:36:53.760 Yeah. 00:36:53.760 --> 00:36:59.360 So this is a cool thing by Steve Brzeer called Types at the Edge of Python. 00:36:59.360 --> 00:37:00.760 The Edges of Python. 00:37:00.760 --> 00:37:04.260 And so Steve apparently creates a bunch of APIs. 00:37:04.260 --> 00:37:10.220 And I think, yeah, he was using FastAPI at the time when he was talking about all these ideas. 00:37:10.220 --> 00:37:12.960 But it's kind of generally valid for all of them. 00:37:12.960 --> 00:37:16.360 Because look, when I start with a new, when I create a new API these days, 00:37:16.600 --> 00:37:18.060 I start with three things. 00:37:18.060 --> 00:37:23.740 I start with Pydantic, mypy, and some kind of error tracking like Rollbar or Sentry or something like that. 00:37:23.740 --> 00:37:24.060 Okay. 00:37:24.060 --> 00:37:24.980 That's pretty interesting, right? 00:37:24.980 --> 00:37:31.300 So Pydantic is a data translation and validation library, much like Dacity. 00:37:31.300 --> 00:37:32.920 Right? 00:37:32.920 --> 00:37:33.420 Yeah. 00:37:33.420 --> 00:37:35.860 They're not the same, but they kind of play in the same realm. 00:37:35.860 --> 00:37:40.700 They transform JSON with validation and type checking over there. 00:37:40.700 --> 00:37:48.560 And then there's mypy, which looks like you can use Pydantic to help specify some of the types on your classes. 00:37:48.560 --> 00:37:53.080 And then use mypy to verify that you're not missing some kind of check. 00:37:53.080 --> 00:37:58.780 So he says, look, the most common error you're going to run into as a Python developer in general is attribute error. 00:37:58.780 --> 00:38:01.800 None type object has no attribute X. 00:38:01.900 --> 00:38:03.760 Where X is whatever you're trying to do, right? 00:38:03.760 --> 00:38:04.680 Yeah. 00:38:04.680 --> 00:38:11.440 I mean, that just means you got none instead of a value, and you're trying to continue to work with that class in some way. 00:38:11.440 --> 00:38:13.000 It's a void dereference in C. 00:38:13.000 --> 00:38:13.760 Yes, exactly. 00:38:14.480 --> 00:38:23.240 So wouldn't it be nice if it said none is not an allowed value for this, or you have none and you can no longer operate on it or something like that? 00:38:23.240 --> 00:38:26.460 So Pydantic will actually give you those types of errors. 00:38:26.460 --> 00:38:33.240 It'll convert things like attribute errors and mismatch type errors to explain what was wrong, right? 00:38:33.240 --> 00:38:34.060 So that's pretty awesome. 00:38:34.060 --> 00:38:42.400 And so you can use Pydantic to actually specify what your understanding of the interface, like if you're calling an API, the stuff that you expect to get back. 00:38:42.400 --> 00:38:43.880 Okay, I think this is going to be a date. 00:38:43.880 --> 00:38:46.200 I think this is an optional string and whatnot. 00:38:46.200 --> 00:38:51.260 It says, then when you launch a code into production, your assumptions are tested against reality. 00:38:51.260 --> 00:38:53.500 That's pretty cool. 00:38:53.500 --> 00:38:55.660 And it says, if you're lucky, they turn out to be correct. 00:38:55.660 --> 00:39:00.340 But if not, you're going to run into some of these none type errors and Pydantic can help with that. 00:39:00.340 --> 00:39:06.020 But then you can also, once you put in the typing into your code, then mypy will go on helping. 00:39:06.020 --> 00:39:11.980 So for example, if you're taking an argument that says, you know, first, you think it's a string. 00:39:12.060 --> 00:39:14.660 So you say colon stir refers type, then you go work with it. 00:39:14.660 --> 00:39:16.100 And that means it cannot be none, right? 00:39:16.100 --> 00:39:22.640 Like none ability is explicitly set in the type thing in Python and the type space. 00:39:22.640 --> 00:39:29.700 So if you find out that it could be none, then you're going to go and say, this is a typing dot optional of string, right? 00:39:29.700 --> 00:39:31.000 Like that's what it's got to be. 00:39:31.000 --> 00:39:34.500 If it could be none or a string, you'd find that out and specify that in Pydantic. 00:39:34.500 --> 00:39:41.560 And then if you run mypy against it and you start working with an optional string, you don't check for it to be none first. 00:39:41.560 --> 00:39:46.500 Mypy will actually give you an error saying that you're not checking for none, basically. 00:39:46.500 --> 00:39:54.200 So it'll even tell you like the missed if statements or other conditional code to like verify that like, no, it's not the optional none. 00:39:54.200 --> 00:39:55.180 It's actually the value. 00:39:55.180 --> 00:39:55.760 Okay. 00:39:55.760 --> 00:39:56.580 That's pretty cool, right? 00:39:56.580 --> 00:39:57.200 And then if you will. 00:39:57.360 --> 00:39:58.440 Tripped me up before, yeah. 00:39:58.440 --> 00:39:59.120 Yeah, for sure. 00:39:59.120 --> 00:40:01.800 I mean, normally it's just not present. 00:40:01.800 --> 00:40:04.740 And it's not because Python is a dynamic language. 00:40:04.740 --> 00:40:06.980 Like C++ would have the same problem, right? 00:40:06.980 --> 00:40:15.800 If you take a pointer and you just start to work with it in C, C++, the compiler is not going to say, you didn't check that for, you know, equal to null first. 00:40:15.800 --> 00:40:17.620 It just doesn't do that, right? 00:40:17.620 --> 00:40:17.800 Yeah. 00:40:17.800 --> 00:40:21.500 So this is a really awesome like addition for like safety in your code. 00:40:21.500 --> 00:40:26.700 So he was talking about how FastAPI automatically integrates with Pydantic out of the box, which is pretty cool. 00:40:26.980 --> 00:40:33.780 And then also at the end, he has a kata, a mini kata that like works you through these ideas. 00:40:33.780 --> 00:40:37.120 So a kata is like a practice to like play with these typing ideas. 00:40:37.120 --> 00:40:37.540 Yeah. 00:40:37.540 --> 00:40:40.260 And a nice picture of how these all fit in. 00:40:40.260 --> 00:40:40.940 Yeah. 00:40:40.940 --> 00:40:41.140 Yeah. 00:40:41.140 --> 00:40:41.860 Yeah. 00:40:41.860 --> 00:40:42.720 There's some cool diagrams. 00:40:42.720 --> 00:40:54.260 So anyway, if you're building APIs and you're taking data, especially from sources where they might give you junk when you expected something valuable or you're not really sure, you're like the docs say this, but I remember getting something different some other time. 00:40:54.500 --> 00:40:58.360 This is a really cool way to formalize that and then have your code automatically check it. 00:40:58.360 --> 00:40:58.740 Yeah. 00:40:58.740 --> 00:40:59.400 This is cool. 00:40:59.400 --> 00:41:00.060 I like it. 00:41:00.060 --> 00:41:00.540 Yeah. 00:41:00.540 --> 00:41:00.940 Awesome. 00:41:00.940 --> 00:41:02.700 That's all over our six items. 00:41:02.700 --> 00:41:05.140 Do you have any extra little things to share? 00:41:05.140 --> 00:41:12.240 Well, I kind of went overboard on the extras this week, but I'll keep them all quick because there's a bunch of cool stuff out there that people send in. 00:41:12.300 --> 00:41:15.380 First, Jack McHugh did a really cool thing. 00:41:15.380 --> 00:41:23.040 So Jack McHugh created a blog post or a page on a site called Python Bytes Awesome Package List. 00:41:23.040 --> 00:41:24.380 Have you seen this? 00:41:24.380 --> 00:41:24.880 Yeah. 00:41:24.880 --> 00:41:34.180 And he like listened to 174, 171 episodes in 174 days or something like that of Python Bytes. 00:41:34.320 --> 00:41:38.480 I mean, this is awesome because as I flip through this, there's a couple of things I've forgotten. 00:41:38.480 --> 00:41:39.500 I'm like, oh, that's cool. 00:41:39.500 --> 00:41:41.720 Oh, we must have talked about that, but I don't even remember. 00:41:41.720 --> 00:41:43.400 It's got beautiful pictures. 00:41:43.400 --> 00:41:47.440 It's, I mean, it's kind of an awesome list, but it's for a podcast. 00:41:47.440 --> 00:41:49.040 So that is super cool, Jack. 00:41:49.040 --> 00:41:50.040 Thank you. 00:41:50.040 --> 00:41:50.680 Thank you. 00:41:50.840 --> 00:41:52.380 I'll be sure to link to it at the end. 00:41:52.380 --> 00:41:54.480 And I hope you keep adding to it. 00:41:54.480 --> 00:41:57.060 That would be great, but no pressure. 00:41:57.060 --> 00:41:57.400 Yeah. 00:41:57.400 --> 00:41:59.240 I want to talk about VB.net for a second. 00:41:59.240 --> 00:42:00.400 That's kind of weird, right? 00:42:00.400 --> 00:42:01.380 Yeah. 00:42:01.380 --> 00:42:08.620 Because I kind of appreciated VB back in the early days when it was like a drag and drop VB6 and whatnot. 00:42:08.620 --> 00:42:12.600 And then Microsoft came up with a thing called Visual Basic.net and it was complete crap. 00:42:12.600 --> 00:42:13.680 Didn't like it. 00:42:13.680 --> 00:42:20.100 But here's what's interesting is like they have just announced that they are no longer maintained. 00:42:20.280 --> 00:42:23.300 They'll keep that thing running, but they will no longer work on it. 00:42:23.300 --> 00:42:24.360 And I just thought it was interesting. 00:42:24.360 --> 00:42:32.640 Like here's a fairly major language, not super top five or something, but it's kind of a major language that's like declared dead. 00:42:32.640 --> 00:42:37.920 And I just thought it was kind of interesting to point out like, man, languages, they can go dead. 00:42:37.920 --> 00:42:39.200 It's weird. 00:42:39.200 --> 00:42:40.040 Yeah. 00:42:40.040 --> 00:42:44.240 I think this one should have been shot a long time ago, but you know. 00:42:44.240 --> 00:42:46.100 It's also worth thinking about this. 00:42:46.100 --> 00:42:47.240 I agree, by the way. 00:42:47.240 --> 00:42:48.280 It should have never existed. 00:42:48.480 --> 00:42:50.400 But anyway, that's a different story. 00:42:50.400 --> 00:42:57.000 It's also an interesting take on like, here's a language controlled by a single company and they can just decide they don't like it anymore. 00:42:57.000 --> 00:42:57.800 Right? 00:42:57.800 --> 00:43:03.400 Like this wouldn't really happen to Python because there's not a single person or organization that goes, ah, we're done. 00:43:03.400 --> 00:43:03.900 Yeah. 00:43:04.640 --> 00:43:08.580 Well, that's actually one of the fears I have for, I mean, even Java. 00:43:08.580 --> 00:43:12.620 Java is not controlled by one company, but it kind of is sort of. 00:43:12.620 --> 00:43:12.840 Yeah. 00:43:13.360 --> 00:43:13.760 Yeah. 00:43:13.760 --> 00:43:21.280 Well, and there's also that, that Supreme Court case or the legal case of like, are you allowed to copy the Java API? 00:43:21.280 --> 00:43:23.220 I don't think that's resolved yet. 00:43:23.220 --> 00:43:23.760 I can't remember. 00:43:23.760 --> 00:43:25.760 It's still working its way through the courts. 00:43:25.760 --> 00:43:26.620 I want to reiterate. 00:43:26.620 --> 00:43:27.220 I don't. 00:43:27.480 --> 00:43:29.000 I don't think that's just a good idea. 00:43:29.000 --> 00:43:29.480 I don't think that's a good idea. 00:43:29.480 --> 00:43:29.920 I don't think that's a good idea. 00:43:29.920 --> 00:43:30.160 I don't think that's a good idea. 00:43:30.160 --> 00:43:31.060 I don't think that's a good idea. 00:43:31.060 --> 00:43:31.940 I don't think that's a good idea. 00:43:31.940 --> 00:43:32.240 I don't think that's a good idea. 00:43:32.240 --> 00:43:33.120 I don't think that's a good idea. 00:43:33.120 --> 00:43:34.120 I don't think that's a good idea. 00:43:34.120 --> 00:43:35.120 I don't think that's a good idea. 00:43:35.120 --> 00:43:36.120 I don't think that's a good idea. 00:43:36.120 --> 00:43:36.120 I don't think that's a good idea. 00:43:36.120 --> 00:43:37.120 I don't think that's a good idea. 00:43:37.120 --> 00:43:38.120 I don't think that's a good idea. 00:43:38.120 --> 00:43:39.120 I don't think that's a good idea. 00:43:39.120 --> 00:43:39.120 I don't think that's a good idea. 00:43:39.120 --> 00:43:40.120 I don't think that's a good idea. 00:43:40.120 --> 00:43:41.120 I don't think that's a good idea. 00:43:41.120 --> 00:43:41.120 I don't think that's a good idea. 00:43:41.120 --> 00:43:42.120 I don't think that's a good idea. 00:43:42.120 --> 00:43:43.120 I don't think that's a good idea. 00:43:43.120 --> 00:43:44.120 I don't think that's a good idea. 00:43:44.120 --> 00:43:45.120 I don't think that's a good idea. 00:43:45.120 --> 00:43:46.120 I don't think that's a good idea. 00:43:46.120 --> 00:43:47.120 I don't think that's a good idea. 00:43:47.120 --> 00:43:49.120 I don't think that's a good idea. 00:43:49.120 --> 00:43:50.120 I don't think that's a good idea. 00:43:50.120 --> 00:43:51.120 I don't think that's a good idea. 00:43:51.120 --> 00:43:52.120 I don't think that's a good idea. 00:43:52.120 --> 00:43:53.120 I don't think that's a good idea. 00:43:53.120 --> 00:43:54.120 I don't think that's a good idea. 00:43:54.120 --> 00:43:55.120 I don't think that's a good idea. 00:43:55.120 --> 00:43:56.120 I don't think that's a good idea. 00:43:56.120 --> 00:43:57.120 I don't think that's a good idea. 00:43:57.120 --> 00:43:58.120 I don't think that's a good idea. 00:43:58.120 --> 00:43:59.120 I don't think that's a good idea. 00:43:59.120 --> 00:44:00.120 I don't think that's a good idea. 00:44:00.120 --> 00:44:01.120 I don't think that's a good idea. 00:44:01.120 --> 00:44:02.120 I don't think that's a good idea. 00:44:02.120 --> 00:44:03.120 I don't think that's a good idea. 00:44:03.120 --> 00:44:04.120 I don't think that's a good idea. 00:44:04.120 --> 00:44:05.120 I don't think that's a good idea. 00:44:05.120 --> 00:44:06.120 I don't think that's a good idea. 00:44:06.120 --> 00:44:07.120 I don't think that's a good idea. 00:44:07.120 --> 00:44:13.120 like the Johns Hopkins CSSE data set and some other dashboards and some things on Kaggle. 00:44:13.120 --> 00:44:15.120 So if you're in data science, you want to explore it. 00:44:15.120 --> 00:44:17.120 Here's some data sets that are probably interesting. 00:44:17.120 --> 00:44:21.120 And finally, working a new course, adding a CMS to your data-driven web app. 00:44:21.120 --> 00:44:22.120 That'll be a lot of fun. 00:44:22.120 --> 00:44:24.120 And I'll talk more about that later. 00:44:24.120 --> 00:44:28.120 But I'm just super excited to be creating more courses as we kind of talked about earlier. 00:44:28.120 --> 00:44:29.120 Yeah. 00:44:29.120 --> 00:44:34.120 One of the things we talked about is people working from home and getting around technical problems with that. 00:44:34.120 --> 00:44:36.120 That happened to me just this morning. 00:44:36.120 --> 00:44:38.120 So this morning I tried to hook up. 00:44:38.120 --> 00:44:43.120 I realized that I had an external keyboard that's working fine-ish. 00:44:43.120 --> 00:44:46.120 I wanted to use like a real mouse. 00:44:46.120 --> 00:44:53.120 So I plugged in an external mouse with a little click wheel thing on it and realized that on Apple, 00:44:53.120 --> 00:44:58.120 the click wheel behavior just goes the wrong direction for scrolling. 00:44:58.120 --> 00:45:00.120 And it confused me. 00:45:00.120 --> 00:45:04.120 And you can reverse it, but I didn't want my trackpad to be reversed. 00:45:04.120 --> 00:45:05.120 The trackpad's fine. 00:45:05.120 --> 00:45:07.120 So they're tied together for some reason. 00:45:07.120 --> 00:45:08.120 Weird. 00:45:08.120 --> 00:45:17.120 So Dave Forjack, sorry Dave, he suggested I use something called the scroll reverser. 00:45:17.120 --> 00:45:25.120 That is a little tiny app that allows you to untie those and have trackpad scrolling and mouse scrolling be different. 00:45:25.120 --> 00:45:26.120 And thank you, Dave. 00:45:26.120 --> 00:45:27.120 That's awesome. 00:45:27.120 --> 00:45:28.120 That's super cool. 00:45:28.120 --> 00:45:37.120 I guess my work from home thing that I've been playing with is with Zoom, you can have virtual backgrounds. 00:45:37.120 --> 00:45:38.120 You don't even have to have a green screen. 00:45:38.120 --> 00:45:47.120 You can have like alternate backgrounds just by uploading an image and it'll put you in, you know, an office space instead of a messy bedroom or whatever it is. 00:45:47.120 --> 00:45:48.120 Oh, nice. 00:45:48.120 --> 00:45:48.120 Yeah. 00:45:48.120 --> 00:45:51.120 So you can block out the kids behind you and stuff like that. 00:45:51.120 --> 00:45:52.120 Yeah, exactly. 00:45:52.120 --> 00:45:55.120 You don't have to see the kids being crazy home from school and whatnot. 00:45:55.120 --> 00:45:56.120 Anyway. 00:45:56.120 --> 00:45:58.120 So you can do a lot of stuff we're learning around those types of things. 00:45:58.120 --> 00:46:08.120 And I think the joke that I chose for us this week is going to be perfect for the opening of community as documentation as building community that you brought up. 00:46:08.120 --> 00:46:09.120 Okay. 00:46:09.120 --> 00:46:13.120 This is before that person gets inspired from listening to you and actually makes things better. 00:46:13.120 --> 00:46:14.120 All right. 00:46:14.120 --> 00:46:16.120 So let me, let me set the stage here. 00:46:16.120 --> 00:46:28.120 There's three people, two of them clearly more senior and a very excited new person sitting in a laptop, like beaming with enthusiasm, ready to get going on the whole project. 00:46:28.120 --> 00:46:33.120 And one of the senior person says to the other, and this is Jim, our new developer. 00:46:33.120 --> 00:46:35.120 The other one says, great. 00:46:35.120 --> 00:46:37.120 Does he already know something about our system? 00:46:37.120 --> 00:46:39.120 The new person turns around. 00:46:39.120 --> 00:46:41.120 I read the whole documentation. 00:46:41.120 --> 00:46:44.120 Blink looks between the senior people. 00:46:44.120 --> 00:46:45.120 No. 00:46:45.120 --> 00:46:46.120 Yeah. 00:46:46.120 --> 00:46:47.120 Yeah. 00:46:47.120 --> 00:46:48.120 That's good, right? 00:46:48.120 --> 00:46:49.120 Yeah, definitely. 00:46:49.120 --> 00:46:57.120 I started a job once in my career where I had read the documentation because it was an internal job transfer. 00:46:57.120 --> 00:47:03.120 I read the documentation before getting there and the people there that didn't know they had documentation. 00:47:03.120 --> 00:47:06.120 So it was so out of date. 00:47:06.120 --> 00:47:08.120 Nobody currently knew it existed. 00:47:08.120 --> 00:47:09.120 Yeah. 00:47:09.120 --> 00:47:11.120 It may be a little out of date if they don't even know it exists. 00:47:11.120 --> 00:47:12.120 Yeah. 00:47:12.120 --> 00:47:13.120 All right. 00:47:13.120 --> 00:47:14.120 Well, awesome. 00:47:14.120 --> 00:47:15.120 Cool. 00:47:15.120 --> 00:47:16.120 Well, thanks a lot. 00:47:16.120 --> 00:47:17.120 You bet. 00:47:17.120 --> 00:47:19.120 Great to be here with you as always. 00:47:19.120 --> 00:47:20.120 See you later. 00:47:20.120 --> 00:47:21.120 Bye. 00:47:21.120 --> 00:47:22.120 Thanks for listening to Python Bytes. 00:47:22.120 --> 00:47:24.120 Follow the show on Twitter at Python Bytes. 00:47:24.120 --> 00:47:27.120 That's Python Bytes as in B-Y-T-E-S. 00:47:27.120 --> 00:47:30.120 And get the full show notes at Pythonbytes.fm. 00:47:30.120 --> 00:47:36.120 If you have a news item you want featured, just visit Pythonbytes.fm and send it our way, 00:47:36.120 --> 00:47:38.120 where I was on the lookout for sharing something cool. 00:47:38.120 --> 00:47:44.120 This is Brian Okken, and on behalf of myself and Michael Kennedy, thank you for listening and sharing this podcast with your friends and colleagues.