Want to read Slashdot from your mobile device? Point it at m.slashdot.org and keep reading!

 



Forgot your password?
typodupeerror
×
Programming IT Technology

How to Keep Your Code From Destroying You 486

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 rah1420 ( 234198 ) <rah1420@gmail.com> on Wednesday May 30, 2007 @03:14PM (#19325781)
    Comments, clarity, constants. If you're not doing this in your daily coding exertions, you deserve to have to maintain your own stuff 10 years from now.

    I have. It ain't fun. Not that I'm bragging on myself, but I've now had people from the support group stop me in the hall and compliment me on the quality of the code I've written and deployed.

  • by FortKnox ( 169099 ) * on Wednesday May 30, 2007 @03:15PM (#19325801) Homepage Journal
    That has to be the worst written article on cleaning up your code I've ever read.
    This looks like it was written for (and BY) freshmen CS majors.

    Comment your code smartly? No shit?
    Use #defines everywhere? Honestly, I find that having a config file (or DB table) is a lot better, as I can change global variables without even a recompile...

    I'm not saying its BAD advice, its just advice that anyone in the real world already knows.
    How about something new?
    1.) Use test driven development
    2.) Write complete unit tests, including bad input
    3.) If any piece of code is complex enough to require a comment, make it its own function and comment the function. I believe the only thing that REQUIRES comments are classes and methods. Not pieces of code...

    I code go on, but I'm not a writer...
    And neither is the author of that pile of trash...
  • by mkcmkc ( 197982 ) on Wednesday May 30, 2007 @03:17PM (#19325809)
    It's amazing how much simpler life is if your language will check errors (esp I/O errors) by default. That is, if you do a write and if fails (e.g., because the disk is full), an exception gets thrown, and even if you haven't written any error handling code at all, you get a nice explanatory error message.

    C, C++, and Perl are not "safe" in this sense. Python is. Not sure about other common languages.

  • by endianx ( 1006895 ) on Wednesday May 30, 2007 @03:21PM (#19325889)
    I agree. This is very basic stuff you would learn in Intro to Programming, or in any other article on the subject. Nothing new here.
    I highly recommend a book called Code Complete. It is kinda pricey and, yes, it is by Microsoft, but I found it to be both helpful and thought provoking.
  • Mostly agreed (Score:5, Insightful)

    by ZorbaTHut ( 126196 ) on Wednesday May 30, 2007 @03:23PM (#19325913) Homepage
    I thought I'd make two comments on things that I think he got a bit wrong.

    Tip 2: Don't use #define. Avoid it as best as you can. Use const int. That's what it's for. It will be typechecked by the compiler, it's much harder to produce bizarre errors, and 99% of the time it's better.

    const int NUM_ALIENS_TO_KILL_TO_END_WAVE = 20;

    Tip 4: Warning messages don't work. Don't bother with them. Use assert() - if it triggers, your program will crash with a useful error message. Now that's an incentive to make things work!

    In my current project, out of 25,000 lines of code, I have almost 1100 asserts. And the first number counts whitespace. Any bugs I have get found and squashed pretty much instantly.
  • by dpbsmith ( 263124 ) on Wednesday May 30, 2007 @03:26PM (#19325937) Homepage
    Not that I don't use them a lot myself, but I thought that in C++ you were supposed to try to avoid the use of macros altogether, and in particular were supposed to use consts for, well, defining constants.

    I.e. not

    #define PIXEL_WIDTH_OF_PLAY_AREA 800
    #define PIXEL_HEIGHT_OF_PLAY_AREA 600

    but

    const int PIXEL_WIDTH_OF_PLAY_AREA=800;
    const int PIXEL_HEIGHT_OF_PLAY_AREA=600;
  • Invariants (Score:2, Insightful)

    by ljw1004 ( 764174 ) on Wednesday May 30, 2007 @03:27PM (#19325951)
    The comments I like best are correctness invariants or induction hypotheses. Like the ones you'd use to prove that your algorithm is correct. For example:

    // Invariant: if an object is ReadOnly, then everything that points to it is ReadOnly
    // Invariant: if an object is writeable, then everything it points to is writeable
    // Invariant: when you call CopyOnWrite(), you get back a writeable object
    // Invariant: The ReadOnly flag starts false, and will change to true during the object's lifetime, but can never change back to false.

    The correctness invariants for a data structure can also be embodied in a "sanity_check()" function for that data structure, and also in test cases. But the correctness invariants for an algorithm are rarely embodied in any code anywhere. But they're the ones that give you the programmer an assurance that your code is correct. That's why they're so important.
  • It was fine... (Score:5, Insightful)

    by Erasmus ( 32516 ) on Wednesday May 30, 2007 @03:27PM (#19325981)
    People who are just starting their careers as programmers are allowed to read articles too. Just because something is aimed at a population less experienced than you doesn't mean that it's crap!

    I'm not sure if it really called for a Slashdot entry, but I've been on a few projects with new coders where a quick read of something like this on their parts would have saved everyone a lot of grief.
  • by LiquidCoooled ( 634315 ) on Wednesday May 30, 2007 @03:31PM (#19326039) Homepage Journal
    Relying on the environment to do all cleanup leads to bad code.
    Every time you create something, destroy it afterwards.
    Assume every action will fail and handle it appropriately.

    I have seen 'developers' assume everything will be taken care of, then when the software gets into the users system their usage patterns make it explode.

    Simple management needn't make a development time longer or harder and allows you to migrate things to other applications/systems with ease.
  • by AuMatar ( 183847 ) on Wednesday May 30, 2007 @03:31PM (#19326041)
    Not every program uses a db. In fact the majority of programs don't. And unless a constant is going to change frequently, or needs to be configured per client, putting it in a configuration file or db table is a bad idea. It makes it fairly likely it will be changed by accident. The only things that should be in configuration files are things you actually expect the user to configure per install.

    As for your advice

    1)Thinking about testing early- good. Writing unit tests-good. The test driven development mentality (write tests instead of design, write unit tests before coding)- bad. It leads to a lot of wasted time, completely rewritten test suites, and throw away work. Thinking about testing early is useful, it may cause you to think about corner cases. But writing them first causes 2 problems- you end up writing the code to solve the tests (rather than solving the problem) and/or you end up throwing away half the test suite in the middle when you refactor the design.

    3)Disagree. The purpose of comments is to make sure that maintainers know what the code is trying to do. Anything block of code thats more than 5 or 6 lines deserves a comment. Breaking all of those into independent functions leaves you with hundreds of 5 or 6 line functions, which is even harder to understand how they interact. Frequently the correct thing to do is not break it into a function and just write a 1 line comment.
  • by 19thNervousBreakdown ( 768619 ) <davec-slashdot&lepertheory,net> on Wednesday May 30, 2007 @03:33PM (#19326071) Homepage

    ... if your C code requires you to know the difference between i++ and ++i, it is too complicated.

    Advice on good comments--great--but really, it's just obvious. Anyone that doesn't get how to comment well doesn't want to comment well. And the above quote made me want to wring his neck. If you don't know the difference between those two operators, you should stick to VB.

  • Basics (Score:2, Insightful)

    by Joaz Banbeck ( 1105839 ) on Wednesday May 30, 2007 @03:36PM (#19326115)
    Ok, yeah, it's redundant. But is there anything wrong with going back to the basics? Look at sports figures ( and I chose sports for an analogy because most pro sports are very good at filtering out all but the best, and the very best get to the top by proving that they are better than the second best in head to heah competition, something that programmers almost never do. ) The best often are known for continually going back to basics.

    Lombardi was known for his comment that if you block and tackle better than the other guy you win.

    Magic Johnson was known for being on the court an hour earlier than the other pros and practicing shot after shot.

    And Bobby Fisher's guide for beginners is still the best because he knew the basics like nobody else.
  • by loqi ( 754476 ) on Wednesday May 30, 2007 @03:45PM (#19326271)
    ... if your C code requires you to know the difference between i++ and ++i, it is too complicated

    It's not a matter of knowing the difference, it's a matter of the code depending on the difference. If you need to increment beforehand, do it on the previous line. Afterward, do it on the next line. Expressions are like sex: they're better without side-effects.
  • by Kalzus ( 86795 ) on Wednesday May 30, 2007 @03:47PM (#19326305)
    "Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it." - Brian W. Kernighan
  • Re:Mostly agreed (Score:3, Insightful)

    by ZorbaTHut ( 126196 ) on Wednesday May 30, 2007 @03:49PM (#19326325) Homepage
    Why "must"? Almost all C code compiles fine as C++ code, and the few things that won't (generally involving implicit casts to void*, as I understand it) aren't too hard to fix.

    I mean, unless you're using K&R C, in which case I feel very sorry for you.
  • Re:I remember when (Score:1, Insightful)

    by Anonymous Coward on Wednesday May 30, 2007 @03:50PM (#19326341)
    Funny, because I also remembered when programmers, so concerned with CPU time, would cut out checks, mess with code until it worked 'right' and would generally tweak it until it worked, to their mind, perfectly. But in fact would crash on unusual conditions or outright produce incorrect output. But it was fast. Fast and wrong. And often times the 'optimizations' they were doing were not anything that really affected performance. First rule of optimization, don't until it's proven needed.

    We end up with so much bloated, broken code because people don't worry about right before 'performance' or 'reusuablity'. Get it right and you'll find a lot of the rest of it comes from that. Including maintainability. The best maintainable code package I worked on also happened to be the fastest(faster by an order of magnitude) over the original. The new code was also object oriented, extensible, clean and actually correct(original didn't accurately calculate a value right).

    So I don't think that developers are any different than they used to be. They just write more code that's wrong, bloated and crappy. It just has a prettier interface, more colors when it explodes and more features that never get tested or used.
  • by Swizec ( 978239 ) on Wednesday May 30, 2007 @03:52PM (#19326383) Homepage
    You'd be surprised by how many programmers who have gone to actual rpogramming classes fail at anything as simple as indentation. Peopel jsut aren't taught this at school so either they are wise and learn it themselves or they read somewhere about how to code like a human being and not a thousand monkeys on typewriters.

    The article may be redundant to many, but I can name at least twenty people off the top of my head taht should give this a long hard read and then read it again.
  • by seaturnip ( 1068078 ) on Wednesday May 30, 2007 @03:52PM (#19326399)
    Somewhere along the course of reading the article, I also got the impression that he wasn't a professional developer himself (at least, a smart one).
  • Re:Expected Value (Score:3, Insightful)

    by jfengel ( 409917 ) on Wednesday May 30, 2007 @03:53PM (#19326429) Homepage Journal
    This is also one of those "Check mark: $1 Knowing where to put it: $49,999" problems. It takes you a minute to comment a function... times 50 methods a day... times the 1% of comments you ever actually need to go back and read.

    Suddenly that "one minute" is a lot of hours spent writing comments that you'll never read, cluttering up your code and getting wronger as you don't maintain them.

    If I knew which comment to write, sure, I'd write it. And I do, when I think it's appropriate. There are plenty of times I wish I'd commented something, but they're way outweighed by times I didn't bother. Good function and variables names are more important to me.

    If you can't come up with a good name, refactor until you can. A unit of code should do something coherent and easily describable, preferably until you don't need a comment.

    Yeah, you'll have to document dependencies, but you should keep them as few as possible. A good language decreases dependencies. I don't have to write "I expect you to free the space for this string" or "This string is stored in a static; do not reuse" because I write in Java.
  • by itlurksbeneath ( 952654 ) on Wednesday May 30, 2007 @04:04PM (#19326575) Journal
    Well, there are a lot of "programmers" that I work with that don't actually have degrees in computer science. Heck, some of them don't have degrees at all and certainly haven't attended "Intro to Programming". I forwarded the article around to several folks here as a "hint, hint".
  • Tip 3 is crap. (Score:3, Insightful)

    by geekoid ( 135745 ) <dadinportland&yahoo,com> on Wednesday May 30, 2007 @04:11PM (#19326679) Homepage Journal
    " For example, in real life, I probably wouldn't give constants names as long as I did in the previous section. I just did that so that you, the reader, would totally understand what they meant without any context. In the context of the program itself, instead of:

    #define MAX_ALIENS_ON_SCREEN_AT_ONCE 5
    I would almost undoubtedly write:

    #define MAX_NUM_ALIENS 5

    Any confusion caused by the shorter name would be cleared up very quickly, and the shorter name would lead to much more readable code. "

    Cleared up very quickly? no, it can only be cleared up after searching your code to realize it is the max number of aliens on the screen, not the max per level, or per game, or whatever the hell you were thinking 2 years ago when you wrote it.
    Bad Bad Bad.
    sloppy.
  • by hobo sapiens ( 893427 ) <[ ] ['' in gap]> on Wednesday May 30, 2007 @04:21PM (#19326855) Journal
    "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."

    Agreed, but some of the things in this article can be applied conceptually if not literally. Don't want a 2MB config file or header file? Right, me either. But break your program down into smaller pieces and declare stuff at that level and group it together at that level. Conceptually the same as what he is recommending, just done according to your actual implementation.

    I too thought the article was very basic, but that doesn't mean that the principles don't apply well to systems larger than a simple game.
  • by Nyh ( 55741 ) on Wednesday May 30, 2007 @04:37PM (#19327093)
    If any piece of code is complex enough to require a comment, make it its own function and comment the function.

    That is just hiding your complexety. A massive tree of functions called each one time is as complex as all the code sequencally in one function. Plowing through the massive tree of functions will cost you more time as reading sequentially through your code whit comments at the places you would have created a function.

    Nyh
  • by Anonymous Coward on Wednesday May 30, 2007 @04:37PM (#19327099)
    I suspect that was a formatting error on the part of the people who put up the web page.
  • *ahem* (Score:3, Insightful)

    by Greyfox ( 87712 ) on Wednesday May 30, 2007 @04:50PM (#19327337) Homepage Journal
    /* * Maximum number of aliens on the screen at once */ #define MAX_NUM_ALIENS 5
  • Re:Mostly agreed (Score:3, Insightful)

    by drxenos ( 573895 ) on Wednesday May 30, 2007 @05:04PM (#19327585)
    I use C++ for embedded systems all the time (along with C and Ada). Compilers will "inline" constants just as well as #defines are. By "symbol table" I assume you are talking about the global symbol table used by the dynamic link-loader. Since constants are local by default I would not worry about this, as they will not appear in the table. And don't listen to the commenter saying you should use C instead of C++ for embedded systems. That's just know-nothing nonsense.
  • Hungarian Notation (Score:4, Insightful)

    by PhoenixRising ( 36999 ) <ngroot+slashdot@ ... g ['l.o' in gap]> on Wednesday May 30, 2007 @05:11PM (#19327699) Homepage

    For example, there is something called Hungarian Notation. There are lots of flavors of this, but the basic idea is that you put a tag at the beginning of a variable name saying what type it is.

    I wish he'd included a link to the Wikipedia article on Hungarian notation [wikipedia.org] and specifically referenced "Apps Hungarian". Hungarian notation is essentially a cheap way to create programmer-enforced "types". When these are truly new types ("dirty string", "null-terminated string", etc.) not known to the compiler/interpreter, it might be reasonable; this is "Apps Hungarian". However, prefixing an unsigned int with "ul" (i.e., "Systems Hungarian") is silly; your compiler should warn you/error out if you're trying to do something inappropriate with it, since it knows what an unsigned int is. Hungarian notation will be a useful thing until it's as easy to define new types in common programming languages as it is in, say, Haskell, but it should be used judiciously.

  • by geek2k5 ( 882748 ) on Wednesday May 30, 2007 @05:16PM (#19327783)

    It may be a dead horse to good coders but it is essential that the accounting staff know such things. Such articles can be used to educate them. The articles can also be used to train the newbies they hire.

    Unfortunately, there are organizations where the bean counters and CEOs want results NOW and don't want the staff to 'waste' their time on documentation and good coding practices. If you can get your quick and dirty code completed for that 'one time' application up and running really fast, they will love you for it and the effect it has on the bottom line. If you insist that documentation and special good coding refinements are needed and it will take half a day longer, they'll tell you not to waste your time.

    Then, a month or a quarter or a year later, they will ask you to take that 'one time' application and modify it for slightly different parameters. And because you did it before, they will expect it to be done in a fraction of the time.

    It is an endless treadmill with some organizations. While doing the right thing usually costs less in the long run, getting the results NOW often overrules doing the right thing.

  • by Lord Ender ( 156273 ) on Wednesday May 30, 2007 @05:20PM (#19327849) Homepage
    I disagree. Since I started writing the "main" part of my programs as nothing but flow control (if, else, while) and function calls (which did the actual processing), I find that it is much easier to analyze problems in my software.
  • by mkcmkc ( 197982 ) on Wednesday May 30, 2007 @05:26PM (#19327967)

    Assume every action will fail and handle it appropriately.

    True enough, but this misses my point. The question is: What happens when a programmer fails to properly handle errors?

    This happens all the time, either because the programmer is not sufficiently competent, or simply misses a check, or because the program in question is a prototype that got pushed into production without being reworked.

    Having the language produce useful error messages by default does not preclude an other strategy regarding error handling, resource deallocation, etc. It wouldn't necessarily even need to be done via exceptions. It just needs to change the default strategy from fail-silently to fail-safe, which is what you really want if you care at all about reliability and correctness.

  • by Anonymous Brave Guy ( 457657 ) on Wednesday May 30, 2007 @06:07PM (#19328675)

    That's a bit harsh. Apart from writing comments that are a maintenance liability, using C++ macros when constants would be better, mentioning the use of Hungarian notation that is a liability without mentioning the use that can actually be useful, advocating silent failure in the case of failed preconditions, misquoting Knuth and, to add insult to injury, citing a Wikipedia article in support when that article is currently tagged as having dubious citations (I know; I put the tag there a few weeks ago), failing to understand that games development is one of the few areas where early optimisation is basically a fact of life for some genres, and arguing that you shouldn't rely on programmers knowing basic language facilities like the pre- and post-increment operators in the C family, what was wrong with it? :-)

    I am, of course, being facetious. As the author himself points out at the end, much of this stuff isn't obvious to newbies, and it's better if someone tells them earlier rather than later, so kudos to him for taking the time to write it up. I do wish people volunteering such material would get some peer review if they can, though, because the only thing worse for inquisitive newbies than no information is bad information.

  • Re:Mostly agreed (Score:4, Insightful)

    by drawfour ( 791912 ) on Wednesday May 30, 2007 @06:25PM (#19329041)
    Not only that, but when debugging, since there is an entry in the symbols for that variable name, you can see what the value is inside the debugger without having to look to find where the macro was defined.
  • Re:Expected Value (Score:3, Insightful)

    by moderatorrater ( 1095745 ) on Wednesday May 30, 2007 @06:57PM (#19329535)
    I'm with you. Almost every time I commit to commenting more, I can't find things to comment that aren't obvious in the code. In 90% of the cases, the code should either comment itself or be rewritten.
  • by ThePromenader ( 878501 ) on Wednesday May 30, 2007 @07:39PM (#19330105) Homepage Journal
    Although based on advice that would seem rather obvious (one should think) to a more experienced coder, in all it is a good article that will provide food for thought for anyone still learning or tired of wrangling through code. I don't see what microsoft has to do with anything contained in the article.

    I have the misfortune of having two trades - photography and graphic/web design/development - and I can say that when I return from a location to pick up coding where I left it three days before, it sometimes takes me several hours to fully comprehend/remember and reconstruct all the already completed processes in my head so I can move forward. Much of the article's seemingly obvious lessons I only learned through time, so perhaps it will save some of the same (and a few headaches) for another just getting into the trade. The coding one.
  • by Coryoth ( 254751 ) on Wednesday May 30, 2007 @07:59PM (#19330333) Homepage Journal
    Refactoring code to make things as clear and obvious as possible is, of course, a good idea. But it is no substitute for actually stating your intentions in the form of comments or contracts. A maintainer coming to search for a bug in code that is clear but uncommented can only discern what the code actually does as opposed to what it was intended to do. Thus if the bug is a result of a gap between intended functionality and what got implemented (as happens often enough to matter) it makes things a lot harder to track down. If you an state clearly and simply what a block of code is intended to do, do it -- it gives a maintainerr something to verify against.
  • by tagattack ( 412693 ) on Wednesday May 30, 2007 @08:49PM (#19330771) Homepage
    I appreciate your effort(s) to improve the quality of code in general. It's a noble goal I've spent my entire career trying do, including mentoring co-workers and junior level engineers as well as writing papers, and a lot of preaching and ranting...

    But part of the problem I saw when I read your article is that your suggestions really aren't that good. Take for instance your example of "good" comments:

    // This procedure moves the bullet upwards. It's called
    //NUM_BULLET_MOVES_PER_SECOND times per second. It returns TRUE if the
    //bullet is to be erased (because it hit a target or the top of the screen) and FALSE
    //otherwise.

    That comment is actually documentation, and if you are going to document in comments (not *entirely* a bad idea) then you should be actually using a type of comment designed to be picked up by some documentation system, such as autodoc.

    Describing what your code does is the job of documentation, describing why your code does what it does the WAY it does is the job of comments.

    Boolean player_bullet::move_it() /* <- move_it is kind of a crummy name */
    {
    Boolean is_destroyed = FALSE;

    // Calculate the bullet's new position.

    An example of just saying what you're doing instead of why you're doing what you're doing.

    [Small chunk of code.]

    // See if an enemy is in the new position. If so, call enemy destruction call and
    // set is_destroyed to TRUE

    [small chunk of code]

    // See if bullet hits top of screen. If so, set is_destroyed to TRUE

    [Small chunk of code.]

    // Change bullet's position.

    Why do we need to say that this changes the bullets position?
    Shouldn't the code probably look like, bullet.setX(x); bullet.setY(y); or some variation thereof and a redraw? Wouldn't that indicate that the code is moving the bullet? Do we really need a comment to say this?

    [Small chunk of code.]

    Return is_destroyed;
    }

    In retrospect, a good time to use a comment is when you deviate from anything a future onlooker would be expecting from your method, function or subroutine. For instance, where you would do something specifically because of another bug that or behavior that is out of your control.

    /* We cannot add the events immediately or they simply will
    * fail to work. Event's on un-rendered nodes are
    * optimized away. But these nodes will be rendered when
    * this operation is finished, so a single cycle delay will
    * make sure we let this thing render before attaching
    * events.
    */
    window.setTimeout( function () {
    self.populate(child)
    }, 0 )

    A smart person doesn't need to remind himself what code X does, unless it's not obvious.

    this.y = y
    this.x = x

    ...That's pretty obvious

    In the section where you suggest using define a lot, I honestly consider that a particularly bad idea. Using global constants is just in general a bad practice. A good practice, on the other hand, is having all arbitrary static data be a matter of configuration. That is, instead of defining NUM_ALIENS_TO_KILL_TO_END_WAVE as a compile time constant, or any other form of literal, define it in a configuration file so it can easily be modified without a code change.

    Enabling behavioral changes without code changes is critical to software success. Every time you change code, you rebuild and redeploy things. And this provides the potential for other

  • MOD PARENT UP (Score:2, Insightful)

    by Deef ( 162646 ) on Wednesday May 30, 2007 @10:18PM (#19331479)
    I've tried this both ways over the years, and in my experience, separating large functions into small, well-named ones with clearly delineated inputs and outputs is a big net win for maintainability. It makes it much easier to see what is going on, because it doesn't force someone reading the code to have to confront the whole mess at once. They can dig deeper into only the subfunctions that are relevant to the problem that they are trying to solve, without worrying that some non-local effect of a prior block of code will confuse the issue.

I've noticed several design suggestions in your code.

Working...