Showing posts with label naming conventions. Show all posts
Showing posts with label naming conventions. Show all posts

Tuesday, August 12, 2008

Mental Economy - Communicating with Programmers Part I

Good programming style is more a matter of communicating well with other programmers than capturing an algorithm elegantly. Sometimes that other programmer is you, in six hours or six years. Being a good programmer starts with good problem-solving skills and a broad knowledge of both algorithms and APIs. Good employees are disciplined and self-starting. Beyond that, on almost any scale of project, is writing code that can be easily understood.

There are a few aspects of inter-programmer communication that I want to cover. The first is mental economy -- how much data your brain can work with at one point. Part two is about jargon, and in the third part of this series I'll cover grammar.

I was working on ellipse-drawing code the other day and, while mired deep in the math, realized that I didn't have a lot of bandwidth to deal with other bits of the code. I've talked to (and, unfortunately, worked with) a number of programmers that think they're great if they can solve really complex problems and fix deep bugs in spaghetti code. I think it's worse having a manager that thinks the mark of a good programmer is fixing deep bugs in spaghetti code.

The problem with spaghetti code is that you spend all your mental powers unraveling the spaghetti, rather than solving the problem. Part of the problem with the ellipse code I was working with was that the paper I was reading from had bugs. So, not only did I have to read and understand what the code was doing, I had to reverse-engineer the algorithm they were using. They had comments like "it's faster to do it this way", without explaining why, or even what the 'stupid' way was.

I liken the brain to a CPU. An x86 CPU, to be specific, according to its architectural definition rather than its actual implementation, which is even worse. Details aside, it only has about 7 (plus or minus two) registers to play with. Having to deal with chasing down the meaning of a new or unknown or poorly-explained piece of data means saving some state off to long-term storage (e.g. a piece of paper), exploring a bit, and then reconstructing where you were before.

That's the point of variable names, subroutines, and classes. It's not to elegantly capture behavior; the purpose of "elegantly capturing behavior" is a means to the ends -- the end of minimizing the subroutines that you'll subject your brain to when you try to parse code.

You can either be born brilliant, or trained. About half of the great programmers I know have learned to be brilliant. They're rational, methodical, skeptical, and mentally disciplined. Writing code that is well-organized makes it easier to read code, and that makes that code easier to extend, modify, and debug. They "work smarter, not harder." They don't have to waste brain power untangling spaghetti, or mentally keeping track of dozens of variables and routines and equations.

If you haven't read that George Miller paper (the "plus or minus two" link above), I highly recommend it.

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.