"It seems strange that I never learnt in any structured way how to debug a program. Everything I know about debugging has been acquired through experience, trial and error, and from watching others. Unless my experience is unique, it seems that debugging techniques should be a topic of vital interest to every developer."
List of techniques:
* Progressively Narrow Scope
* Change Only One Thing At A Time
* Insert Trace Statements
* Search The Web For The Stack Trace
* Insert assertions
* Read The Documentation
* Polish documentation: If you debug/refactor an algorithm and its documentation is missing/insufficient, try documenting it. This will enable you to think more clearly. Example: If you try to debug the collision detection algorithm, start by documenting (comments, a word document with illustrations etc.)
* Recompile And Relink
* Probe Boundary Conditions And Special Cases
* Check Code That Has Changed Recently
* Don't Trust The Error Message
* Explain/discuss the bug to a colleague
* Don't Be Too Quick To Blame The Tools
* Understand Both The Problem And The Solution
* Take A Break
* Introduce Debugging Aids Early (logging, unit test etc.)
* Loose Coupling And Information Hiding
* Write A Regression Test To Prevent Reoccurrence
* For long debugging sessions (>1 week) keep a written history of the things you tried and the results you got so that you don't repeat yourself and loose time
* While in the process of debugging, write down what you are doing and plan to do (debug diary). This has a similar effect of talking about the problem with someone else; it helps to think more clearly and not get overwhelmed/depressed. Additionally, during debug sessions spanning more than one day, you will not forget what you did (and not waste time re-doing things).
* In code there should be special comments for lines that show the main flow. In that way it will be easier to follow the main flow amidst all the auxilliary data reading, comments, error handling, resetting etc. Example using --!!! MAIN FLOW comment to indicate important functions. [Code Complete, Steve McConnel, 1993, p.40]:
"...as much as 90 percent of a program's code is written for exceptional, error processing cases or housekeeping, implying that only 10 percent is written for nominal cases"
* When coding a complex model, first start coding the simplest configuration by making assumptions (const g, no drag, QE=0), do some tests, code them as unit tests or at least jot down assumptions and inputs/outputs. Then one by one remove the simplifying assumptions, update the design to accomadate new functionality, check if the old tests still pass and add new tests. That way you will not be overwhelmed by complexity and you will always have a running program.