The most basic debugging tool is a print statement. It could be print
function, echo
, console.log
, or alert
. Principle is the same: you display (partial) state of the program on the screen, check what stands out, and try to diagnose the issue. “It seems the loop is running more than it should. Let’s see what’s the value of i
here”.
No manual is needed to teach it. The idea just comes naturally to programmers.
It works wonderfully for a while. But as programmers get more experienced, they discover better tools called debuggers, which allow programmers to add breakpoints and watchers. With breakpoints, the programmer can pause the execution of program at a specific point and inspect the state with a greater flexibility. In case of watchers, they can keep an eye on the state of a variable.
I have used these tools, too. Over the years, however, I have realized that except for very specific situations, “Print” just works better.
- Print is universal: Every language has it. After all, putting something on screen is a pretty fundamental feature of any language. Debuggers can vary across different languages/platforms.
- Print doesn’t need specialized tools:
print
is part of the code. Setting up debugger can often be a hassle, since the debugger and the program are usually different. Sometimes, the integration is seamless, like Chrome DevTools, but with something like node.js, it’s not as easy to make the setup work. - Print doesn’t tie you to a particular tool: Let’s say you’ve the setup working in VS Code. What if you want to switch your editor? You’ll have to figure out to make it work in that editor too. Print, on the other hand, will work everywhere.
- Print is faster to use: Compared to creating breakpoints, or watchers, and removing them after use, print is definitely easier. Sure breakpoints give you more flexibility in diagnosing the issue, but often most issues aren’t that complicated to warrant them.
- Print works on Staging and Production: It’s not easy to work with debug tools when you’re dealing with code running on the server. On the other hand, print statements work there too. Even if you don’t have access to STDOUT, you can write statements to a file or send them to a logging server/service (e.g, Loggly)
- Minifying/Compiling code doesn’t break print: If you’re compiling/minified your code, there’s extra complexity in mapping original source do to the compiled one. Let’s say you encounter an issue in the final build. With minified variable and function names, it would be exhausting to find the root cause. However, if your original source includes a print statement, it would be compiled as well and you can print the variables you’re interested in.
Print statements are powerful, versatile and flexible. They are rudimentary but every time I have tried to upgrade myself to supposedly better tools, I have come back to printing variables. They are “it just works” version of debugging.
Debuggers have their place. They are useful in places where you don’t have the liberty to modify the code or you want to analyse the execution stack better. But in day-to-day work, you’ll find print to be a better solution.