Another finding-my-thoughts posting. Documenting helps fix bugs early, but what are its companion techniques?
Polishing the code all the time. Make it look good. Beauty is truth, truth is beauty. I firmly believe that good-looking code works well. Christopher Seiwald wrote a fairly good essay on what constitutes pretty code.
I can't really say what quality this is, but often Abhijit and I can look at a class and predict whether it'll attract bug reports or not. I can't describe what I see, Abhijit can't describe what he sees, but we invariably agree, and we're usually right.
Counting cases, enumerating the possible paths through a function. A small number is good. If the number is high, or difficult to determine, then it's good to eliminate eliminate some paths. Some of the many paths are sure to be badly-considered and buggy.
Code review always helps. Pair programming is IMO just code review while typing.
Single-stepping just for the sake of it, which to my mind is not very different from a computer-aided code review. Rubber duck debugging is a static variation of the same theme. Single-stepping is in execution order, rubber duck debugging is in reading order.
Never write unused code: Code to be used
later is code to solve a badly-understood problem. Begging for bugs.
Writing unit tests is another way to count cases.
write unit test
until tired of all the cases
Then either stop testing the code, or find a way to simplify the function. What I don't like about this is that there's no mechanical help. The compiler doesn't mention that a code path is untested.
Static analysis, including compiler warnings. Particularly computer warnings.
All of the above really are: Getting a second look. The compiler's, another developer's, a rubber duck's. Or a second kind of look.