February 3rd 2015 Tabs vs. Spaces: A Treatise

The programming world is full of epic battles. Mac vs. PC. iOS vs. Android. Vim vs. Emacs. One of my favorites is the epic battle concerning source code file indentation: tabs vs. spaces. It’s one of my favorites not only because it seems to be one that evokes the greatest passions, but because I started out firmly on one side but switched over, and in doing so, developed what I find to be a well-reasoned argument for my decision.

One of the reasons I love this debate so much is that it seems like a classic bikeshed argument. Which character should be used to indent and align source code, a tab or a space? To most, the answer would be who cares? To programmers (or at least the passionate and vocal minority who understands and even cares about the issue), that view is naïve and even dangerous. For this battle’s warriors, it comes down to a clash between consistency and customizability. And what may surprise even some of the participants is there there are not two, but three sides in this battle.

Let’s take a look at each and examine their strengths and weaknesses. But first, a note about symbols: In order to highlight tabs and spaces, I will use a ^ to represent a tab character and a · to represent a space character. (Tabs, of course, will only consist of one character, but I will pad them to the left they appear to be using blank spaces.)

Now, without further ado…

Tabs

There are two key benefits to using tabs to indent source code. The first is that tabs, being a single character, take up only a single byte of memory—as opposed to spaces, which may take up 2 or 4 or 8 for every indent—and thus make source code files smaller. That argument can be discounted fairly easily by pointing out that today, disk space is cheap, and whenever file size does matter—namely, when transmitting files over a network—they are generally compressed or minified or both, negating the benefit of saving a few bytes on disk.

The second argument is more interesting: Tabs allow programmers to set the visual size of an indent to their preferred width. I may like 4-space indents and you may like 8; with tabs, each of us can configure our editors to display the tab however we like, without changing the actual content of the source code file. For example, this bit of code looks like this when displayed using 4-space indents:

int abs(int n)
{
    if (n < 0) {
        return -n;
    } else {
        return n;
    }
}

But looks like this when another programmer’s editor is configured to display tabs with a width of 8:

int abs(int n)
{
       if (n < 0) {
               return -n;
       } else {
               return n;
       }
}

Given that extreme level of customizability, it may seem like the argument is over. But there are (at least) two sides to every story…

Spaces

Fans of spaces point out that the promise of customizable visual widths that comes with tabs isn’t much of a promise at all. Consider this bit of C code:

int return_nothing(int arg1,
                   char arg2)
{
    return 0;
}

A tab advocate with an editor configured to use a visual tab width of 4 would be likely to align that code like this:

int return_nothing(int arg1,
^   ^   ^   ^   ···char arg2)
{
^   return 0;
}

Which means that a programmer using an 8-space visual width would see this in their text editor:

int return_nothing(int arg1,
                                   char arg2)
{
        return 0;
}

And now the space fans’ argument becomes clear: Tabs only work when indenting text, not when aligning text. The second line should consistent entirely of leading spaces, not tabs, but since it doesn’t, programmers either have to use the same tab width anyway (negating the primary benefit of tabs) or deal with misalignment.

Things get even weirder if a line is both indented and aligned, as in this code:

void do_nothing_with_array(void)
{
    int array[2] = {get_some_int(),
                    get_another_int()};
}

In order for this to be properly aligned irrespective of an individual programmer’s tab width setting, tabs and spaces would have to be inserted like so:

void do_nothing_with_array(void)
{
^   int array[2] = {get_some_int(),
^   ················get_another_int()};
}

Tab fanatics point that “tabs should be used for indentation, and spaces for alignment.” This rule of thumb solves the issue, but programmers who stick to spaces argue that it’s much easier to tell people to “use spaces” rather than trying to explain when to use spaces and when to use tabs, and having to make sure all programmers stick to the rules. Besides that, programmers now have to use both tab and space for indenting and aligning, rather than just tab. That’s a lot of work for the sole benefit of being able to set your preferred visual tab width.

So is that it?

Smart Tabs

Ideally, programmers would be able to use “smart tabs”. Smart tabs offer the best of both worlds: Tabs are used for indentation, allowing programmers to set their preferred tab width, but the editor is smart enough to automatically insert spaces when aligning code (even if the tab key is pressed), relieving the programmer of the burden of properly using spaces and tabs.

Unfortunately, as far as I can tell, few editors support this feature. Vim can do it, and it looks like Emacs can, too, but my research has not unearthed any settings or plugins for Sublime Text, TextMate, or the multitude of other editors programmers use to write code.

The Verdict

Given the pros and cons of each, I am firmly in the spaces camp. Once upon a time, I preferred tabs, but one too many incidents of pulling my hair out over misaligned text, and too much time spent reconfiguring my editor to someone else’s tab width ideal, made me realize that the benefits of spaces outweigh the benefits of tabs.

To me, it’s an issue of customizability vs. consistency. Tab users value customizability; I value consistency (and neatness). I also value not having to explain to every new team member or open source contributor the difference between tabs/indentation and spaces/alignment when I can just say, “Use spaces,” and get on with work.

(I also spend most of my work time writing Python code, where this isn’t even a debate—no self-respecting Python programmer would ever use tabs.)