I'm at the stage where I can build small programs, but I still can't always tell what went wrong just by reading the code. My usual approach is to add print statements throughout the program to check whether variables contain the values I expect at each line. It works, but the output quickly becomes difficult to manage, and removing all the temporary prints takes time. Is there a beginner-friendly way to inspect how variables change and identify which line caused a problem? I mainly use Python with VS Code, and I also work with C++ and Bash.
4 Answers
Print statements are not wrong, especially while you’re learning. They can help you understand how data flows through a program. For quick checks, I still use them. For anything more involved, though, a debugger is usually cleaner because the values and execution path are shown directly instead of being mixed into a long stream of output.
A logging library is a useful middle ground. Instead of calling print everywhere, use messages at levels such as debug, info, warning, and error. You can enable verbose debug output while investigating a problem and turn it off without deleting the messages. This is especially helpful for longer-running programs or deployed applications. A simple debug flag or a command-line option like --verbose can provide a similar setup for smaller projects.
The main tool for this is a debugger. Set a breakpoint where you suspect the problem is, run the program in debug mode, and step through it one line at a time. You can inspect local variables, view the call stack, step over or into functions, and sometimes evaluate expressions while the program is paused. VS Code has debugger support for Python and several other languages, although you may need to install the appropriate language extension.
Use small assertions and tests alongside the debugger. If a function should always return a particular kind of value or satisfy a condition, an assertion can stop the program close to the actual mistake. A focused test can also tell you which function is failing before you start stepping through dozens of lines. Breakpoints do need to be placed manually, but you normally only keep them around while investigating; they don’t become part of the program or its output.

That makes sense. I was worried that using prints meant I was debugging the wrong way, but they’re probably still useful for simple checks while I learn the debugger.