When you have eliminated the impossible, whatever remains, however improbable, must be the truth.-----Sherlock Holmes in "The Sign of the Four"
The primary difference between a programmer right out of college and one with five years' experience is the ability to debug programs. Debugging is still an art, not a science; but there are some techniques that can help. For more ideas on debugging, see The Elements of Programming Style by Kernighan and Plauger ( McGraw-Hill).
Begin finding bugs by reproducing the problem. Start by getting exact input if you can. Try reproducing the problem in your test environment with as little data as possible. Ask about all the conditions present when the problem occurred (for example, other users, batch jobs, etc.)
Parker Brothers produces a popular came called Clue. The objective of Clue is to deduce the solution to a crime by a process of elimination. With programs, you can do the same thing by doing numerous, carefully-selected test runs, each of which changes only one factor. From the differences in the results, you can often deduce which module the error is in and even which data structure is involved.
"LISTEN, KID, HOW MANY TIMES DO I HAVE TO EXPLAIN IT TO YOU...BUGS DON'T BECOME PROGRAMMERS."
Examine all the evidence. This includes the input data, output data, the source code, any library routines, and anything else that might help, even the documentation. Check your premises before you invest too many hours.
On MPE ask yourself if you have any :File equations or temporary files. Are you using a Run-time library and is it the correct one? Use the System Debugger to set breakpoints at key routines to see where they are called from? Use Display Functions mode to see what Escape sequences you are actually sending to your terminal.
Keep an open mind. If you think you've identified the section of the program that contains the bug, but there appears to be nothing wrong with it, look somewhere else. Showing the code to another person can highlight the problems with those pieces of code "that couldn't have a bug". If you have a really bad bug, leave it overnight. Often, the answer will be sitting there in the morning.
Include code in your subroutines to check the parameters that are passed into it. Do not assume that the caller, who may simply be yourself on a bad day, follows the rules or has the latest manual. Include assertions in your program, even if they are just comments. An assertion is something that must be true for the next piece of code to work: "when this section is performed, the variable MCU-CUST-NUMBER must contain the correct customer number." Monitor the program during execution, checking the contents of key variables. Can you explain the values they contain?
Use structured programming to write your code. If you work on another person's code, try to find the assertions. These usually lead the way to understanding and debugging the code. Rewrite bad code, and write and test code in small pieces.
Errors are most frequent on boundary conditions. For example, beginning of file, end of file, empty file, full file, beginning of loop, end of loop, entry to module, exit from module, value less than limit (instead of less than or equal), table overflow, or table empty. When verifying code, check that the boundary conditions are what you expect.
Here are some suggestions from Elements of Programming Style:
More ideas on software quality and productivity.