Browse in : |
All
> Topics
> Programming
All > Journal Columns > Professionalism All > Journals > CVu > 295 Any of these categories - All of these categories |
Note: when you create a new publication type, the articles module will automatically use the templates user-display-[publicationtype].xt and user-summary-[publicationtype].xt. If those templates do not exist when you try to preview or display a new article, you'll get this warning :-) Please place your own templates in themes/yourtheme/modules/articles . The templates will get the extension .xt there.
Title: Code Aesthetics
Author: Bob Schmidt
Date: 10 November 2017 17:18:32 +00:00 or Fri, 10 November 2017 17:18:32 +00:00
Summary: Pete Goodliffe implores us to care (enough) about code beauty.
Body:
Appearances are deceptive.
~ Aesop
Code aesthetics are the most immediate determinant of how easy a section of code will be to work with. No one likes working with messy code. No one wants to wallow in a mire of jagged, inconsistent formatting, or battle with gibberish names. It’s not fun. It’s not productive. It’s the programmer’s purgatory.
Sadly, programmers care so much about code presentation that they end up bickering about it. This is the stuff that holy wars are made of. That, and which editor is best [1]. Tabs versus spaces. Brace positioning. Columns per line. Capitalisation. I’ve got my preferences. You have yours.
Godwin’s law states that as any discussion on the Internet grows longer, the probability of a comparison to the Nazis or Hitler approaches one. Goodliffe’s law (unveiled here) states that as any discussion about code layout grows, the probability of it descending into a fruitless argument approaches one.
Good programmers care deeply about good code presentation. But they rise above this kind of petty squabble. Let’s act like grown-ups.
Stop fighting over code layout. Adopt a healthy attitude to your code presentation.
Our myopic focus on layout is most clearly illustrated by the classic dysfunctional code review. When given a section of code, the tendency is to pick myriad holes in the presentation. (Especially if you only give it a cursory skim-read, then layout is all you’ll pick up on.) You feel like you’ve made many useful comments. The design flaws will be completely overlooked because the position of a bracket is wrong. Indeed, it seems that the larger the code review, and the faster it’s done, the more likely this blindness will strike.
Presentation is powerful
We can’t pretend that code formatting is unimportant. But understand why it matters. A good code format is not the one you think looks prettiest. We do not lay out code in order to exercise our deep artistic leanings. (Can you hear the code-art critics? Daaaahling, look at the wonderful Pre-Raphaelite framing on that nested switch statement. Or: you have to appreciate the poignant subtext of this method. I think not.)
Good code is clear. It is consistent. The layout is almost invisible. Good presentation does not draw attention or distract; it serves only to reveal the code’s intent. This helps programmers work with the code effectively. It reduces the effort required to maintain the code.
Good code presentation reveals your code’s intent. It is not an artistic endeavour.
Good presentation techniques are important, not for beauty’s sake, but to avoid mistakes in your code. As an example, consider the following C snippet:
bool ok = thisCouldGoWrong(); if (!ok) fprintf(stderr, "Error: exiting...\n"); exit(0);
You can see what the author intended here: ++exit(0)++
was only to be called when the test failed. But the presentation has hidden the real behaviour: the code will always ++exit++
. The layout choices have made the code a liability. [2]
Names have a similarly profound effect. Bad naming can be more than just distracting, it can be downright dangerous. Which of these is the bad name?
bool numberOfGreenWidgets; string name; void turnGreen();
The numberOfGreenWidgets
is a variable, right? Clearly a counter is not represented by a boolean type. No; it’s a trick question. They’re all bad. The string does not actually hold a name, but the name of a colour; it is set by the turnGreen()
function. So that variable name is misleading. And turnGreen
was implemented thus:
void turnGreen() { name = "yellow"; }
The names are all lies!
Is this a contrived example? Perhaps; but after a little careless maintenance, code can quickly end up in this state. What happens when you work with code like this? Bugs. Many, many bugs.
We need good presentation to avoid making code errors. Not so we can create pretty ASCII art.
Encountering inconsistent layout and hodgepodge naming is a sure sign that code quality is not high. If the authors haven’t looked after the layout, then they’ve probably taken no care over other vital quality issues (like good design, thorough testing, etc.).
It’s about communication
We write code for two audiences. First: for the compiler (or the language runtime). This beast is perfectly happy to read any old code slop and will turn it into an executable program the only way it knows how. It will impassionately do this without passing judgment on the quality of what you’ve fed it, nor on the style it was presented in. This is more a conversion exercise than any kind of code ‘reading’.
The other, more important, audience is other programmers. We write code to be executed by a computer, but to be read by humans. This means:
- You, right now, as you’re writing it. The code has to be crystal clear so you don’t make implementation mistakes.
- You, a few weeks (or months) later as you prepare the software for release.
- The other people on your team who have to integrate their work with this code.
- The maintenance programmer (which could be you or another programmer) years later, when investigating a bug in an old release.
Code that is hard to read is hard to work with. This is why we strive for clear, sympathetic, supporting presentation.
Remember who you’re writing code for: other people.
We’ve already seen that code can look pretty but obscure its intent. It can also look pretty, but be unreasonably hard to maintain. A great example of this is the ‘comment box’. Some programmers like to present banner comments in pretty ASCII-art boxes:
/************************************************ * This is a pretty comment. * * Note that there are asterisks on the right- * * hand side of the box. Wow; it looks neat. * * Hope I never have to fix this tiypo. * **************************************************/
It’s cute, but it’s not easy to maintain. If you want to change the comment text, you’ll have to manually rework the right-hand line of comment markers. Frankly, this is a sadistic presentation style, and the people who choose it do not value the time and sanity of their colleagues. (Or they hope to make it so crushingly tedious to edit their comments that no one dare adjust their prose.)
Layout
Code layout concerns include indentation, use of whitespace around operators, capitalisation, brace placement (be it K&R style, Allman, Whitesmith, or the like), and the age-old tabs versus spaces indent debate. In each of these areas there are a number of layout decisions you can make, and each choice has good reasons to commend it. As long as your layout choices enhance the structure of your code and help to reveal the intent, then they’re good.
A quick glance at your code should reveal the shape and structure. Rather than argue about brace positioning, there are more important layout considerations, which we’ll explore in the following sections.
Structure well
Write your code like you write prose.
Break it up into chapters, paragraphs, and sentences. Bind the like things together; separate the different things. Functions are akin to chapters. Within each chapter may be a few distinct but related parts of code. Break them up into paragraphs by inserting blank lines between them. Do not insert blank lines unless there is a natural ‘paragraph’ break. This technique helps to emphasise flow and structure.
For example:
void exampleFunction(int param) { // We group things related to input param = sanitiseParamValue(param); doSomethingWithParam(param); // In a separate "paragraph" comes other work updateInternalInvariants(); notifyOthersOfChange(); }
The order of code revelation is important. Consider the reader: put the most important information first, not last. Ensure APIs read in a sensible order. Put what a reader cares about at the top of your class definition. That is, all public information comes before private information. Creation of an object comes before use of an object.
This grouping might be expressed in a class declaration like the one in Listing 1.
class Example { public: Example(); // lifetime management first ~Example(); void doMostImportantThing(); // this starts a new "paragraph" void doSomethingRelated(); // each line is like a sentence void somethingDifferent(); // this is another paragraph void aRelatedThing(); private: int privateStuffComesLast; }; |
Listing 1 |
Prefer to write shorter code blocks. Don’t write one function with five ‘paragraphs’. Consider splitting this up into five functions, each with a well-chosen name.
Consistency
Avoid being precious about layout styles. Pick one. Use it consistently. It is best to be idiomatic – use what fits best with your language. Follow the style of standard libraries.
Write code using the same layout conventions as the rest of your team. Don’t use your own style because you think it’s prettier or better. If there is no consistency on your project then consider adopting a coding standard or style guide. This does not need to be a lengthy, draconian document; just a few agreed upon layout princples to pull the team together will suffice. In this situation, coding standards must be agreed on mutually, not enforced.
If you’re working in a file that doesn’t follow the layout conventions of the rest of your project, follow the layout conventions in that file.
Ensure that the entire team’s IDEs and source code editors are configured the same way. Get the tab stop size the same. Set the brace position and comment layout options identically. Make the line ending options match. This is particularly important on cross-platform projects where very different development environments are used simultaneously. If you aren’t diligent in this, then the source code will naturally become fractured and inconsistent; you will breed bad code.
Names
We name many things: variables, functions and methods, types (e.g., enumerations, classes), namespaces, and packages. Equally important are larger things, like files, projects, and programs. Public APIs (e.g., library interfaces or web service APIs) are perhaps the most significant things we choose names for, as ‘released’ public APIs are most often set in stone and particularly hard to change.
A name conveys the identity of an object; it describes the thing, indicates its behaviour and intended use. A misnamed variable can be very confusing. A good name is descriptive, correct, and idiomatic.
You can only name something when you know exactly what it is. If you can’t describe it clearly, or don’t know what it will be used for, you simply can’t name it well.
Avoid redundancy
When naming, avoid redundancy and exploit context. Consider:
class WidgetList { public int numberOfWidgets() { ... } };
The numberOfWidgets
method name is unnecessarily long, repeating the word Widget. This makes the code harder, and more tedious, to read. Because this method returns the size of the list, it can simply be called size()
. There will be no confusion, as the context of the enclosing class clearly defines what size
means in this case.
Avoid redundant words.
I once worked on a project with a class called DataObject
. That was a masterpiece of baffling, redundant naming.
Be clear
Favour clarity over brevity. Names don’t need to be short to save you key presses – you’ll read the variable name far more times than you’ll type it. But there is, however, a case for single-letter variable names: as counter variables in short loops, they tend to read clearly. Again, context matters!
Names don’t need to be cryptic. The poster child for this is Hungarian Notation. It’s not useful.
Baroque acronyms or ‘amusing’ plays on words are not helpful.
Be idiomatic
Prefer idiomatic names. Employ the capitalisation conventions most often used in your language. These are powerful conventions that you should only break with good reason. For example:
- In C, macros are usually given uppercase names.
- Capitalised names often denote types (e.g., a class), where uncapitalised names are reserved for methods and variables. This can be such a universally accepted idiom that breaking it will render your code confusing.
Be accurate
Ensure that your names are accurate. Don’t call a type WidgetSet
if it behaves like an array of widgets. The inaccurate name may cause the reader to make invalid assumptions about the behaviour or characteristics of the type.
Making yourself presentable
We come across badly formatted code all the time. Be careful how you work with it.
If you must ‘tidy it up’ never alter presentation at the same time as making functional changes. Check in the presentation change to source control as a separate step. Then alter the code’s behaviour. It’s confusing to see commits mixing the two things. The layout changes might mask mistakes in the functionality.
Never alter presentation and behaviour at the same time. Make them separate version-controlled changes.
Don’t feel you have to pick a layout style and stick with it faithfully for your entire life. Continually gather feedback from how layout choices affect how you work with code. Learn from the code you read. Adapt your presentation style as you gain experience.
Over my career, I have slowly migrated my coding style, moving ever towards a more consistent and easier to modify layout.
From time to time, every project considers running automated layout tools over the source tree, or adding them as a pre-commit hook. This is always worth investigating, and rarely worth using. Such layout tools tend to be (understandably) simplistic, and are never able to deal with the subtleties of code structure in the real world.
Conclusion
Stop fighting about code presentation. Favour a common convention in your project, even if it’s not your personal preferred layout style.
But do have an informed opinion on what constitutes a good layout style, and why. Continually learn and gain more experience from reading other code.
Strive for consistency and clarity in your code layout.
Questions
- Should you alter layout of legacy code to match the company coding standard? Or is it better to leave it in the author’s original style? Why?
- Which is more important: good code presentation or good code design?
- How consistent is your current project’s code? How can you improve this?
- Tabs or spaces? Why? Does it matter?
- Is it important to follow a language’s layout and naming conventions? Or is it useful to adopt a different ‘house style’ so you can differentiate your application code from the standard library?
- Does our use of colourful syntax-highlighting code editors mean that there is less requirement for certain presentation concerns because the colour helps to reveal code structure?
Notes
[2] This is not just an academic example to fill books! Serious real-life bugs stem from these kinds of mistakes. Apple’s infamous 2014 goto fail security vulnerability in its SSL/TLS implementation was caused by exactly this kind of layout error.
Notes:
More fields may be available via dynamicdata ..