The big thing here is that you seem to only work with your own code, where you can arbitrary refactor it and keep the entire thing in your head, as well as quickly find which module does what. But when working with a large foreign project, none of this works. You have to start working at the scope of the entire program, because you have no idea of the internal structure yet. Of course, people who use debuggers divide the code up as they go, but the point here is that they place a few choice breakpoints at central points in the application logic, inspect the stacktraces when one gets hit, and use them to further dig in to the part of the code they need to look at.
Not at all. Due to lack of documentation I look at code of foreign libraries and applications all the time to check what really happens inside and what are the guaranties. The latter being often the case when it comes to concurrency problems.
The vast majority of code I investigate is "someone else's" code. Most of the cases, it's a historical accumulation by multiple authors. If you generally only work in your own code, that's quite a different experience, and debugging is generally easier (because you were there when it was written).
The article hits the point of print debugging, you get to see the backward in time.
By the time you hit "the problem", the pointer is NULL, the memory is trashed, the system is deadlocked, etc. You need to reason about how you got there.
There is a reason why the next step up from basic debugging embedded is "streaming trace"--effectively print on steroids.
I just told one of my co-workers last week that I was going to print-debug an issue. He paused for a moment before saying, "Uh, I can just debug this for you if you like."
So yeah, there's definitely some kind of stigma against print-debugging.
For over ten years of commercial work I used a debugger only a couple of times and in most cases it was against someone else's code, usually when things were completely broken and I needed to get backtraces from multiple deadlocked threads or lacked debugging symbols and things like radare were also required. There were also times when I manually called a syscall using gdb.
My opinion is that if you can't reason about the code helping yourself with just a couple of additional messages the code is probably broken/too complicated to begin with and requires serious refactoring. I've never understood people stepping through a program hoping to find some mysterious creature somewhere along a huge stack of calls. In my career I have often seen people always debugging an application as a whole instead of separated modules. Dividing a problem is the key. The same key that allows me to still program using vim without autocompletion, keep APIs sane and coherent, and avoid dead code.
One really useful exception is when dealing with electronics. My friends programming hardware use debuggers all the time and in this case it actually makes perfect sense because there is no way to print anything and things like hardware interrupts come into play.