All the cool kids use debuggers for debugging their code. In most situations that’s the right answer. After all, you can set breakpoints, step through your code, and examine values at each step. If you want a detailed view into what your code is doing, debuggers are the best solution.
Sometimes, though, they’re a bit heavy weight and way more than you need. Often all you need is a few strategically placed printfs to get the necessary information to solve the problem. Sometimes, for various reasons, you can’t use a debugger so you’re forced to fall back on printf.
Marcin Borkowski (mbork) has a nice example of the latter. He was debugging an Elisp function that messed with windows and that interfered with Edebug’s use of those windows. The answer, of course, was to fall back to the printf method. The natural solution was to use the Elisp message function but there were some problems. The first problem was easy. The messages appeared in the minibuffer and disappeared before he could read them. He fixed that by wrapping message in a function that waited for a key press before continuing.
Then he noticed that virtually all his invocations were of the form (message "var1: %s, var2: %s" var1 var2), and that it made sense to abstract that a bit into a new function. That brought a couple of new problems but they were also easily dealt with.
Mbork’s post is well worth reading and his code is worth a bit of study too. I like the way he just digs in and solves the problems without too much worry about whether or not it’s the best way. At the end of the day, he solved his problem and that’s what matters.