|Pathologically Eclectic Rubbish Lister|
Code Maintainability is a hot topic: I've been mulling over the issue(s) of code maintainability and code density for several years, and I thought it might be useful to lay out some of my conclusions.
I've written and maintained code (mine and others') for several years, professionally and for my own personal use. I've worked in Fortran, Python, Scheme, Perl, Java, and Ruby; to name just a few. I made the most money with Java, but I had the most fun with Scheme. Perl comes in a close second on the fun scale. Sometimes I've known the people whose code I've maintained, sometimes I haven't.
The problem of subjectivity
Code maintainability is fairly subjective. What one person considers maintainable, another considers bloated and a third considers cryptic. This suggests at the outset that "maintainability" can never be a true standard, and it's probably an elusive goal. It might be a worthy goal, but I'd suggest maintainability is really best achieved when it's not the primary aim. In other words, improving the clarity and brevity of code will result in much more maintainability than attempting to make it maintainable.
The bottom-line problem from my perspective is, coders have been told to write maintainable code, and they've been reminded that they can't tell who's going to have to maintain it. Thus, they have largely opted to write code as simply as possible: code that's inefficient and bloated, "but easy to understand". What they actually produce is a brain-dead code no intelligent maintainer wants to touch... code so ugly the maintainers conclude the original developers were idiots.
It's a double-blind scenario: the coder and subsequent maintainers all assume the worst about one another, and allow that to tempt them to write code that's uglier and less maintainable than their initial instinct would have been to write. Thus, the lofty ideal of "maintainability" is used to excuse all manner of coding abominations.
Bloated code is ugly
I have seen a lot of code in the Java world that looks like this:
This is considered good style by many javanistas, but working in code bases like this will rot your soul. It's not only bloated, it's ugly. This would be much more beautiful (or at least less ugly):
But if you suggest that to people who write this stuff, they'll make noises about how unmaintainable the second example is, because convenience constructors are hard to understand. Are they? If your maintainer has trouble understanding a convenience constructor, you need to look for a new maintainer and a new hiring manager.
See, verbose code is not only bloated: it's ugly. And ugly code is harder to maintain: it's harder to parse, harder to care about, and harder to force yourself to work on. Maintainability is subjective, so let me make a subjective point: ugly code makes me want to find another task to do. Ugly code is too great a temptation to procrastination.
Obfuscated code is ugly
Obfuscated code is impressive, but it's ugly. Seeing how closely you can make your Perl resemble line noise is interesting---and I greatly admire some of the monks solely for their ability to make random characters do cool stuff---but it has no place in code you expect others to run.
Try it: try to maintain some of the obfuscated code on this site. You'll throw in the towel in no time, partly because it's ugly. Maybe even mainly because it's ugly.
Perl people have a reputation for writing ugly code, and it's well-deserved. But our fascination with seeing how ungodly a mess the Perl interpreter can unravel has actually damaged our cause. Nobody wants to work on code that looks like a comic character cursing. You don't, I don't. Not for real, not for a living.
Good comments don't make up for bad code.
I'm becoming convinced that comments in code are close to useless. The first and greatest problem with comments is that they don't alter the code's behaviour. When your code's wrong, you can tell from its (wrong) behaviour. But when your comments are wrong, you can only tell by comparing them to the code. And if you're parsing the code to test the comments, then the comments are basically worthless: you're having to read the code as though they aren't there anyway.
This is perhaps not much of a problem unless the code has changed and the comments have not (which happens all the time), but how can you tell that's not what happened? In the real world, a maintainer has no choice but to assume the comments are wrong. Trusting comments is a very risky course of action: risky enough that I'm almost convinced of the sed -e '/^(\s+)?#/d' technique others espouse.
According to Tilton: "Every comment is a place the code could be better."
Of course comments are good for some things: as a maintainer I find it's easy to figure out what code does, and hard to figure out what it's supposed to do. I've more than once asked the original programmer what he was trying to accomplish: I find the context for a piece of code is what's more often lacking. I love to see comments that say things like:
I know what the code does, my concern is figuring out why it does it. That's precisely where comments should be used.
Comments that simply narrate the code are close to useless. Comments that explain the rationale of some code can be very helpful. In my experience, there is much more of the former going on than the latter.
Beautiful code is more maintainable
Beautiful code is generally easy to maintain. I find the best Perl reads a whole lot like English: the closer to English prose you can get your Perl, the easier it is for the next person to maintain it. When you accomplish prose-like Perl, you essentially write comments that are your code. If your comments are your code, and your code is your comments; maintenance is a snap.
Perl has several features that make prose-like code easier. One of my favourites is unless. if not is ugly in any language: unless makes that flow so much better.
I also prefer to test predicates at the end of a line: it makes it read more like a conversation. For example:
is pretty clear, but if we re-write that one line, it becomes more of a conversation with the maintainer:
Notice how well it flows in English: do this if that is very much what we say to one another all day in the real world. If this that is very much how one expects a computer to talk, not a real person.
But being prose-like is not the only test of beauty. Accomplishing a lot with very few well-chosen commands is elegant: that's an important part of beauty. Using well-named variables is very beautiful. Initialising variables when they are declared is much more aesthetic than declaring something and using it later. Using constants to represent constants and variables to represent varaiables: that's graceful. Carefully building scopes to subtly influence the meaning of code: that's just flat-out gorgeous.
An aesthetic sense of code accomplishes more than self-flagellation over maintainability. Code you're proud of, code that is genuinely appealing, is code your maintainer will enjoy working on. That's really the defining characterstic of maintainability.
Brevity and tact are the soul of beauty
All other things being equal, shorter code is better. I was discussing a piece of legacy Perl with a co-worker the other day, who was defending it's length and incredible verbosity on the grounds that "it should only do one thing per line". That's a cute theory, but it's a real pain to try and maintain long pieces of code. If you take 15 lines to say what can be said in 10, then you've squandered 5 lines' worth of screen real estate. More lines of code take more screen real estate, require more time to read, and are harder to scroll through. Obviously there is such thing as code that's too dense, but fluffy, airy, light-as-a-feather density code is agony for a competent maintainer. Don't write something like this:
when you can use something like:
Of course, this is even better, if it's appropriate for your purposes:
Always look for a shorter way to express the same behaviour. Every line you add gratuitously is a line your maintainer has to read, parse, understand, and potentially alter. If you're adding them for some obscure, esoteric ethic; then you're making your code less maintainable.
Is there such a thing as too short? Yes, there is... but my experience has been that my skill level generally grows to catch up with dense code: my frustration level rarely diminishes with bloat.
A certain competence level must be assumed for the maintainer.
I find it insulting as a frequent code maintainer, that people equate "maintainable" with "obvious and basic, even for a total idiot." Don't assume the maintainer is not as smart as you are: write code you want to maintain, and leave it at that. If the maintainer can't read decent code, then let Natural Selection take its course: eventually someone competent will touch your code, and they'll thank you for not making them rewrite it all.
It's ironic that the quest for "maintainability" has prompted a lot of people to write code that's so idiot-proof it's hard to read. There is a point of diminishing returns in code clarity efforts: and there's a point where the coder's effort to write "maintainable" code will actually make it much worse for the poor schmuck who has to actually maintain it.
I've learned a lot of Perl from maintaining other people's code. Code that makes the maintainer reach for the Camel now and then is not a bad thing. A decent maintainer will thank you for it.
I think part of the solution can be summed up in one phrase: What Would Howard Roark Do? Roark would rather starve than design an "ugly" building. If you apply that idea to your code, you'll find it's much more maintainable than if you'd set out to make it so. Write code you can be proud of, code you want to email to your friends to boast about. That's code that will prove to be maintainable in the long run. If your friends look at it and say "wow," then it's not hard to read, and your maintainer won't have trouble either.
Write code that's short, elegant, and beautiful. Write code that uses more of Perl's vast vocabulary than a few loop constructs and some declarations. Try to assemble collections when you declare them (using map and grep), rather than declaring a variable without initiaizing it. Uninitialized variables are... well, ugly.
Look at return values, and don't call an operator solely for its side-effects when it's designed to return a value. Think in terms of immutable variables: they're implicitly thread-safe and easier to debug.
Learn the language. Don't be satisfied with knowing enough Perl to get by. Everyone has to start there, but that's not a good excuse for not developing further. Perl is a vast language with a zillion keywords and unimaginable number of permutations of solutions. Don't stop learning: use every Perl problem as an opportunity to learn just a little more Perl. It took me years to get around to learning map, and now I can't imagine writing Perl without it. Who knows what cool Perl features are waiting for me to discover them? A wider vocabulary means more beautiful code: that's more maintainable code.