Monday, June 2, 2008

Naming Conventions

Hungarian is lame. Foolish. Using Hungarian is saying "knowing the type of this variable is sofa king important that I must encode it in the variable name."

Typing isn't a major part of programming. Being able to type quickly helps, but short names don't save you that much. If you have to spend just one second trying to figure out what a variable is, that short name just cost you more time than you gained. So I'm not complaining about typing the extra characters in the name.

Source code is for the programmer, not the compiler. Compilers are easy to satisfy. They tell you if you did anything wrong. If you need to make sure you have the types right... don't. The compiler will tell you. In C#, you can't mistakenly treat an int like a char, or a bool like an int, or even mix your pointer types transparently. With warnings cranked up, C++ is just as type-strict. Back in the olden days of C, when pointers could go to void* or int or any other type without letting you know, the programmer had to be careful with variable types. We don't need to do that any more. It's a bad justification for variable-name cruft.

But correctness, that's a different kettle of fish. Your code should be easy to understand, by you, by your coworkers, by yourself in six months. That's my standard here: variable names should help you understand the code.

One analogy I frequently make is to say the brain is like a CPU. You've got 7 (plus or minus two) registers to play with. Time that you spend parsing a Hungarian prefix (and then discarding it) is still time spent, plus processing all that will consume one or two of your registers -- meaning that you'll either have to dump some registers into long-term storage or, more likely, discard them. When you're looking at a function, you've got to keep a lot of stuff in mind. What should the function do? If you're debugging, what is it actually doing? You might be thinking about a calling function's needs. The algorithm itself might be complex. The more complex the algorithm, or even dealing with special cases, the more registers you need to understand it. If you're wasting registers dealing with cruft, then it'll take you longer to make the function correct.

Forget typing it in the first place. You want to minimize the time you spend on a function while still making sure it is correct, which means satisfying yourself about correctness as efficiently as possible.

Words are tags; labels. Their written (and spoken) forms serve as anchors for the underlying concepts. Those concepts are mental entities. When you're thinking about an algorithm, you want to be working with simple tags. If a tag is something long like "previousWorldStateIndex", or, worse, "wsidxPrevious", then you've got to blow registers parsing all those keywords. When thinking about the algorithm at hand, "previous" works just fine. When you're in deep thought, all you need is a simple handle, not an accurate one.

Your functions shouldn't be long, either. Most of mine are around 7 (plus or minus two) lines long. There's a good number that are shorter, plus some that are over 9 lines. If you can abstract a line into one thought, then you can grasp a whole function at once, rolling your mind back and forth over it, like you'd probe a strange object with your fingers.

A variable declaration defines an object type just fine. If the function is only 7 lines long, you don't need to repeat the variable declaration over and over. When I'm debugging, I need to read that declaration just once, and then I've loaded that concept into one of my mental registers and I can ignore the declaration from then on. Because the compiler will catch gross type incompatibilities, and your thinking about the algorithm should catch others, all you need is a one-word tag for the variable.

No comments: