Some practical suggestions
Following are some practical suggestions (given in no particular order) that you should keep in mind while developing your code.
Top-down programming
Always start your design with a high-level, conceptual statement of the solution and then work towards a specific implementation of the solution.
Always try to decompose a large, difficult problem into several smaller, easier problems.
Use many small functions, rather than a few large ones.
This lowers the complexity and helps in verifying correctness. Also, it encourages resuse, lowering overall code size.
As a general rule, whenever the length of a function exceeds one page, think about dividing it into its component parts.
Except in cases where performance is paramount, try to generalize.
Don't write separate functions to evalute 2D quadratic B-splines, 3D cubic B-splines, etc. Write a single routine for arbitrary degree, n-dimensional B-splines.
Follow a programming style: indentation, naming, comments, etc.
The specifics of the style are unimportant. What is important is that you follow them rigorously. This will aid in comprehension of the program by others, and by yourself, when you return to it after a period of time.
Document your code as you write it.
Everyone hates to write documentation. However, your understanding of the code is never greater than at the moment when it is being written. Try to add a few comments describing the use of the function, the meaning of the major variables, the overall strategy of an algorithm, etc.
Eventually, someone else will have to look at, and understand, your code. Also, you will need to recall the specifics of the code in the future; your memory will not be sufficient.
Use descriptive function and variable names.
This helps your code become somewhat self-documenting.
Avoid clever tricks based on peculiarities of the hardware.
Any assumptions that you make about word size, byte ordering, etc., will be wrong eventually.
Avoid magic numbers.
Put macro definitions (#define) in a header file.
Avoid using global variables
Pass data through argument lists. This encourages modularity and code re-use, and minimizes unforeseen side-effects.
To avoid excessively long argument lists, encapsulate related variables into a structure.
Debugging
Try to isolate the bug: the subdivision, or "Lion in Africa" method.
Find the most tightly constrained set of conditions that exhibit the bug, e.g. input variables, loop iterations, etc. Examine the intermediate steps in the vicinity of the bug: Where was the program last correct? Where is it first wrong?
Test and debug as you go.
Don't wait until you have made ten changes to discover an error. Make the first change and test and correct; only then, make the second change and test, and so on.
When you have a version that works, make a copy and save it.
There are many other practical hints, most of which are based on common sense or the general rules for good design applicable across domains. Approach software development as you would any other engineering problem.
Back to review of basic principles...
Back to the 13.016 overview...