Home

Elegance in programming

[Click to print this article]

Helmsley Castle, N. Yorks

Over the next couple of weeks, a small team of us will be going through a subsystem in our product to clean it up and to harden it against exceptional situations. It currently works, but when something goes wrong, it can fail in mysterious ways. We're also going to take the opportunity to do some much-needed profiling for speed and memory. The original subsystem was written in C# back in the day when the company hadn't much experience in all things .NET and I'm afraid it shows.

In our preparatory work, we've already uncovered (and fixed) a number of issues, including one in some code that caught (and swallowed) all exceptions in a catch block (don't do this at home, kids!). It turned out that there was a null reference exception that was always thrown in this section of code, but no one ever knew. It had never been spotted because it was in some logging code: the logger was repeatedly trying to log an exception, failing, raising another exception for the error (null reference), and failing to log that. At that point the logger prudently gave up, otherwise we'd have certainly noticed the infinite exception recursion.

This got me thinking. There were undoubtedly many reasons for this bug to be present in this code for so long, but possibly one of them was the complexity and length of the method containing it. It has many local variables; it does several things, not just one; and it has many conditional statements and loops. The McCabe complexity metric is in the high twenties.

In looking at the code now, I feel that the original developers were being overly pragmatic. They had to get the logging code written, the schedule crunch at the time meant that they didn't have time to design it properly, or even to test it thoroughly. They just had to get the code written and it seemed to work ("look, entries in the log file!"). In those situations, and I've been there before, you tend to write longer, more complex methods (the "I'll refactor it when it works" methodology, except you don't).

From my viewpoint, with 20-20 hindsight, at a distance of some 18 months, this seems to be a victory of pragmatism over elegance. The developers were good (one of them has been working for the company nearly as long as it has been around) and have proven themselves over and over. They know what's good code and what's not.

To me, good code implies elegant code, not just code that works and works efficiently. There is a certain aesthetic quality to good code. The classes are named well, describing their purpose, and they have a single responsibility. Methods are short and easy to read and understand, the code is the minimum required to achieve the functionality needed. There's no dodgy-looking code where you're not quite sure why it's been written the way it looks. There's no bad inheritance models; there's no empty constructors that cause the maintainer to worry about possible missing code; there's no setters where you wonder what it could mean to alter the value of this property.

Pete McBreen, in Software Craftsmanship: The New Imperative, proposes that software development can be done better by following a traditional craft or guild model: a software craftsman to direct and a team of apprentices under him who are collaborating and mastering the craft of writing software. It's a good book. But to me, the central idea that's embodied by the word Craftsman is beauty and elegance. I have this vision of a master carpenter crafting this jewelry box out of hardwoods where you don't even notice the mortise and tenon joints, it's so well done. Craftsman don't build Ford trucks on an assembly line, they make $4000 suits; they don't paint by numbers, they write requiems. They fashion beauty out of raw materials.

Don't get me wrong, I'm not talking about élitism. Writing elegant code is not élitist, it's just common sense. 40% to 60% of the cost of a particular piece of software over its lifetime is paid in maintenance dollars (source: Robert Glass, Facts and Fallacies of Software Engineering). Elegant code is more flexible and less brittle; more readable and less obscure; more likely to work correctly in a consistent manner and less likely to contain bugs.

Reading elegant code is like reading a well-written novel. Badly-written code is like one of Dan Brown's novels: it works and sweeps you along in its wake, but the sentence construction is jarring, the characterization is one-dimensional, and the plot laughable. But a good novel? Wow. Suddenly you care about the characters, they seem fully-fleshed out: you could meet them on the street tomorrow. Their travails are your problems too. The writing is smooth and uncluttered: each word seems perfectly chosen, the grammar and sentence construction transparent. You get the impression that the author cares about the novel and the language he uses for it, that it's not just a hack written over three or four months.

Just as I love reading a good well-written novel, I enjoy reading good, well-written code. Just as I aspire to write prose well (and I think I've got better at it over the years, but I'm definitely not there yet), I aspire to write great code. Code that is elegant.