My preferred code style is 2-space K&R. This is intended to provide a justification for this style.
K&R style has the following properties:
- Provides symmetric size (in terms of screen space consumed) between the opening and closing syntax of a clode block.
- Forces no empty or meaningless lines, thereby avoiding artificial distance between related things that should be together.
- Consumes the minimum vertical space while keeping the opening and closing syntax of a block on separate lines from the content.
Symmetry in size is important because it provides a visual cue that two things are similar in effect/importance.
For example, in written text, lines of a paragraph are the same space apart. The space between paragraphs is larger, but also the same between each. The space between subsections, sections and chapters are progressively larger still. The space between things indicates their relatedness, the size of the headings indicate how large of a topic change they introduce. All line spaces are the same, all paragraph breaks are the same size, all chapter headings sizes are the same, etc.
Two subsequent statements in a program are related by one happening after the other, i.e. one directly inheriting the state of the previous. Within a control structure (if
, for
etc), the first statement inside the body and the statement before the structure are less related than two sequential statements, and so the introducing line of the control structure increases the space between them, and specifies the different nature of their relationship (conditional, loop etc).
The finishing line of a control structure has the opposite, but equivalent role. The opener transitions fully into a block and the closer transitions fully out. Their real effect on the meaning of the code above and below each of them is of the same magnitude but in opposite directions.
In K&R, the space the opener and closer of a control structure occupy on the screen is equivalent (one line) to reflect their equivalent importance in terms of effect on the meaning of the code above and below each.
Here is a comparison of brace styles found on Wikipedia:
Notice the consistency that results between a stand-alone C (or a C-style language) block,
{
foo();
bar();
}
an if
block in C (or a C-style language) (K&R style),
if (x == y) {
foo();
bar();
}
a PHP block-style if
block,
if (x == y):
foo();
bar();
endif;
a Ruby if
block,
if x == y
foo()
bar()
end
an if
block in the Fish shell,
if x == y
foo
bar
end
some HTML/XML,
<ul style="...">
<li>foo</li>
<li>bar</li>
</ul>
and an array, object/struct or dictionary literal (JSON, JavaScript, Python, PHP, Ruby etc).
var things = [
'foo',
'bar',
];
var things = {
foo: 'foo',
bar: 'bar',
};
All of these follow a simple pattern:
(introducer)
(entries)
(finisher)
One line to open, one line to close.
The visual relationship between the opener of the block and it's contents is also the same as in languages which use the off-side rule, such as CoffeeScript, YAML, Python, Haskell and SASS:
if foo():
bar()
baz()
boo()
Except for the lack of a finishing line, since a dedicated closer is not needed (the block is closed by returning to the previous indent).
It is also the same as a control structure lacking braces, if permitted by your code style:
if (foo())
bar();
if (foo()) {
bar();
}
This means that the presence or absence of braces has a less dramatic effect on the code's layout. Compare these code samples, for example:
function typeName(obj) {
if (obj.isFoo())
return 'foo';
else if (obj.isBar())
return 'bar';
else if (obj.isBoo()) {
log_notice('Cannot get name of a boo');
return null;
} else
throw new Error();
}
function typeName(obj)
{
if (obj.isFoo())
return 'foo';
else if (obj.isBar())
return 'bar';
else if (obj.isBoo())
{
log_notice('Cannot get name of a boo');
return null;
}
else
throw new Error();
}
In the Allman case, the simple fact that the obj.isBoo()
branch needs two statements instead of one has caused the number of lines for the body of that branch to be four times taller than the others.
Code in K&R style has no artificial meaningless lines enforced by the style. Each line tells the reader something they wouldn't otherwise know, and they can therefore progress from line to line next gathering new information at each. Blank lines are not bad, since they are useful to group related lines together and seperate unrelated lines, and so hitting a blank line is a signpost, like a paragraph or section break in a book, that the old topic ends and a new topic starts, but whether a blank line is applicable in any given context is entirely dependant on that context, and so cannot be decided by the coding style. Under K&R, every blank line has a purpose, decided by the programmer, based on the context, to convey some informaiton about the relatedless of each line.
Here is an example of the consequence of meaningless lines being enforced by the Allman style:
$foos = array();
foreach (getFoos() as $foo)
{
$foos[] = transformFoo($foo);
}
Here, a blank line separates the initialisation of $foos
and the loop header from the loop body. The blank line implies that they are unrelated, when in fact they are. The whole set of code belongs to a single topic of "array of transformed $foo
s", but the Allman style has artificially inserted a topic break in the middle of this.
Under K&R, the whole code block can be properly treated visually as a single topic:
$foos = array();
foreach (getFoos() as $foo) {
$foos[] = transformFoo($foo);
}
K&R style minimises the amount of vertical space which the code consumes, while maintaining that the syntax of the control structure itself does not share lines with its contents.
Ensuring a control structure doesn't share lines with its content is important. Lines are the "boxes" or categories in which related syntax goes. In the case of a code block, all the syntax related to a given statement goes on the same line. This not only helps visual comprehension, but means line-wise operations are meaningful (triple-click select line, delete line, cut line, duplicate line, source code diff etc). With the Lisp-style bracing, for example, the last brace on the same line as the last statement means that the "delete line" operation cannot be used to delete the last statement, and adding a statement to the end of the block creates a "-1 lines +2 lines" diff instead of a "+1 lines" diff.
Ensuring the code minimises the amount of vertical space is important for information density, i.e. the total amount of information that is readily available, per unit of screen space. Minimising vertical space (lines) used helps the reader to get a "birds eye" view of the code without using a smaller font and without removing the indents and purposeful blank lines that give the code a visual structure. The more code that can be fit on screen without compromising its visual structure, the more readily the code can be read.
Consider you have two statements:
foo();
bar();
and you want to wrap them in a if
block wrapped by a for
loop. The K&R result is:
for (...; ...; ...) {
if (...) {
foo();
bar();
}
}
While the Allman result is:
for (...; ...; ...)
{
if (...)
{
foo();
bar();
}
}
Under K&R, the overhead in consumed lines for each control structure is 2. In Allman the overhead is 3, i.e. a 50% higher cost in vertical space.
This is a survey of well known software projects and companies and their chosen brace and indent style.
Note that some codebases use K&R style for control structures but Allman style for classes and functions. These have been grouped under "K&R".
Company/Project | Brace Style | Indent Type | Indent Size | Reference |
---|---|---|---|---|
K&R | spaces | 2 | Google C++ style guide, Google Java style guide | |
V8 (JavaScript engine) (Google) | K&R | spaces | 2 | code |
HHVM (Facebook) | K&R | spaces | 2 | HHVM guidelines, code |
Proxygen (Facebook) | K&R | spaces | 2 | code |
Phabricator (Facebook) | K&R | spaces | 2 | Phabricator coding standards |
.NET CLR (Microsoft) | Allman | spaces | 4 | code |
C# Guidelines (Microsoft) | Allman | spaces | 4 | link |
TypeScript (Microsoft) | K&R | spaces | 4 | code |
Visual Studio Code (Microsoft) | K&R | tabs | ? | code |
Intel TBB | K&R | spaces | 4 | code |
Sun Java JRE/JDK (Oracle) | K&R | spaces | 2 | code |
Linux Kernel | K&R | tabs | 8 | Coding Style |
IntelliJ (JetBrains) | K&R | spaces | 2 | code |
Nginx | K&R | spaces | 4 | code |
LLVM (Apple/Google) | K&R | spaces | 2 | code |
JavaScriptCore (Apple) | K&R | spaces | 4 | code |
WebCore (Apple) | K&R | spaces | 4 | code |
SystemD (RedHat) | K&R | spaces | 8 | code |
KDE | K&R | spaces | 4 | code |
GNOME | GNU, K&R | mixed | 2, 4 | C code, JS code, Vala code |
RequireJS | K&R | spaces | 4 | code |
Apache | K&R | spaces | 4 | code |
Firefox | K&R | spaces | 2 | code |
Chromium (Google) | K&R | spaces | 2 | code |
LibreOffice | Allman | spaces | 4 | code |
PHP | K&R | tabs | ? | code, code 2 |
Vim | Allman | mixed | 4 | code, code 2 |
Git | K&R | tabs | ? | CodingGuidelines, code |
Why does what other projects do matter? Only for familiarity. Switching between code written in different styles can be jarring. With most code having been written in K&R, the code you write will feel more familiar to others and others' code will feel more familiar to you by sharing the style.
- The good thing about tabs is that each person can configure their tools to render tabs using their preferred size.
- The bad thing about tabs is that each person must configure their tools to render tabs using their preferred size.
The requirement of #2, that all software displaying the code be configured to have the correct tab size, is at best inconvenient, at worst impossible. The problem is there is a large and diverse range of software that will be involved in displaying your code, including:
- Debuggers (
gdb
,lldb
,hphpd
, Firefox/Chrome JavaScript debugger...) - Error reporting systems (Bugsnag, Rollbar, FailWhale, Whoops!...)
- Source code browsers (GitHub, Bitbucket, Upsource...)
- Code review tools (GitHub, Bitbucket, Upsource, Crucible...)
- Diff tools (
git diff
, GitHub app, Meld, TortoiseGit, KDiff...) - Editors (including both IDEs and simple text editors (Vim, Gedit...) used to quicktly view files and make small changes)
Not to mention code samples that are put in emails, chat messages, code review/bug tracker issues/comments, blog posts and presentation slides.
Each tool will have its own default display size for a tab (usually 8) and each tool may or may not let you change it. GitHub, for example, renders tabs with 8 spaces, which can only temporarily be changed by adding ?ts=...
to the URL and reloading the page. Consequently, using tabs, you are destined to find yourself reading your code with the wrong indent size (usually 8), whether you like it or not, and depending on the tool, you may not be able to do anything about it.
It is simply not possible to impose the requirement of user-configurable tab sizes on all the software which happens to render your code.
Spaces impose no such requirement. By embedding the correct indent size directly in the code, the code is rendered correctly everywhere, even if you cut and paste it into an email.
Characters in most monospace fonts are approximately twice as high as they are wide, and as such a 2 space indent is approximately the same width as the height of a blank line. If a blank line is the correct size for grouping and spacing things vertically, then it follows that 2 spaces should be appriximately the correct size for groping and spacing things horizontally.
This also means that a line that follows the lead of nested control structures and the line of closing braces are both at a 45° angle.
function foo() {
if (bar) {
while (baz) {
if (zap) {
return zoo;
// ↖ this line has a 45° angle to "function foo() {"
// ↙ line of closing braces is also at a 45° angle
}
}
}
}
2 spaces is also the minimum possible indent size that is larger than the individual spaces that are used to separate words within a line.
2 spaces conserves more horizontal space than the alternative 4 or 8 spaces. This is important because just as there is a cost in vertical space, there is a cost in horizontal space. The higher the indent size, the less horizontal space the body of a control structure has between the indent on the left and the margin on the right. Deeply nested control structures (which are sometimes appropriate) become more feasible the smaller the indent.
From Steve McConnell's Code Complete Second Edition chapter on Layout and Style:
Subjects scored 20 to 30 percent higher on a test of comprehension when programs had a two-to-four-spaces indentation scheme than they did when programs had no indentation at all. The same study found that it was important to neither under-emphasize nor over emphasize a program’s logical structure. The lowest comprehension scores were achieved on programs that were not indented at all. The second lowest were achieved on programs that used six-space indentation. The study concluded that two-to-four-space indentation was optimal. Interestingly, many subjects in the experiment felt that the six-space indentation was easier to use than the smaller indentations, even though their scores were lower. That’s probably because six space indentation looks pleasing. But regardless of how pretty it looks, six-space indentation turns out to be less readable. This is an example of a collision be tween aesthetic appeal and readability.
Please update the picture (Nov 2019)