Pybites Podcast

#062 - The Art of Debugging (and Design Patterns)

Today we have Thomas Gaigher (creator of pypyr) back on the show to talk about debugging.

We hit on various important areas:
- Visualization.
- The debugging process.
- Tracking and tracing.
- Preventative measures.

We even unexpectedly (and interestingly) spoke about design patterns.

We hope you enjoy the show.

Mentioned resources:
- A debugging tale PyBites article
- Thomas' first appearance on the podcast

Books mentioned:
- Metropolitan novel
- Design patterns (Gang of Four)

Reach out to Thomas:
- Twitter
- PyBites Slack

---
Are you serious about taking your Python career to the next level?

Our PDM program takes you from scripter to proficient Pythonista in just 10 weeks.

You'll become an effective developer that writes robust software and knows how to market those skills well.

Does that sound appealing to you? If so, book us in for a chat and we'll see how PDM would look for you, based on your individual needs and goals ...

If you're using the debugger because you have no other way of knowing what your code is doing under the hood, you're doing something wrong. It means you're not logging enough, you're not instrumenting enough, you haven't created enough infrastructure in your program to be able to track and trace your issues effectively. Hello, and welcome to the Pibytes podcast, where we talk about Python career and mindset. We're your hosts. I'm Julian Sequeira. And I am Bob Valdebos. If you're looking to improve your python, your career, and learn the mindset for success, this is the podcast for you. Let's get started. Welcome back to the Pibytes podcast. This is Bob Elderboss, and today I have Thomas Geiger back on the show, and we talk about some very useful debugging techniques and ways to make your code more robust. And we even go into design patterns as a nice bonus. So, without further ado, hope you enjoyed this episode and reach out to us if you have any feedback. All right, Thomas, welcome back to Pivyte's podcast. Really happy that you're back. How are you doing today? Pleasure being back, Bob, and thank you for having me. Yeah. So, what are we going to talk about today? I believe we were going to talk about debugging, because it is exciting. It's sort of an overlooked activity, isn't it? Or it's interesting to me how we tend to separate mentally coding versus debugging, as if it's two wildly separate things. But the process of coding really is debugging. I mean, unless you're an exceptional person who can just compose code in your head and it runs perfectly the first time you hit the button. If you're that person, well done. Switch off this episode and go back to writing code. Yeah, realistically, probably not. If you're the rest of us, then I'm sure the coding process involves debugging at some point, right? Yeah, exactly. So before getting to the meat, we always start with some wins, right? So, do we have a win to share today? Yeah, I actually, and it's apropos to the topic that is debugging, that I spend the weekend chasing down a annoying little issue in Piper, which is the open source project I maintain, and I noticed the same thing in a blog article you recently wrote, which is that the debugging wasn't actually on functional code in and of itself. It was actually on the code around the code. In other words, the automation code or the testing code. So there's multiple layers of indirection here, where not actually the functionality itself works, the functional code, but the code that is testing the code is not quite doing what we expect it to do. So you're in fact debugging the code that is testing or debugging the code that you have. And this also leads me to the thought that I think this is something we can very easily overlook when we're planning how much work something actually is. And we say, I think this will take me a day, but it ends up taking three days. Because we thought about the functional code change, we didn't think about the code around the code that we would also have to work with. So this involved class factories, subtleties around how the class type is injected into a class method, and how you can intercept that via mocking it is pretty abstruse. And unless you've run into this yourself, you're very unlikely to care. So I won't bore you with details and yourself. Have you had a good win recently, Bob? Yes. Before I move on, if people want to check out your project, it's Piper. P y p y R. Right, that's it. And we spoke about it on episode 41, letting the target hit the road. How much software planning do you really need? Your first appearance on the Pivots podcast? So, yeah, if you want to see some really cool python project and code, and I just want to leave that reference here and we, of course, link it in the show notes. My win? Well, I bored the audience enough with the fast API learning path we're doing, and that's coming along. That's a win in itself. But to take it to debugging, I wrote an article about debugging based on an issue I was hitting with isort and precommit. And that's a win itself because we hadn't updated the blog in three or four months, or maybe even more. Yeah, I think the takeaway for me was like, if you hit interesting things in your developer workflow, write it down. Because I could have easily moved along, but this was an interesting. There were some interesting things I learned and practiced, and some of that we're going to talk about today, that's pretty. Well, it took me like 2 hours to make it to a fully flashed blog post. Right. It's now a content piece, and we can point people to it and it will help people. Right. So I think that's a win. Good win, too. And a good tip. Write things down. Yeah, the other thing I want to say about that is I literally opened Vim and just started writing in plain text. And Derek Sivers last week shared an article about that, that he's prolific because, because he just uses plain text, and then he later worries about making it beautiful and turning it into books and whatnot. But he just writes in an editor, and that's an enormous relief because it's very easy to, and I'm getting into a lot of side topics. But anyway, this one I think is important because we always say as a python developer, you should become a content creator. But a lot of people over perfect that process. Right. But no, just start with plain text and close to that, you could do actually markdown on GitHub. Right. It's not that. Don't worry about the tools, basically. Yeah. And I wouldn't, I mean, personally, given that the word content creator maybe carries overtones today of doing TikTok videos, in my case, I mean, write things down just as notes to yourself. Because again, speaking for myself, but three weeks from now, I don't remember why I did something or even if I did something. In fact, I just in the last week had a joy of having to set up a new development environment on a new machine. And there's a lot of little tweaks that you don't remember you made in your environment, setup little config files you might have fiddled with. And if you don't have those written down somewhere, finding all those settings again is a significant time sink. No, exactly. Great tip. Yeah, very important. That's scary how easy and how much we forget. All right, let's get into some debugging then. I noted down four subtopics here. We wanted to address visualization, debugging process, tracking and tracing and preventative measures. So, starting with the visualization. What do you mean with visualization? Why is it important? I don't want to veer too much into chess as an analogy, but if any listeners are into chess, a key skill is visualization, which is to say, when you're playing chess, you have to plan in advance the moves you're going to make. And good players have an ability to see in their head the way the moves are going to work. Also, maybe there was that Netflix series where they demonstrated the lady having the chessboard on the inner head displayed on the ceiling. To make yourself a better coder, you need to be able to, as you read code, you need to be able to visualize what that code is actually going to do. I've got a little bit of a side point here, which is I've noticed some people depend on the debugger a little bit too much. Now, if you're just beginning to code and you're still learning code, then I think a debugger is useful, and the debugger is absolutely a useful tool. But if you're using the debugger because you have no other way of knowing what your code is doing under the hood, you're doing something wrong. It means you're not logging enough, you're not instrumenting enough. You haven't created enough infrastructure in your program to be able to track and trace your issues effectively. You should really be coding and planning your code as if a debugger didn't even exist. Your code should have the ability to pump out log outputs. If you, for example, change an environment variable, or maybe there's a debug switch that you have at the end of a CLI app. However you do it, you need to be able to switch on some sort of verbose debugging mode that will give you detailed error output, detailed error messages, log error messages somewhere. How exactly you do this will depend on your platform. It will depend on the context in which your app runs. But I'm sure I don't need to lecture people on the options. You have everything from event logs to your own custom tracking and tracing files. Thus then with visualization. I'm not saying don't use the debugger. I'm saying a debugger is a useful tool while you're still coding. But when you're dealing with an app that has been deployed and in production, or you're trying to hunt down an error that someone else will reported to you, that being able to visualize what code does just as you read it is an important skill to have, especially with async code, because a debugger works very well when you're stepping through sync code. But the moment you start dealing with async and you've got events firing all over the place and it's happening asynchronously, that gets very hard to conceptualize and track what's going on. I think it's a good idea to harden yourself or train yourself to develop that skill. And sadly, I don't really have a good tip for learning this quickly. I think it just comes with time. Do it 10,000 times and you'll maybe get a bit better at it. Code run your code. Check your output logs where you write about what the code actually does. I think what you get at the end of the day with visualization is that it helps you track down areas of interest quicker. So when you're in a code base and you're trying to track down an error, you're trying to track down a transient issue. You can find your usual suspects more quickly because you can speed read through the code and have a good mental idea of what that code will be doing and where the problems are likely to be. And then you can hone in on where the problem is likely to be more quickly. Yeah, in the article I wrote, which we'll link below, when I was debugging isort, the minus fee or for both switch was tremendously useful. That actually showed the exact strings I could then look up in the source code. Yes, only then I used the debugger and could be very targeted. But it started with the logging. It started with getting more messaging strings around the problem that I could then look for in a code base. Yeah, I think this brings us to a second point very quickly, which is visualization is all fine and good, and it helps you get to the problem more quickly, but then you have to get your hands dirty. I also noticed people forgetting about that step, or what I sometimes see is it's almost like a sense of existential despair that comes over a developer where you just stare at a broken line of code and you think it's going to fix itself somehow, or if you run the same code repeatedly enough that it will somehow start working. I wish that was true. Sadly not. Right. But the fact is, the point of a machine is that given the same inputs, it's going to give you the same output every time, which is good news as well. It is deterministic. It is deterministic, exactly. That's a good word for it. And I think psychologically it's important to accept that the thing is broken and it doesn't help you just to stare at the screen and press the run button repeatedly and hope that the 20th time it's somehow not going to be broken anymore. You have to get your hands dirty, you have to get in there, aggressively isolate the code that you think is causing the problem. And visualization will help you get to that code more quickly. Like you say, you can maybe put a debug point, a breakpoint. And what I also tend to do, Bob, is especially when I'm on large projects that are cumbersome to run because there's a lot of code in them, I actually physically copy out the code that I think isolate certain areas of the code into a script. Py or whatever. Exactly. Yeah. So in Arp, Py, script of Py, whatever it might be, I have a little scratch folder that I just keep messy little bits and pieces of string in it, like this. And in this py file I will copy and the bare minimum of the code I'm using. So let's say there's a class and there's a function and some imports that's needed to make them work, just copy and paste them in. Because this now allows me to run just this code in isolation. And I invariably start using my favorite debugging tool, which is print, and just hit throw in a bunch of print statements, printing out where you are in the sequence of code. What this also allows me to do is cycle over the code more quickly, because again, on larger frameworks, it might take longer to get to the thing that's breaking if you're running the entire sequence every time, this allows you to cycle much more rapidly over smaller iterations. What I do then is try and reproduce the problem. And at this point I start taking things away, like take as many things away as I can, very destructively, until either the problem is not there anymore or it breaks in a new way. And the new way it breaks in might give you some information that's interesting. In this regard. A very valuable button is the toggle comment shortcut. On Mac, it tends to be the mackie. On windows, it's control, forward slash, comment, out a line, run it again. Uncomment, aligned, run it again. Okay. So that you can see the effect of a line of code being in the output or not, like divide and conquer, like the bisect module, right. That you're. Yes, half it down every time to get exact piece of code that's failing. Yeah, exactly. And something else that helps me about this is kind of structural that, especially, again, in a big code base where you're maybe dealing with intertwining dependencies. So let's say there's a couple of classes that interact with each other in some sort of way. When you trim it down into one file like this, it gives you, for me at least, it makes it easier for me to see the structure. Like you can see the bare bones of how the components relate to each other structurally. And it makes it easier for me to spot at one glance if something is not relating to something else in the way that it should, or if it's not coordinating in the way that it should. Whereas something like that, you might spot that, say you're assigning to the wrong attribute, which is harder to do when the classes are on completely separate modules, for example, but when they're right on top of each other, you might notice a spelling mistake quicker, for example. Or you might notice a casing issue more quickly because you have something direct to compare it against. Yeah, it's all about getting to a reproducible use case that's as small as possible, isolated as possible, and that you can quickly run and run again, because you will run this constantly to pinpoint where the exact issue is, right? Yeah, exactly. Great tip. Yeah, I think that's one of the essential debugging techniques I use as well and will save you a lot of time and will be the difference between finding it or not finding it, or taking a whole day to fix a bug versus maybe fix it in half an hour, an hour. So it has a great roi. Yeah, definitely. So is that also in relation to the tracking and tracing or what did you have in mind for that subtopic? Yeah, I would say so. I mean, tracking and tracing for me is almost a preventative thing, in that it's something you should be wiring into your code from day one. So for example, notable branches or conditional statements you should log, the fact that you're in that branch, or that you've triggered that branch in your code to your debug output or your trace output. If you're in python, which I'm assuming you will be given the podcast we're on. If you're listening here, you probably are. Yeah. Use the logger class in Python. It is very light on performance. It's very optimized. So if you do logger debug your debug string, it doesn't actually have a real performance impact. You shouldn't be scared of littering your code with debug statements. You mean the login module from the standard library, right? Not an external? Yeah, absolutely. Just standard lib logging, nothing fancy. It's plenty optimized. It's plenty clever how it works with logging levels. Good example of a singleton pattern if you're looking for one. And given how lightweight it is on performance, there's no real reason not to be using it and just aggressively helping you view from the future. When you hit a bug, then you can actually see what your code is doing. Hey, I know it's not a design patterns discussion, but you mentioned singleton. Can you expand a little bit on that? Yes. A singleton is either a anti pattern or a pattern, depending on your politics, but it is a design where you only ever have one instance of a class. Differently put, it's when you have a single class that is shared by all callers. Yeah. So a lot of sense with logging yeah, so a very typical thing. The way you might use a singleton is in a cache, which the exact purpose of a cache is you want to share some undefined stuff between different callers. Right. A singleton also gets you away from some of the difficulties you have with concurrency. If you can guarantee that you're only accessing one object in Python, you actually get singletons for free, because whenever you do a global variable in a module, it is effectively a singleton. The Python framework takes care of that for you. Interesting. This is maybe it's not so relevant in Python. It's a design pattern that you have to take a bit more care of when you're in c sharp or Java or one of the more traditional oo languages. In Python, like I said, you just get them for free, so it's not really a pattern you have to worry about too much. Yeah, interesting. Not to go into design patterns, but sometimes it's overrated in Python because it seems you get many for free, or you're already using them without specifically naming them that way. For example, default dict in the collections module, where you're basically using a factory. Yeah, without probably talking about a factory, you just do it. And you don't have to call it factory either. Yeah, back on the debugging topic. So the fourth one is preventative measures, and I think you already alluded to it a little bit so far. Any additional pointers there for us? Yeah, I think a lot of the how to code better tips that you're probably getting every day from somewhere. A lot of it are actually to do with making your troubleshooting and debugging life simpler. So encapsulating scope, avoid globals. I mean, the real reason for that is it makes it harder to get strange or hard to troubleshoot problems. Something I would also add to that is to avoid spaghetti. Spaghetti meaning code that lacks structural cohesion. I don't really know how to define spaghetti code other than I know it when I see it, which is not a great definition. Maybe very large functions with a lot of indenting and hard to isolate. Yeah. Where you don't really have separation of concerns anymore. A function is not doing a single thing anymore. Code has a lot of weird dependencies pointing in a lot of different directions. And it's not entirely clear how the object dependency graph looks. The cleaner your code is, the tidier your house is, the easier it is to spot when something is out of order. So there's a hygiene aspect to your coding that the more structurally cohesive you are, the more likely you are to find problems. Something I would add to that is adversarial testing, which is not really a phrase, but I'm introducing it by adversarial testing. I mean your unit test shouldn't be just testing the success path and then you're done. You should aggressively be trying to break your code. In other words, missing values, non inputs. A file is not available on disk. The file is available, but permissions are getting blocked, or permissions don't allow you to read it. Special characters encoding issues let's say you're not on UTF eight, which is still a reality if you're cross platform on Windows. If you're not testing adversarially, if you're not trying to break your code, you're not going to notice those problems, and those problems then rear their head later, which hopefully you can find quickly. Or maybe you don't. So, as an example of this, on the last update I was doing to Piper, I could have had a bug, but I didn't because of aggressive testing. Are you stuck in endless commutes? Have you ever dreamed about being able to work anywhere, control your schedule, give back to society, become an open source contributor, or become a successful developer, doubling your salary? Well, it's time to look at the PDM program, and it's time to actually build something that's going to help you get the future that you're looking for. The people that we've worked with in the PDM program have achieved some incredible things, including starting their own SaaS business with their own application. Imagine that. That could be you building your own application, selling it, making your own income. We've had people more than double their salary. I'm not making that up. I'll say it again, double their salary after completing our program and applying for developer jobs. These are the sorts of things that you can actually achieve through ten weeks of dedicated life coaching in the PDM program. So here's the challenge. If you are actually serious about taking your future into your own hands and not letting someone else control that for you, click the link below and get on a call with myself or Bob. That's right, we want to talk with you about your goals and how you can use Python to leverage your career. So book a call below and we cannot wait to talk with you soon. This has to do with mutable types in Python, which is to say something like a list or a dict. And so, as I think we all know, if you're append into a shallow copy of a list, the original list also updates oh yeah, yeah, that's insidious. It's very insidious. You're right. You're making copies to start with of that list of. Exactly. So you need to be very clear on when you're working with a shallow copy versus deep copy. Otherwise you might be mutating your original data. And if the original data is meant to be pristine, that's not something you should be doing. Now there's various ways of coding around this. You could, as a design pattern, only work with immutable objects. But the way that you catch this issue is by in my tests it so happens that I rather redundantly also checked the source object, that it was still as expected. Now that was strictly speaking not necessary for the test. The test was actually testing something else. But then that made me notice that I forgot about the shallow copy and that the original source was mutated. So this was preventing a hard to find bug just by taking effects. 30 seconds extra. Putting a few extra asserts in your code. Yeah, and once you have a couple of tests, it's easier to do. It's just adding another test case on the existing one, which the first test is usually the most work anyway, and asserts are cheap. Don't be shy. Even any existing tests just add more asserts testing that the source conditions are still as you expect them to be. It's not just about the destination, if you will. Another good housekeeping tip that I have is take the time to write good error messages, because again, future you will. Thank you. Another very recent example I had is I got an error that said attribute error object has no attribute get, which a little bit mysterious. Now if you've been around python a while, you probably would start thinking this might have something to do with a list, or it might have something to do with a property because it's looking for a get property. But even so, it's not the world's most useful error. So I find a lot of the time in better code I write. I'm actually spending a fair bit of time catching exceptions, not so, and re raising them. What I'm actually doing is augmenting the error message. So, especially if you're writing an end user facing code. In other words, it's not code for other developers, it's code that a non technical audience might consume. You want the error to be fully informative. Imagine you in six months when you can't remember anything about the code you just wrote. Would the error message actually tell you what the problem is? I'm sure we all have errors like this. We've seen object not found a very typical error. What does that mean to an end user? That's crap. Well, even to you. Yeah, because it's not naming any specific object or place in the code or the other famous one is object not defined. And it's okay. Why? What object is not defined? What's the code trying to do? Why is the code looking for this object to be defined? An error message like I can't find the configuration source because the web server's timed out. That starts being a useful error rather than object non exception because you didn't trap for the fact that there might be a non response from something so less mysterious errors. And that again, it's a code hygiene practice that take the extra minute or two to write your code a bit more carefully and to trap errors a bit more carefully. And that makes your debugging life in the future way easier. Yeah. Take two minutes now to save hours in the future. Yeah, exactly. Good return. Yeah. And also like that mindset here of not only thinking about the happy path, right, but becoming a skeptic and always think of what can go wrong. Right. And then you're probably going to add a lot more error checking to your code, right? Yeah, exactly. I mean, you know, saying this, this is not to say all exceptions should be handled. I mean, an exception is, should be an exceptional case where the program cannot proceed anymore. I'm merely suggesting that if you can make your errors provide more context, do so. And this is also relevant for your debugging and your tracing outputs that you do. The less knowledge you need when you're consuming the debug and the trace output, the better you know, because it means you have to guess and infer less when you try and troubleshoot your code in the future. Awesome. So visualization, debugging, process tracking and tracing preventive measures. Thanks for all the tips. I think that's very useful and it will definitely help our audience. Anything more to add or. Yeah, I guess if you're working in a corporate environment under SLA, so if you're doing ops or support work, that the way you debug and troubleshoot also becomes slightly different. I think when we're working in a more private capacity or we're working on open source even, or we're working on our personal projects, you almost view a bug as an opportunity to improve everything. Or you maybe will embark on a big refactor that will make the bug disappear. But when you're doing corporate work, you of course can't do that. What you in fact have to do is touch as little of the code as possible. So sometimes this even leads to what you might think of as a counterproductive fix where you might have to stitch up one or two lines, but a more fundamental problem remains. But you're not allowed to touch the more fundamental problem because you are in a code freeze, effectively, many developers changing the code in parallel. And it's way more restricted. Right. Yeah, you have to make compromises almost. You very much have to. And this makes it all the more important, what we were talking about earlier, which is isolating the problem aggressively and finding the exact line or the exact part of the line that is causing the problem and only changing that and not changing anything else. I mean, I despair that the world works this way, but sadly, when you're under commercial pressure, this is the reality of how you have to work. You don't necessarily get to make everything better and code everything up perfectly. There's like you say, Bob, the compromise to contend with. No, I'm happy that you're bringing this up because there's definitely a difference. Fixing a bug in a relatively small open source project versus working in an enterprise environment with a million lines of code, commercial solution, with 50 developers committing to it constantly, I found that you actually, yeah, you have to be very minimal in your scope and almost be creative to fix the bug, but really also manage there not to be any side effects. There's way more chance to introduce any side effects at that skill. Yeah. And also the side effects have commercial implications. Right. Because you have to meet SLA, the company has a financial penalty if you don't hit the SLA. Any further bugs you introduce as a result of fixing the bugs might have a further penalty implication on you. So there's some, shall we say, political considerations here in addition to the good software. Yeah. Can be very tough. Yeah, it's a tough line. Cool. But these tips will help also developers in that scenario, in that situation. So, yeah, again, thanks for sharing. And lastly, what are you reading? Any good books? I am reading some trashy Sci-Fi from the nineties. Nice. It's called Metropolitan by Walter John Williams. It's, I guess somewhere between fantasy and Sci-Fi I don't even know why I'm talking about this. I should be lying and saying I'm reading something much more worthy instead. You know, like that's cool. Like I think a lot of our audience will appreciate Sci-Fi like a, like a good improving book on design patterns or something. Yeah, it's sort of you. It's sort of like wizards, but in a Sci-Fi context. There's some in common with a lot of Sci-Fi there's a lot of philosophizing about social structures, politics, overpopulation in the future, political systems and how they relate to the populace. So in some ways there is a commentary on some social issues that we can even see today. In other ways, it's just a silly Sci-Fi book. So if you're into some classic nineties Sci-Fi Walter John Williams, he's a great author. Cool. I'll link that below. Yeah, interesting. I'm reading, as always, many books at the same time and not finishing any. But not gonna lie, Bob, every time I hear what you're reading, I feel very insecure because you're always reading very worthy and improving books. And here I am with complete trash on my side. I don't finish them writing solid code. I did finish. I even noticed Julian asking me, did you finish it? Yes, that one I did. But yeah, the gang of four design patterns book. Although it's a java, it's the classic, right? So a lot of designers will be java ish. I think it's still a great read just to think about design patterns and architecture. So yeah, I'm committing accountability here to finish that one once and for all. You were hinting earlier, Bob, when we were talking about the singleton pattern. Since you're reading the gang of four, do you often, or how does that interrelate with Python for you? Because like you mentioned, Python in very many ways doesn't quite do patterns in the same way that you would in Java because the language works differently. How are you finding those patterns relating into Python? Or is it more that the book is broadening your mind in other ways? Yeah, just generically. I'm not very far in the book actually, to be honest yet, but I already noticed that some patterns we just get for free almost, and we just don't like the default dicta I mentioned. Or libraries like factory boy when you want to create objects for Django or the factory pattern as Flask uses it on their documentation to create an app instance. So it's almost like a given. And you wouldn't really call those design patterns per se, but in that sense, Brennan Rhodes has this design pattern page as well. So after the book I'm definitely going to go to his because he will probably translate or filter out what's really relevant for Python. And I love his work. I mean, I've watched many of his talks. So yeah, for Python I definitely want to look at his site as well in this context. Yeah. One of the things I actually like about Python is that you don't have this overwhelming corpus of proper pattern practice that so frequently sidetracks from actual work getting done. Don't get me wrong, I'm all for patterns and practices and I'm all for a good design pattern, but a lot of the time in programming languages that will remain nameless for the second. I find that technical conversations turn into very abstract, almost theological grounds where people are arguing in the abstract about different design patterns rather than getting on with actual code. And there's sort of a practicality to python, get things done in a simple way, which is I like. Yeah, no, like the set of Python. Right. Practicality beats purity, and it's a language. You can get stuff done very quickly and in a clean way, but you're also responsible yourself, for example, with the whole encapsulation and private versus public attributes, where Java has getters and setters. In python you can just access anything, although then we work more with conventions. If a method or attribute has an underscore, then you should deal with it in a private manner, but the language doesn't enforce it. You can easily override and underscore something. Right? We're all consenting adults, right? Yeah, yeah, that's where that saying is coming from. Right. So. But yeah, that actually does lead to shorter code. And as long as we just be idiomatic about it and use the language as it was designed, then we're pretty good. And yeah, I think something that helps me particularly about patterns is it helps me recognize the shape of a problem better. So we were talking about a singleton earlier. Once you know about a singleton pattern, you start recognizing when you're using one that, oh, this is a singleton. But you also understand a bit more about what that means. When you start thinking about things like shared state or shared connections, you might more intuitively have start thinking about maybe using a singleton. If you're thinking about a problem where you might want to share a workload across multiple threads or multiple processors, a producer consumer pattern works quite well for that. And notice, what I'm suggesting is that I don't think there is a one true iteration of a pattern. How you implement a pattern is a practical question for the programming language or even the style that you're using within the programming language, because there's many roads lead to Rome. Right. We can solve the same problem in different ways, and the patterns are merely how other people have solved them. And if they solve the problem well enough. It might become canonical, but I'm not suggesting a slavist devotion to copying a pattern line by line. I'm more saying that it could be a good starting point for you to see the sort of things that someone else was thinking about when they were solving a similar style of problem. And it might also help you avoid a few bugs to circle back to debugging in. Because they learned from experience, right? They learned from flaw designs, and then they came up with patterns to overall make your code more or your design more robust. Yeah, exactly. And avoid the sort of day one issues that you would have run into if you've not had the benefits of their experience and the benefits of their hindsight of what they could or maybe should have done differently. And that's why I'm perfectly fine by reading the classic, which is more Java focused, because this is more like, this is not a syntax read, this is a philosophical read. Yeah, for sure. Awesome. Well, thanks for joining us today and providing all this insight. I think they'll be very useful for audience. And yeah, we sure can have you back. I was going to invite you back for design patterns, but we just did it, so you might have to come up with another topic. Well, design patterns, as a bonus, well, design patterns could be a longer talk, actually. Then again, I don't know how interesting it would be to whom, but, well, great catching up with you and yeah, you have a great day. Same for you, Bob. Thank you. Yeah, thanks. Hey, it's Bob again. I hope you enjoyed this episode. To reach out to Thomas, check out our pibyte slack on Pybit es community or hit him up on Twitter where he is. Piper pipes. So that's p y p y r, Pipes. If you have any feedback, reach out to us. You have preferences for topics we should discuss on this podcast. Let us know. And please give us a vote if you like this episode. I'm not sure how the podcast apps algorithms work, but it surely will benefit growing the show. Thanks for listening, and we're back next week. We hope you enjoyed this episode. To hear more from us, go to Pibyte friends, that is Pibit es friends and receive a free gift just for being a friend of the show. And to join our thriving slack community of Python programmers, go to Pibytes community. That's Pibit es forward slash community. We hope to see you there and catch you in the next episode.