Print Statements or a Debugger? Choosing the Right Tool

print debugging vs debugger

Stuck on a bug and unsure whether to add prints or fire up the debugger? In the print debugging vs debugger choice, use prints for quick checks and logs, and a debugger when you need to pause, inspect state and step through.

This guide is one part of the wider topic of debugging techniques.

Item Value
Print statements in compiled code Require recompiling the program
Output order in multi-threaded programs Likely out of order with print statements
TotalView eval point that stops a loop Every 100 iterations
VS Code: start debugging the current file F5
VS Code: step over to the next line F10

These figures come from the TotalView user guide and the TU Delft VS Code debugging chapter. Tool documentation changes between versions, so check the source before you rely on them.

What print debugging does well

Print debugging means adding output statements to your code and reading what they report. A print statement works in almost any language, needs no setup and answers one narrow question fast. That is why it stays popular even among people who own better tools.

Brian Kernighan is quoted in a Substack essay as saying, “The most effective debugging tool is still careful thought, coupled with judiciously placed print statements.” The essay’s author answers “No, I don’t think so” and argues that debugging tools can do better. Hold both views in mind.

Prints also work where a debugger can’t follow. One commenter on a Hackaday post notes that sometimes you don’t have access to a debugger, but almost every program has some sort of I/O that can show status.

Logging matters most for bugs on machines you can’t touch. Another commenter on the same Hackaday post says nothing beats good logging for problems somebody else has with your software, because you can’t connect a debugger to their computer.

Prints or logs are usually the better first move when:

  • You want to check one value in a small script.
  • No debugger is available, or it is awkward to set up.
  • The failure happens on someone else’s machine, and logs are your only record.

What a debugger can do that prints can’t

A debugger pauses a running program so you can look around, instead of guessing in advance what to print. A Hackaday post from September 11, 2025 lists what that buys you.

  • Catching exceptions when they occur, without a print for each one.
  • Walking the call stack to see the flow of code before the stop.
  • Evaluating expressions on the fly.
  • Altering program flow, such as jumping over some code.
  • Stepping backwards, although not all debuggers can do that.

Debuggers also keep your source cleaner. A TU Delft chapter points out that print debugging means changing your code to add test prints and altering it again to remove them. Its VS Code approach does not alter your code with print statements.

Python already ships with one. The Educative guide to the Python debugger says the pdb module is a standard debugger that comes with all versions of Python.

Where print debugging breaks down

Prints get expensive as programs grow. The Educative guide calls placing print() calls after every variable inefficient and says it can get unmanageable with larger bodies of code.

Compiled and concurrent code add more trouble. TotalView’s documentation says print statements require that you recompile your program. It adds that output may be hard to follow because it is likely to be out of order in multi-process, multi-threaded programs.

Debuggers are not free of side effects either. The TU Delft chapter warns that some bugs are sensitive to timing or system resources, and that the debugging process itself might interfere with the bug’s occurrence. A Hackaday commenter makes the same point about real-time and multithreaded applications, where breakpoints can mess up timing.

print debugging vs debugger Laptop keyboard
A silver laptop keyboard fills the foreground beneath a dark editor screen.

Can you get both in one step?

Often you can, because several tools let you attach output to a breakpoint. You get the convenience of a print without editing the code.

  • TotalView: a breakpoint with associated code is an eval point, and the program keeps running if you didn’t tell it to stop.
  • IntelliJ: one Hackaday commenter likes non-suspending breakpoints that log that the breakpoint was reached or generate a custom message.
  • VS Code: the TU Delft chapter lists conditional breakpoints and logpoints among advanced breakpoint features.
  • Plain code: another commenter on the same post notes you can put the printf in an if statement so it fires only on the failing case.

TotalView’s guide shows an eval point that stops a loop every 100 iterations and prints the value of i. That turns a flood of output into one useful pause.

To practise pausing code on purpose, read up on how to use breakpoints.

How to choose for the bug in front of you

Start with the bug itself. The TU Delft chapter calls determining whether you can reproduce the bug consistently the first and most important step in debugging. Learn how to reproduce a bug before you pick a tool.

Then work down this list:

  1. If the bug only shows up on a machine you can’t reach, add logging.
  2. If you suspect a single value, print it with a clear label.
  3. If you need the call stack, or want to try expressions on live data, use a debugger.
  4. If stopping the program would change its timing, use logs or non-suspending breakpoints.
  5. If you keep adding prints in a loop, switch to a conditional breakpoint.

Habits that make either tool work better

The tool matters less than the routine around it. The Educative guide recommends a structured workflow: reproduce the bug with a minimal case, set breakpoints at state-changing lines, explore the call stack, clean up debug hooks before committing, and turn fixes into automated tests.

  • Label every print with where it sits, so the output explains itself.
  • Guard noisy prints with a condition so they fire only on the case you care about.
  • Remove temporary prints before you commit.
  • Write a test once you find the cause, so the bug stays fixed.

Treat each print or breakpoint as a question with an expected answer. The TU Delft chapter says that if a variable holds an unexpected value, that’s often where the problem lies, so compare expectation with reality and move your next check closer to the first mismatch. Whether you use a print or a pause, the habit is the same.

Frequently asked questions

Is print debugging a bad habit?

Show answer

No. Kernighan’s line about careful thought and well-placed prints, quoted in the Substack essay, shows how respected it is. It becomes a problem at scale, when prints after every variable turn unmanageable.

Do print statements always need a recompile?

Show answer

In compiled programs, yes. TotalView’s guide says you can get the same output without recompiling by adding a breakpoint that prints information.

Which is better for multi-threaded code?

Show answer

Neither wins outright. Print output can arrive out of order, and a debugger can disturb timing, so labelled logs or non-suspending breakpoints are a sensible middle path.

How do I start with a debugger in Python or VS Code?

Show answer

Python includes pdb, per the Educative guide. In VS Code, the TU Delft chapter says to press F5 to start debugging, F10 to step over a line and F11 to step into a function call.