Want a challenge? Take a nice. long paper you've had around for years, expand it into a chapter in a book and then try to fit it on a flashcard. Flash cards are like poetry. You have to cut the material down to the smallest and most poignant form you can so that it is memorable and sparks memories in the psyche of the reader. I'm hoping that this one doesn't find me a poor poet.
The name says what it is for, not what it is. Poor names tend to say nothing, or the wrong thing. Take the variable 'x' for example. How about psz? I hope it's a pointer to a zero-terminated string, but I can't bet on it and have no idea what it is for. The English word for one piece of automobile safety equipment is "windshield". I'm told that the Japanese name for the same device is "front glass". In variables, we lean toward "windshield" and away from "front glass" if we want our code to make sense to our peers.
Avoid encodings of any kind. Psz (from above) encodes type enformation. IDoldrum contains an encoding "wart". Psz_agey may be a string containing a person's age in years, but that's not obvious. The example from paper and book is "gen_ymdhms". These encodings can be learned, and having been learned they join the other pile of pointless minutae in the reader's mind, but that's not a good reason to use them, no more than I should pinch your arm just because I know you'll heal. We write names to make code obvious and clear.
Functions and variables have the same level of abstraction. For this reason, in a Person class, we expect fullName, birthDate, address. We don't really expect stringDict or autoPtrArray to be part of a person.
Use pronounceable names. Most people read text with their innervoice, so a pronounceable name keeps them from say "blah-blah-blah" mentally (leading to errors). It is also much easier to collaborate with a pair partner if you can actually pronounce the names of the symbols you're manipulating. Finally, you can explain the program to new partners, bosses, or the like if you have pronounceable names. Names exist to communicate. Don't hamstring them.
Shun names that disinform or confuse. Don't call something 'list' if it is not a list. (Even if it is a list, don't call it 'list' but that's covered elsewhere in this list.) Avoid calling a variable 'ram' or 'mem'. Don't call your internal integer a 'socket' unless it is a socket. Don't use 'iSomething' for non-interfaces. If you use a name that causes a peer to misunderstand your code, take it as a coding error and fix it. Renames are cheap.
Make context meaningful. Don't add gratuitous warts at the front or back of your names. Especially the fronts. If everything in your application is named with the prefix 'app_' then you are causing people to look into the middles of names to find meaning in the name. Likewise, the dotted-names common in object-oriented code should have meaning in their context (person.age() v. person.session()). If a variable name is out of place in its namespace, class, or module then perhaps it is because it is in the wrong place.
Length of identifier matches scope. For a local variable in an anonymous one-line function, x, y, z, i, j, k are all fine variable names. For a variable in a 12-line function it is insufficient. For a parameter to a function call it is wholly in appropriate. As class names or module names, these are insanely poor choices. A global variable with a short name is an abomination on so many levels. This rule was gleaned from James Grenning, and I think it should have been in my original paper. It's a good rule.
No lower-case L or upper-case o, ever. 1t should be pretty Obvious that nob0dy shou1d ever cause others to confuse the letters 1 and 0 with the numbers l and O. It obviously is okay for you to use L in names like
oldName
and capital O in something like Organize
but it is an act of purest naming evil to have a name that consists only of l and O
. It doesn't take a genius to see the confusion in "return l - 12 > O;"
Beck calls the first one "intention revealing" names.
ReplyDeleteYes, but I struggle with using that. I wanted to press the "windshield" v. "front glass", just as "name" verses "long string", etc. I think that intention-revealing as a soundbite didn't quite say that to everybody. "I intended it to be an arbitrary integer variable" is hard to defend in that way.
ReplyDeleteI think I could fold the second-to-last and third-to-last together (and some others I cut out) by switching to the statement "scope is context". It's less obvious, but once you've heard it it should help.
ReplyDeleteMore-inner-scope variables have more context, need less explicit statement of type/etc. More global ones need more explicit differentiating. The '.' is a naming element so database.gateway.Customer.byId() is a very long name with a pretty obvious scope. Likewise, any variable in the method is scoped clearly enough that you don't need a lot of name redundancy.
Likewise, having things in Customer.Customer.customer_by_id() looks stupid. Likewise any gratuitous branding looks moronic: timco.timcoWebApp.timcoCustomer.getTimCoCustomerbyId() hardly look like the work of a sane person.
Context is naming, naming is context. If you 'dot in' context by nested namespaces or you glue warts onto the front or back of your class, it's all naming.
And yes, Kent should get credit for "intention-revealing". I agree.
ReplyDeleteIs the last rule supposed to be there for humor purposes? All of the other rules have more general applicability; this one is fairly specific and narrow.
ReplyDelete"Length of identifier matches scope of identifier"--"matches" connotes equality to me; how about something like "Identifier length in proportion to scope"
I agree with most of the points but I completely disagree with the last bullet about lower case Ls and upper case Os. I've never found it a problem and am struggling to contrive one. Sure, avoid them if there is an equally good name that doesn't use them, but don't pick a worse name so as not to break that rule.
ReplyDeletevar O = l;
Deletevar l = 27 * O;
O += l;
ps. Good blog and thanks for posting!
ReplyDelete(even though I am 2 years late)
it's still here so you are not too late.
ReplyDeleteThere have been plenty of cases where l and 1 have neeb confused and caused misunderstandings and even bugs. If someone sees "if x == l". It certainly seems like one could simplify it to "if x" to keep it simple and small.
Likewise 0 and O. They might look different enough side-by-side but scattered in code it is rough.
If nothing else, imagine you want to copy the code into an article or document... surely another name might lead to fewer reader typos.
That is really all there is to it. A bad name is one that seems to be saying something other than it really means.
I can buy that last rule if you mean "No lower-case L or upper-case o, by themselves or not as an integral part of a full word." But if you mean my names can't contain a lowercase L or an uppercase O then that's ridiculous. That would mean that "lastName" is unacceptable, as would be "getOfficePhone()," neither of which could be reasonably misunderstood.
ReplyDeleteI hope I never work with the code of someone who misunderstood the spirit of the rule.
My intent was that the name should never be composed of only those two letters, lest it look like a number.
ReplyDeleteMy intent was that the name should never be composed of only those two letters, lest it look like a number.
ReplyDeletel01
DeleteI did something like this once:
ReplyDeletepublic void setReadOnly(boolean read0nly) {
this.readOnly = readOnly;
}
In a day where IDEs weren't all that (i.e. no warnings surfaced), it took me a little bit of time to spot my mistake.
(the code does compile by the way)
Delete