Forgot your password?
typodupeerror
Programming IT Technology

How to Keep Your Code From Destroying You 486

Posted by ScuttleMonkey
from the i-have-been-sent-here-to-destroy-you dept.
An anonymous reader writes "IBM DeveloperWorks has a few quick tips on how to write maintainable code that won't leech your most valuable resource — time. These six tips on how to write maintainable code are guaranteed to save you time and frustration: one minute spent writing comments can save you an hour of anguish. Bad code gets written all the time. But it doesn't have to be that way. Its time to ask yourself if its time for you to convert to the clean code religion."
This discussion has been archived. No new comments can be posted.

How to Keep Your Code From Destroying You

Comments Filter:
  • by Sax Maniac (88550) on Wednesday May 30, 2007 @03:31PM (#19326029) Homepage Journal
    Right, I laughed at that #define remark, it's so green.

    The real thing is to used named constants where it makes sense. #define is the crudest approximation of that, C can use enums, C++ can use "const" for compile-time values, etc.

    In a real project, you have to scope your constants otherwise you'll have a billion of them in "everything.h" and every time you touch it, the world will rebuild. So nix the "centrally located" file theory.

    In a real project, your constants will often have interdepedencies on other bits of code, so changing one will frequently affect the others. Heck, maybe changing one will cause it not to compile. This example makes them all trivial to the point of uselessness. Shuttling it off miles away in an #include file, can frequently give the impression this than can be changed with no effect on anything else.

  • by Enselic (933809) on Wednesday May 30, 2007 @03:36PM (#19326121) Homepage
    I wonder what mushrooms he were on when he came up with that coding style... (yes, this is the actual indentation he used):

    Void change_score(short num_points)
    {
        if (num_points < 0)
    {
    // maybe some error message
            return;
    }

        score += num_points;

        if (num_points > 0)
    make_sparkles_on_score();
    }
  • by Coryoth (254751) on Wednesday May 30, 2007 @03:43PM (#19326251) Homepage Journal

    1.) Use test driven development
    I'll go you one better. Use specification driven development. That is, use a combination of contracts an unit tests. If your method has general constraints, or your object has an invariant, write it into the code using contracts (and ideally use a system that will let your subclasses inherit contracts, and allow contracts to be sucked up and included in the API documentation); if your methods specification is only easily expressed as a set of mappings from input to expected output, write a unit test instead. When you run your unit tests the contracts will automatically get tested too. Better yet, by using contracts you can help yourself on:

    2.) Write complete unit tests, including bad input
    by using the contracts as a test oracle and passing in randomly generated data to really flesh out the corner cases. In some cases you can do this in a purely automated fashion [inf.ethz.ch] at the push of a button. Contracts also have the benefit of: (1) not requiring the biolerplate code of unit tests, so they're faster to write; (2) respecting inheritance which can save you a lot of extra test writing. You can't always easily write contracts for methods, and in those cases unit tests make sense, but you may as well take full advantage of contracts for the parts that can be handled in that manner.
  • by Anonymous Coward on Wednesday May 30, 2007 @03:52PM (#19326409)

    About the author Jeff Vogel Since 1994, Jeff Vogel has run Spiderweb Software. He has written a dozen or so fantasy role-playing games for Windows and Macintosh, including the award-winning Exile, Avernum, and Geneforge series. He is the author of The Poo Bomb: True Tales of Parental Terror and other humorous writing. He lives in Seattle.
    sorry i can't get information about any special power
  • by hobo sapiens (893427) <STRAW minus berry> on Wednesday May 30, 2007 @04:14PM (#19326725) Journal
    Agreed, especially considering that this article was not so much about commenting as it was about writing self-documenting code. To leave indentation out of that discussion is to be quite remiss.

    On the other hand, I work with many CS degree holders who could greatly benefit from this article. So while to some it's obvious stuff, just because it's obvious advice doesn't mean that it's always followed.

    I did kind of cringe though, at the bit about good var naming. I have been known to name vars things like ArthurKingOfTheBritons and IckyIckyIckyPtangZoomBoing when a var is used just once or twice and is declared in close proximity to where it's used, but of course we all know that's bad practice.
  • by swillden (191260) * <shawn-ds@willden.org> on Wednesday May 30, 2007 @05:38PM (#19328183) Homepage Journal

    The real thing is to used named constants where it makes sense. #define is the crudest approximation of that, C can use enums, C++ can use "const" for compile-time values, etc.

    That was my thought, too. When writing C++, you should *avoid* #define like the plague. In fact, avoid using the preprocessor for anything except including (and guarding) headers and, occasionally, conditional compilation. One of the best things about enums is that they create not just values, but *types*. So, if you define:

    enum AlienCount { MAX_NUM_ALIENS = 20 };
    enum PointValue { POINT_VALUE_FOR_ALIEN = 10, POINT_VALUE_FOR_SPACESHIP = 30 };

    void givePlayerSomePoints(PointValue points);

    The compiler will barf if you accidentally type:

    givePlayerSomePoints(MAX_NUM_ALIENS)

    ... or something equally silly, but perhaps less obviously wrong.

    Smart C++ programmers find ways to get the compiler to point out their mistakes. One of the most powerful (and most clever) examples of this is in Barton and Nackman's book on C++ for scientists and engineers. They make use of templates to define types for representing physical values that not only have units attached, but which allow the compiler to statically check the units. Given:

    Mass m = 4 * kg;
    Acceleration g = 9.8 * meter / (second * second);
    Force f;
    f = m; // Generates a compile error!
    f = m * g; // No error here

    The compiler would take care of all of the unit checking at compile time, and, assuming we got rid of the erroneous line, generate machine code equivalent to:

    double m = 4;
    double g = 9.8;
    double f = 39.2; // No need to delay the multiplication to runtime.

    And if m or g aren't actually used except to calculate f, the compiler will optimize them away completely.

    I used the verbose version of their syntax, BTW. You can also write:

    Mass m(4);
    Acceleration g(9.8);
    Force f = m*g;

    which will apply default units to the numeric values. Of course, good code would also define a "const Acceleration g(9.8)" in a header somewhere, etc., rather than using numeric constants directly in the code, and it would use better variable names.

    Of course, such usage is well beyond the "Introductory" level of this article, but I think even an introductory article on C++ should recommend using enums to define constants, not #define. More advanced C++ users should devote a little time to writing classes that idiot-proof the code (because we're *all* idiots, at least some of the time), ideally without sacrificing performance.

  • by Anonymous Coward on Wednesday May 30, 2007 @05:50PM (#19328383)

    First off, it's not a side-effect. It's intended.

    I think you're just not familiar with the terminology here. Anything that is observable outside of an expression other than through the returned value is a side effect [wikipedia.org]. If you call puts("foo"), printing foo to stdout is a side effect even though it's the whole point of the function call.
  • Re:Mostly agreed (Score:3, Interesting)

    by AuMatar (183847) on Wednesday May 30, 2007 @06:36PM (#19329241)
    In most C++ libraries, assert is conditionally compiled to noop in non-debug builds. So the same issue applies. That doesn't mean don't use asserts, it means that assert statements should not have side effects.
  • by dkf (304284) <donal.k.fellows@manchester.ac.uk> on Wednesday May 30, 2007 @08:01PM (#19330355) Homepage
    I should have noted why I think that Ravioli Code is a bad thing (and hence that those who think it is good style are doing a disservice to their trade). The problem is that it tends to lead to functions (methods, etc.) without true coherence, and it often leaves the code to implement even something fairly simple scattered over a very large number of functions. Anyone having to maintain the code has to understand how all the calls between all the bits work, recreating almost all the badness of Spaghetti Code except with function calls instead of GOTO. It is far better to ensure that each function has a strong consistent description (e.g. "this function frobnicates the foobar", which you should attach to the function somehow - in C, by a comment because there's no stronger metadata scheme) rather than splitting it up into smaller pieces ("stage 1 of preparing to frobnicate the foo part of the foobar", etc.) with less coherence. The principal reason why this is better is precisely that it makes the code easier overall to understand.

    This is not to argue against splitting functions where necessary; sometimes you need to split things in odd ways with tricky internal coherence to make other parts of the code much neater, and sometimes it improves separation of concerns (a good thing!) But splitting stuff into ultra-short functions for its own sake is missing the point, and those who do it are making big headaches for the maintainers of that code. I say this from a position of authority; I've had to maintain applications I wrote several years after believing them obsolete and setting them aside (yes, with a total hiatus in-between) and I know for sure that understanding the code is the biggest challenge of all for maintenance. People who deliberately split code up into ravioli, and especially those who advocate that others do so, are "dangerous idiots" precisely because they've lost sight of the fundamental need for comprehensibility and are encouraging others to also stray from The True Path.

    Writing good code takes good taste, and any basic technique should be applied judiciously. It's the higher-level things (comprehensibility, separation of concerns, consistency) that are the true goals of the Good Programmer.

    [Ye gods! This message is preachy. Serves me right for posting really late...]
  • by jguthrie (57467) <jguthrie@brokersys . c om> on Wednesday May 30, 2007 @10:17PM (#19331477) Homepage
    You need to read this comment [slashdot.org] and stop complaining that college is trying to educate you instead of training you. Increasing your general knowledge level is most emphatically not a waste of your time and money. What's a waste is having an opportunity to access all kinds of knowledge (most especially knowledge that doesn't have anything to do with your major--you'll find that employers encourage gaining knowledge in your field, but won't let you study interesting but unrelated stuff) and not taking advantage of it to learn all kinds of cool stuff. What's wrong with you?
  • by redcane (604255) on Wednesday May 30, 2007 @11:17PM (#19332077)
    Being worth billions is definitely *not* my goal in life. Being able to pick up work easily and without stress is. Having a degree certainly helps the latter goal, but not necesscarily the former.
  • by 91degrees (207121) on Thursday May 31, 2007 @05:58PM (#19344631) Journal
    Re. file scope: if you can figure out where to put function prototypes and const definitions such that they'll be visible in the right places, how is appropriate placement of macro definitions any harder? They go in the same place.

    I tend to put a lot of my consts within a class or function. You can sometimes do this with #define and #undef but it's less clear what you're doing. And even if you do this, you need to be a lot more careful about namespace pollution. C++ will allow the scope to be limitted to a namespace.

    Though you do still have to be careful about passing in expressions that have side-effects (because you don't know how many times they will be evaluated), or that are expensive to compute (inefficient for the same reason).

    This is a concern as well. But unless you need to use the paste operators, a macro doesn't have any benefit over a function. With a function, it's a lot clearer what the code does.

Aren't you glad you're not getting all the government you pay for now?

Working...