Thursday, April 25, 2013

The shortest book on good programming, ever!

The Coders Decalogue

This text is about making software work better and saving huge amounts of time, irritation and frustration for developers, users, customers and other stakeholders in the software business. Which likely means you.

When not developing my own software AxCrypt and Xecrets, I work as a contractor and consultant. In my work, I work with new software and old software. I work quite a bit with advanced troubleshooting and performance optimization in the .NET area.

Over the years, I've come to realize that I spend most of my time as a developer, doing things I wouldn't need to do if just a few simple rules are followed. I'd still have more than enough to do, no worries, but I'd be delivering much more real value to my customers for each hour spent. And so would millions of other developers. Come on - this is really not that hard!

I won't explain the rationale here, or give lot's of pedagogical examples. That would turn this into a real book, which would be nice. But I don't have time to write a book and you probably don't have time to read one.

So just trust me on this ;-) Really.

  • Do write code for humans. Smart one-liners, compact code, use of sneaky language constructs etc may not break your program. But it's not enough that the compiler understands the code. Don't write for the compiler, write your code in a style to make it as easy to read for humans as possible.
  • Don't copy and paste code with any kind of logic (ifs, loops, selects etc). Do always factor out common snippets. Even when you're in a hurry. Single-liners without logic are ok. That's called a statement in most languages, and you do need a few of those to make something happen and they can't all be uniqe.
  • Don't check-in commented out code. It's ok when you're trying out the new code - but when you're done, you're done. Since the code anyway resides in a version control system (right?), the old code is still available in the history. Do check-in clean code frequently always improving it slightly at the very least..
  • Do use long and descriptive class, method and member names. Letters in your source code are cheap. Use them freely. Don't abbreviate unless it's an industry or domain standard. 
  • Don't comment code to explain what it does. If you need comments to explain the code, fix the code instead so it's understandable. If you release libraries, use structured comments for public classes and methods to document intended usage patterns, assumptions and contract details. Do comment why the code does what it does, when it's not obvious.
  • Don't nest if-statements or loops. In some special cases, one-liners inside a nested if may be ok. Do use early-exit and write small methods to remove the need for nestling inside a method.
  • Don't catch exceptions unless you know why you're catching them and what to do about it. Never catch all exceptions, except at the top of a given thread's call hierarchy and then only if consequences of not catching it dictate the need. If you do, log it! Do program to avoid exceptions when you know the conditions to prevent it happening in the first place.
  • Do write short methods that does one thing and are named accordingly. If a method does not fit on a screen of a reasonable size, then it does too much. If you have trouble naming it properly, it probably does too many things. Don't write long methods that you need to scroll to see all of.
  • Don't try to be smart. When there is no known need for advanced or smart solutions, do keep it simple and use simple standard patterns until you know it needs special treatment. 
  • Don't optimize unless you know you need to. You'll know by measurements using performance profilers. This is not the same thing as writing inefficient code. Do write efficient code according to best practices that avoids known pitfalls and bad design. Performance optimizations come on top of that, for example caching or special-purpose thread-synchronization constructs, and are to be avoided until the need is proven.
  • Do always step through your code at least once to verify your assumptions about it's behavior. Don't trust just running the application and be satisified when it appears to work.
This is in no way the complete zen of good programming, nor is it revolutionary or unique. All of this has been said before. I'm sure you'll have your own pet peeves you'd like to add to the list. I have a few of my own, but the idea here is to list important things that are really super-simple to do. Now.

I am absolutely convinced that if these rules are followed, overall productivity in the software industry will rise dramatically.

If you're a developer, are there any of these rules you honestly disagree with? Do you work like this already? If not, try it out! Use peer-reviews to discuss your check-ins with this list as a guideline.

It's really this simple.

PS - There are 11 rules here. I'd like to get it down to 10 as the title indicates. Cast your vote on which one should go! Or perhaps what needs to be added, but then you'll have to drop two... ;-)

4 comments:

  1. When you say don't nest if statements, you mean, instead of writing something like this

    if (cond1)
    {
          statement1;
          if (cond2)
          {
                statement2;
          }
    }

    you should write something like this instead?

    if (cond1)
    {
          statement1;
    }

    if (cond1 && cond2)
    {
          statement2;
    }

    ReplyDelete
  2. Depends on what comes before or after, but the simple case becomes:

        if (!cond1)
        {
            return;
        }
        statement1;
        if (!cond2)
        {
            return;
        }
        statement2;

    The 'return' statement is a very important flow-control-statement in reducing complexity, thus it's important to see the method boundaries. If your example happens in a bigger context, the example needs to change - but your example is very typical for real code

    ReplyDelete
  3. Is there a style guide for this kind of unnesting? You've got me intrigued now...

    ReplyDelete
  4. Essentially, when working with existing code, it's a series of rather simple refactoring transforms. For the outermost 'if'-'else', flip the condition and handle the 'else' first, then return. Do the original 'if' inline. Repeat.

    When common code is executed after the outermost 'else' (or 'if', when there is no 'else'), break out the 'if' to a new method, and re-apply the procedure to the two new methods.

    The nice thing is that it tends to create code that weeds out the error conditions first at the top of the method, finally proceeding to doing the real work inline, outside of any conditional scope. This turns out to be much easier to read and maintain since the limitations of the contract the method fulfills is in plain sight at the top.

    ReplyDelete