Delphi Programming

and software in general.

Tuesday, August 12, 2008

repeat Mistake until LessonLearned;

If we indeed do learn from our mistakes, I should have been a genius by now. Instead I feel silly for forgetting to do the most obvious checks and the most trivial safety measures. And I don't only do it once - I do it all the time!

Coding starts with an idea, some thinking about implications, hopefully some deliberation on requirements, possibly the choice of some pattern for implementation and maybe even some kind of plan for testing that it all work according to plan. Even if we put all of the above into action, we will most likely repeat some common coding mistakes.

"[A]nd then it occurred to me that a computer is a stupid machine with the ability to do incredibly smart things, while computer programmers are smart people with the ability to do incredibly stupid things. They are, in short, a perfect match."
~Bill Bryson

Here are some of my more typical causes of involuntary visits in the debugger.

I know that many of my mistakes stem from the brain being ahead of the fingers in the typing process. I have the idea all figured out in my head, and now I want to see it in action. In my desire to "save time" - I convince myself that I can add the checks later, but for now I can get away with putting up just the basic scaffolding and leave the safety rails for later. Naturally, when later comes - I am already on a different problem, taking the same shortcuts.

It boils down to bad habits.

Bart Roozendaal wrote about pre and post-conditions yesterday, and although these may save my bacon to a certain degree - it still boils down to the same issue: i.e. my failure to take time to actually implement the safety measures immediately, and instead postponing them until later.

So... what should I do to catch my most common mistakes? Pick up some good habits and stick with them. Time spent early will be saved ten-fold later.

Here are some suggestions:

• Forgetting to check for assignment (NIL pointer) and Invalid typecasts
Assert. Assert. Assert. Make sure that you qualify every reference before you use it.
In the context of Bart's post, maybe pre/post requirements would be even more useful if they would generate tips (lightweight hints?) at the location where the method (which have the requirements) is called.

• Dangling pointer (Non-NIL, but invalid)
FreeAndNil(ObjectRef); Just do it. Avoid passing and keeping non-volatile copies of the reference. If you really need the reference, implement some sort of common storage that encapsulate the object(s) you need to reference i.e. create a secure reference to a reference. Keep in mind that the such common storage should exist before and after the life of which ever object it references.

• Index/access out of string/array bound
Take a minute and check the boundary cases - what happens at the minimum and maximum index? Will index +/- length exceed the boundary?

• Numeric value or enumeration value out of range, Boolean evaluation mistake, Numeric evalution mistake (implicit cast accuracy problem)
Be explicit. Adding more brackets and explicit casts will not slow down your code. Break down the expression logic into smaller parts for clarity if you need to.

One book that is a veritable goldmine of healthy coding advice is Code Complete (2nd ed.) by Steve McConnell. The book's advice is generally language agnostic, and you will most likely recognize much of the advice given as common sense - but the real value is to use the book to remind yourself of using that common sense. If I had read this book as a student, I would have saved myself a lot of trouble.

If you don't wanna shop for books - do as Lars D and explain the code to your favorite Teddybear.

"What I mean is that if you really want to understand something, the best way is to try and explain it to someone else. That forces you to sort it out in your own mind. And the more slow and dim-witted your pupil, the more you have to break things down into more and more simple ideas. And that's really the essence of programming. By the time you've sorted out a complicated idea into little steps that even a stupid machine can deal with, you've certainly learned something about it yourself."
~Douglas Adams


  1. The poll above is missing an "All of the above" option - for me. Nice article with some great links. I have a rule of thumb that sets the target level when learning some new concepts/procedures ... "keep at, because you don't know it, until you can teach it".


  2. Wow, what a tough poll! I don't make any of those mistakes very often.... at least not any more!


    Bool evaluation is my weak spot when it comes to simplifying inverted conditions:

    (NOT a) AND (NOT b)

    which "naturally" looks like it could be simplified to:

    NOT (a AND b) // FAIL!

    but of course needs to be:

    NOT (a OR b)

    (I say "of course", but I had to check this with a little truth table, and even then I'm not 100% sure!)

    For the rest, I guess I've either learned good habits over the years (as far as these mistakes go anyway) or evolved defense mechanisms that are so ingrained and automatic that I don't have to think about them. Anymore.

    It maybe helps that Code Complete (1st Ed) was one of the first tech books I ever bought.

    And wholehearted agreement on the "you don't know something until you can teach it" sentiment!

    All developers should have a "Hobbes" on their desk, to learn from (and thereby to teach) their inner "Calvin".


  3. @TDelphiHobbyist: I had to avoid the "All of the above" option - or we would have a very boring result :)

    @Jolyon: I also had the 1st ed. until someone "borrowed" it. I guess "stealing is a form of flattery" is more than a metaphor :)