At Reactuate Software one of our main goals when creating software is making the code readable. Readable code makes everyone’s life easier. It is less prone to mistakes. It’s easier for new developers to learn. It’s easier for a developer to understand when they go back to code they wrote a long time ago.
Here’s how I do it. These practices were developed over decades of coding, and are heavily influenced by Objective-C/Cocoa conventions. Some may actually vary based on the language we’re coding in, but the principles apply. For example, CamelCase is the norm in Objective-C, but frowned on in Python. So if you see inTagArray below in Objective-C, that would be in_tag_array in Python.
Full Words Please
[box type=”warning”]Single letter variables are of the devil.
Never use them.[/box]
About the only place I accept a single letter variable is a simple loop. ‘for( int i=0;i<10;i++)' is pretty understandable1, but actually getting to be rare in the real world. Single letter loop variables can get confusing quickly if you have loops inside of loops.
Another exception are the special x,y,z variables when plotting points. Those are the actual names of the axis you are referencing, so go ahead and use them. Which is a also a good reason not to use X in other places.
[box type=”warning”]No abbreviations or acronyms either
They may be obvious to you today, but not so for other readers of the code. I don’t know why coder’s feel they have to save a few key strokes by making variables abbreviations, especially in the age of editors with autocompletion.[/box]
The other day I was looking at a tutorial and the writer’s sample started with something like this:
[cc lang=”objc”]NSString* rhs = @”abcd”;
NSString* lhs = @”efg”;
if ( [rhs isEqualString:lhs] )[/cc]
It’s pretty easy to understand what the code is doing, comparing two strings. But what does rhs/lhs mean? The sample code was discussing how lhs == rhs is done with string routines. Then I figure it out. They meant Right Hand Side, and Left Hand Side. OK, why not name them that? RightHandSide would be perfectly valid and more readable.
Actually I would probably have named them rightString and leftString. This tells what they do/mean, and it tells me their type. Which brings me to my first style guide.
Always Name The Variable By What It Represents
Needless to say…well I guess it’s not needless to say or I wouldn’t be writing this.
Give variables meaningful names. Later in the code you are only going to have the name when reading code.
I also like to include the type in the name, though it isn’t always needed. Like the above example, adding String to the end let’s the reader know the variable holds a string and not a number. Yeah, you could look back at the comment on the definition if you get confused, but why not make the name self documenting?
When I name my outlets for UI objects in iOS/MacOS, I name them with the type of object they are, for example usernameTextField, and passwordTextField. Because later I may see those variables, and while I know they handle text because that’s what a username is, knowing it is a text field tells me a lot more about that text. It’s a single line not multiple lines. It isn’t rich text. It’s probably editable or I’d have labeled it usernameLabel.
Windows programmers have long used Hungarian notation where the first characters or characters represent the type of the variable. I used to think this was dumb because you had to learn a new convention when you could just look at the definition. I still think having to learn a cryptic set of rules is a pain, but having the type in the name is very useful.
Meaning Can Be Relevant to Role in Code
[box type=”info”]Never use any variable for more than one thing.
Variables don’t cost you anything but keystrokes. If you want to do something else, make a new variable, don’t reuse one you think is “done”. This is asking for disaster.[/box]
Some variables don’t have a global meaning. For instance a variable that is just there to hold a value while you do something to it. If they are really, really temporary, I’ll just name it ‘temp’. But if they are only kind of temporary, meaning they stay around for more than a couple of lines, or there are more than one temporary variable, then I’ll name them based more on what they hold. tempString, or tempSortValue, etc.
Another special naming convention I use is in and out prefixes on parameters. Parameters of a method/function/procedure always get prefixed with ‘in’. If they are being used to send some value back to the caller via reference or pointer, they get prefixed with ‘out’.
Why? Because you should never alter parameters that are passed in. In C++ you could define these as const and the compiler wouldn’t let you change them, but not in Objective-C. If you need to change something in an ‘in’ variable, make a copy. And think about what it means to make a copy.
Here’s an example method:
[cc lang=”objc”]- (void)addTagsFromArray:(NSArray*)inTagArray;
for (NSString* curString in inTagArray)
The routine adds an array of strings to an object’s tag list. The parameter sent in is named inTagArray. ‘in’ because it is a parameter, ‘Tag’ because that’s what it contains, ‘Array’ because that’s its type.
The addTagsFromArray method shows how to handle an enumerator variable. Prefix the variable that contains the current variable with ‘cur’. The variable probably should have been ‘curTag’, since it contains a tag, and a tag just happens to be a String. But in this case it works because in the future I may change a tag to be something other than a string, and when I do I’ll rename the variable to indicate it contains a Tag object.
When I write a new method that is going to return something, the first code I write will look like this:
[cc lang=”objc”]- (int)someRoutine;
int returnInt = 0;
I learned this from the book Code Complete: A Practical Handbook of Software Construction, which is a great resource to learn about writing tactically defensive code.
I’m doing three things here.
1) I’m declaring a value that will hold what’s being returned. If that value isn’t set, the routine shouldn’t be returning yet. When you’ve figured out what to return, this is where it goes.
2) I’m giving the return value a useful default value. If an unset return is a error, then set the error value here. For an int, you probably want to initialize to zero just to be sure.
3) I’m actually doing the return. First this will get rid of a compiler warning that you don’t have a return value. Second, you’ll always return something, which is good, because just exiting a routine that expects a return value is undefined.
There are rare occasions when readability has to take a back seat to performance, and sometimes what the code does is just too complex to easily understand at a glance. But the readability of a variable name never impacts performance. Variable names are for the programmer, not the compiler.
- Always give variables meaningful names.
- Always use complete words. No abbreviations.
- Include variable type in the name.
- Parameter names are prefixed with ‘in’ or out ‘out’.
- Create a variable prefixed with ‘return’ and return statement when creating a new routine.
- Of course 10 is a “magic number” which will be the topic of another post.↩