There's no shame in it but that doesn't mean that it's the best or most efficient way of doing things.
I've found that stepping through code in a debugger at a human pace, and getting to really understand what's happening when a bug occurs is invaluable.
One problem with this, is that your code often ends up in a state where only step-through debugging works anymore. It might become too complex to reason about just looking at the code, from the types alone, or by printing data.
Same problem happens with other methods too: If you develop solely with unit and integration tests, it might actually be quite difficult to get a step-through debugger set up to debug your application. I worked at a company where I was the only one who used a step-through debugger, and some uses of compile-time metaprogramming would frequently break the debugger.
And if you use multiple techniques as appropriate(unit tests, printf or equivalent, step-through debugging), it's generally easy to use any of them as appropriate.
Like every other tool in programming, different people have different preferences and experiences. Symbolic debuggers have always ended up being a waste of my time, but I don't try to extrapolate from there to everyone else's preferences.
With prints and a backtrace function, you can make up for a most debugger usage. But for tracking down heap corruption, a debugger with memory breakpoints is the bees knees.
If Anders Hejlsberg does it, there's no shame in it.