• When manual reference counting and you must replace an object with a new one always ref the new object before unrefing the old one, otherwise you risk deleteing the object prematurely if the object is replaced by itself. This bug can be very hard to find since the memory is reused in the same context immediatly so memory cooruption errors will probably not occur for quite a while. If you explicitly disallow replacement by the same object for some reason ALWAYS include an 'assert(old_object != new_object)'.
  • Use asserts VERY liberally, when writing a function the first thing you should think of is, 'what assumptions is this function making about its arguments and the envirnment?' and then 'assert' that these are true. but be careful, asserts used properly should only induce at most a constant slowdown of code, only assert local information and envirnment. ie, dont have code that works on binary trees do an 'assert(traverse whole binary tree and make sure is sane)' on entry and exit to every function call or your nice logarithmic algorithms will turn into exponential ones, which does more than just slow down your program it actually radically modifies the relative speed of components of your system which can only serve to mislead you as to where one must optimize and useability analysis. a program running under normal debugging should run in a similar fashion to the optimized version just uniformly slower. Just remember if every part of a data structure that a function touches is sane then the whole structure is sane. Of course this rule can be broken temporarily when tracking down a particulary nasty bug.
  • multithreaded code is almost always more difficult but not impossibly so as some might lead you to believe. Just remember that multithreaded interaction is by nature non-deterministic, trial-and-error coding does not cut it even for languages with advanced type systems. since every run of the code can be different and many possible runs of the code will not even happen on your particular machine you can never exaustivly test all input cases to your code. One just needs to be able to logically reason about thread interactions less memory cooruption and/or deadlock will result. if more than one thread ever use the same data structure it must somehow be protected by a mutex. if you ever find yourself needing to lock more than one mutex at once ask if there is ever any other thread that will lock them in the opposite order, if so then you have a possible deadlock. As long as you keep things like this in mind whenever you write multithreaded code it should be safe, of course there are many other things to take into account but as long as you have a model in your mind of how any two threads will interact and how they will not step all over each other then you are on the right track.
  • learn to use the basics of a debugger, just enough to get a stack backtrace from a core file. a debugger will tell you exactly what line of code your program core dumped on and show you all calls leading up to that dump. using gdb once you have a dump (say from a segfault) type 'gdb <programname> core' and then at the prompt 'bt' (for backtrace).