Some great responses so far and all great methods many of us use or have used when needed. However, debugging goes way beyond writing code. When coding, set yourself up for success by doing the proper planning of your architecture BEFORE you start coding. Pseudo code your logic out on paper or in a the editor. Make sure your code is modular, flows logically, and clear enough so when (not if) you have to come back and read it it is easy to follow the flow and understand. If you can do that you are halfway there!
Next make sure you have a clear understanding of testing methodologies and tools for your language and utilize them. The main thing Ruby has taught me over the years is test test test!
Also, learn your debugger! EVERY modern language has one. To most developers the debugger is an after-though. Take the time to learn it inside and out so you can utilize it to it's fullest.
Learn the other tools of your language. Many language have supplemental tools that let you do things like view/monitor memory or call stacks outside the debugger. Learn and use those tools!
Finally, my personal process for debugging is as follows. First I take a hard look at the error stack to get a clue of the flow to that bit of code that blew up. Usually these are logic errors as my tests usually catch the obvious stuff. So I step through the code with the debugger watching all the variables I need to watch and that usually gives me a clue where to look. At that point it's just a matter of getting your hands dirty digging into the code.