Python Debugging: More Than Just A (Print) Statement
As most developers will agree, writing code is oftentimes, if not always, easier than debugging. As a simple definition, debugging is the process of understanding what is going on in your code. When speaking in terms of Python, it is a relatively simple process.
Every developer has their own personal debugging method or tool they swear by. When it comes to Python, most developers use one (or more) of the following: print statements, traditional logging, a pdb debugger, or an IDE debugger.
Alas, as is the way of the world, nothing is perfect. All four of these methods have both their pros and their cons. But the core of the issue when debugging remains: why is it so difficult to understand what’s happening in your code? What is the missing key that makes debugging so difficult- whether it be in Python or any other language? Let’s delve into two of the better-used methods in order to further understand what it is.
Method 1: Print Statements
One of the more common and well-loved methods to debugging Python applications is through writing print statements. Why, you ask? Well, simply put, it is the simplest way to understand what’s happening in your code so that you can then check what has been executed. This method involved putting print statements throughout your code to print the value of variables so that you can inspect them during runtime. For example: print(“The value of var is: “, var).
Print statements only examine the behavior of the code, necessitating the placement of many print statements in order to find the general area of the bug, hone in on the exact code that has the issue, find the relevant variables in that code, print those variables, and then work backwards to trace the source of those variables. Then, these steps need to be repeated until the developer can understand the behavior of the bug. Although it gets you the information you need to debug, It is a long and tedious process.
It’s important to note that utilizing this approach, if you have already deployed your application and you want to inspect more data, you’ll need to make a code change and redeploy your application after making those changes. In enterprise environments where you have CI/CD processes that execute including building and packaging the application, test case execution, approvals, deployments and more, this can oftentimes be a lengthy process with lots of context switching.
Method 2: Traditional Logging
The Python logging API is another approach that developers often use to debug Python applications. It is similar to a print statement, yet gives you more contextual information about the issue you’re facing and allows for configuration of logging threshold levels so that logs can be categorized based on severity. Logging is often used as a method in lieu of debugging tools.
Debugging using logging is often easier with Python, as it has extensive logging facilities and excellent documentation. It is well-standardized and simple, as Python has a powerful logging framework in its standard library. Additionally, logging can be better than using print statements as you can not only get the log record, but also access events automatically logged in included modules.
A nice feature of the Python logging module is the ability for applications to configure different log handlers so that captured log messages can be routed to those handlers. For example, you could create a file handler using the following code:
But, similar to using print statements, this approach requires the writing of more code and waiting for deployment cycles to complete, but achieves the goal of providing data and context you need to debug and understand what’s happening in your code while your application is running.
The Missing Piece
No matter the tool or the method you’re using to debug, all of the problems narrow down to one central cause: a lack of information about your code. But why is that? You wrote the code, so it should go without saying that you should know it best, right? And even if you didn’t, why is it so difficult to obtain that necessary data in order to reach the heart of your code and solve that pesky bug?
It all boils down to the missing ingredient: understandability. Understandability is the concept that a system should be presented so that an engineer can easily comprehend it. The more understandable a system is, the easier it will be for engineers to change it in a predictable and safe manner. In simpler terms? The better a developer can understand what their code is doing, the easier it will be to go in and debug it (of course regression tests can help here as well!). That is the missing key that we covered in not only print statements and logging but in all debugging tools.
The Right Choice
In order to make debugging efficient and effortless, you need a tool that will allow you to immediately get the data you need, from anywhere in your code, no matter where it’s running. And even more so, you need to be able to do so without breaking anything or redeploying your application. While the use of print statements and logs gets you information about what your code is doing during runtime, oftentimes you may not have the right logs or print statements in place. Or other times it may be too much information to sift through or just simply take too long to get a hold of.
Hours of developer’s workdays are spent attempting to extract the information they need to solve their bug. They run through endlessly long processes just to try and get one kernel of data, only to realize it was the wrong one, or completely irrelevant to what they needed, and that they have to go back to start and try again.
By employing a tool (such as Rookout!) that gives insight and understanding into every part of code, frustrating debugging times will be a feeling of the past. No more wasting time and resources. Go find the right tool that allows easy, effortless, and most importantly, happy debugging.