Writing Non-Fragile Code

Oooops…. who broke it?

“Fragile” code is code that breaks in one place because of changes you make in some other place. It’s most aggravating when you’re due to ship a new version tomorrow and you need to make one last tweak at 11:30 PM, or your client is looking over your shoulder and this little “harmless” change shows up as a smoldering heap during the demo.

In this case, “break” doesn’t ONLY mean “broken arrow” , or uncompilable code (at least you can chase those down easily enough). Here, “break” also means “operates incorrectly” or “completely wrecks itself like it never did before” or somewhere in between.

These sorts of breaks come from unrecognized dependencies, and they’re all too easy to make: the header size has been 3 for months and months now, so when you add a new function that needs it, it’s easy to stick in a constant 3 and be done with it.

DON’T DO IT.

If you’re an old-hand bit-banging cycle counter like me, it’s easy to think of saving a few cycles and adding up the bytes in this cluster, and using a constant of 53 when you need the size of it.

DON’T DO IT.

The problem is, or course, that when (not if, but when) these things change, then you will have to track down ALL the instances where you use this number and change them.  Not a good plan.  The compiler won’t complain – the code is still valid.  But reading three bytes when you should be reading four will not get you where you want to go.

One tip to solving this is to reduce the number of places that you use such numbers. Focus such procedures into a single VI if you can. But the real key to solving this is to recognize these things when you originate them.

Example #1

Here we need to receive a packet header, consisting of a cluster of a U8 enum command and a U16 integer.  It’s easy enough to count up to three bytes, and it would be easy to plop down a 3 constant.  However, it is safer to use a constant of the header’s typedef and calculate its size in code.  This might go against your instincts (it does mine), but in fact the extra time taken (to flatten into string and get string length) is trivial (measure it yourself if you have doubts). On top of a TCP READ operation, this burden is truly insignificant.

And the benefit is that when the integer needs to become an U32 or the command must become a U16, here’s one less thing YOU have to worry about.  Since the constant here is a TYPEDEF, and since you’re calculating the size every time, then it will keep on working.

Fragile1

Example #2

This example is similar – sending a packet header plus a payload thru a connection. Even if your payload is always the same size, it’s better to calculate it than to use a constant.  With any luck at all, the STRING LENGTH operation will get the same answer every time. and if you do change it at some point, then this code won’t break.

Fragile2
Example #3
Here’s another use.  When you have an ENUM, it might be useful to loop over every value. But how do you know how many values to use?  You could count them and plop down a constant. That’s no good, because it leaves you vulnerable.  You know the first value has a numeric equivalent of 0, you could add a value called “Last”, but that’s ugly if the enum is a control onscreen somewhere.
My answer is to cast a large U16 (or whatever data type the ENUM is) into that type (and then possibly back to an integer if needed).
Here, the FOLDER is an enum listing the various folders my program can refer to. I don’t know how many there are, maybe 25-30 (in this case, ignorance really is bliss), because I don’t care.
This code is responsible for creating them all (with exceptions) at startup time.
First we start with a large U16.  I picked U16 to match the representation of the enum itself; that’s necessary for proper casting results.
Then we typecast it to the folder type.  Presumably, 9999 is more than the number of entries in the ENUM, so the typecasting process can’t let a literal cast stand, as it would be an illegal value.  So what comes out is the last possible value.
Inside the loop, we convert the “i” variable to a U16 to match the representation of the enum itself.
Then we typecast that value into the folder type.
The result is a variable that cycles from first ENUM value to the last ENUM value, and does something with each value, all without knowing how many there are!
If we add a new folder type to the ENUM (it is a typedef), then this code does not need changing.
Fragile3
I hope that this thought will help you create code that is more robust.  Your clients will love you for it.
Enjoy.

4 Responses to “Writing Non-Fragile Code”

  1. Bob Seegmiller says:

    Nice. I’d just noticed the “constraining” that occurs when type-casting a number to an enum and that it doesn’t “go off the end”.

    Thanks

  2. Steve says:

    Yeah, there’s really no other way it could work. If it’s a variable of your ENUM type, then it has to be a LEGAL value. Use that fact to your advantage.

  3. Bob Schor says:

    A much better solution is to use the fact that incrementing an Enum will cause it to “wrap around”. So wire the Enum into a loop’s Shift register, and wire a copy into a tunnel (which will then hold the initial value). Inside the loop, use the value in the Shift register (current value), increment it (next value), and wire the Next Value and Initial Value (from the tunnel) to an “Is Equal” comparison, wired to the Stop control of the While loop.

    The loop will execute once for every value of the Enum, even if there are more than 9999 values! No “magic” required.

  4. Steve says:

    Excellent, Bob!. That’s the same idea, but even MORE bulletproof!

Leave a Reply



Testimonial  Contact Info

207-593-8109

Culverson Software

184 Lakeview Drive

Rockland, ME 04841

General questions:

Sales@Culverson.com

Accounts/billing questions:

Accounts@Culverson.com


Logo