Simple Rules…

There’s only a couple of things that I stick to when it comes to writing code.  I don’t keep a long list of todos as each situation seems to require a different way of doing things.  But, these things are fundamental and easy to keep in the back of your mind as you hack away at an implementation.

First,

Write the least dangerous code possible.

There is always several ways to attack a problem.  Some are sure to be more maintainable, easier to work with down the line.  Dangerous code is the solutions that result in code that can easily cause problems as soon as someone not familiar with the code attempts to edit it, or you attempt to edit it yourself in the not-so-distant future.

You know some of the code I’m talking about.  The type of code that as soon as you edit it, something completely unrelated breaks in the system.  This type of code is referred to in the Pragmatic Programmer as being highly coupled.

Coupling is dangerous and it can be tough to prevent or even realize that you are doing it.  It’s just something that you should be aware of as you program.  You should be asking yourself how is this going to become entangled in the rest of the app if I do it this way.  How can I do it so it won’t ( I avoid using the word encapsulate here to avoid making it seem like I’m talking about object oriented programming as I don’t believe OOP is the right solution for every problem, even with coupling problems)?

Beyond a topic as big and complicated as coupling there are other ways of coding dangerously.  For instance, case fall-through.  I’m not talking about case stacking, which is case fall-through in which no code is in between the falling case statements.  The cases are just stacked on top of each other as they all share the same functionality.  The case fall-through I’m referring to is when you have one case that does something and then falls into the case below it to do another part of needed functionality.  Now this is dangerous code.  Things can go wrong.  For one, someone might see this and think it is a bug.  Case statements end with breaks, this needs a break, the code is broken that easily.  Another is that someone adding a new case might not see that a case falls into another case and proceed to add a new case in between the two cases (that’s a lot of cases in one sentence).  Another easy break, no pun intended.  The thing about this is that coding this way is easily avoided by breaking common functionality out into functions.  It actually makes the switch statement easier to read, there’s more benefits by avoiding case fall-through.  Just don’t do it.

There’s many more dangers, including just difficult approaches where an easier implementation would have sufficed.  Avoid them all if possible, if realized.

Next,

Write code that is easy as possible to debug.

Which is easier to debug. This:

    public int getSomething(){
        return (calculateThis(getValueOne(), getValueTwo(),
                calculateSomethingElse()));
    }

Or, this:

    public int getSomething() {
        int value1 = getValueOne();
        int value2 = getValueTwo();
        int value3 = calculateSomethingElse();
        return calculateThis(value1, value2, value3);
    }

The latter is of course, but why?  Basically I’m talking about debugging with break points.  As you step through your code with a debugger, the first one makes it very very painful.  Say you wanted to step into calculateThis(), in the first example you would first have to step through the other three functions to get to it.  In the second example you could just step over the first three functions and ignore what they do.  It cuts down on the number of keys presses needed and the amount of code that needs to be viewed.  It just makes your life a little bit simpler.

The only thing that trumps both of these things is optimization.  I’ve worked on embedded software for nearly 5 years now and I know you are going to run across instances when writing difficult code can result in big gains on the memory or speed front. Here’s a fun one, not too bad. Instead of:

    int[] _iaX = new int[numRect];
    int[] _iaY = new int[numRect];
    int[] _iaW = new int[numRect];
    int[] _iaH = new int[numRect];
    // …
    for(int i = 0; i < 10; i++){
        _iaX[i] = 0;
        _iaY[i] = 0;
        _iaW[i] = _screenW;
        _iaH[i] = _screenH;
    }

You can do:

    final static int X_INDEX = 0;
    final static int Y_INDEX = 1;
    final static int W_INDEX = 2;
    final static int H_INDEX = 3;
 
    int[] _iaRect = new int[numRect * 4];
    // …
    int offset = 0;
    for(int i = 0; i < 10; i++){
        offset = (i * 4);
        _iaRect[ offset + X_INDEX ] = 0;
        _iaRect[ offset + Y_INDEX ] = 0;
        _iaRect[ offset + W_INDEX ] = screenW;
        _iaRect[ offset + H_INDEX ] = screenH;
    }

Obviously this is not as nice as the former, but it is really good on memory.  Each array has several bytes of overhead when initialized.  So it saves a bunch if you have a shit load of rectangles in your apps.  But, I tried to make the implementation as readable as possible and hid the implementation from others, hoping no one else will have to deal with it, only reap the benefits.

Leave a comment