Code is indented for Readability:
function foo (arg) {
if ( Array.isArray(arg) ) {
.forEach((e) => {
arg// Indenting visually indicates the scope, making it easier to see
// where a block starts and ends
;
})
} }
function bar (arg) {
if ( Array.isArray(arg) ) {
.forEach((e) => {
arg// Without indentation, it's harder to follow the control flow of a program
;
})
} }
Programmers like to argue about the proper way to indent their code.¹
I prefer using spaces over tabs, and using a tab width of 3.
Tab width
The tab width is how many columns to indent the code by for each level.
function foo (arg) {
if ( Array.isArray(arg) ) {
.forEach((e) => {
arg// A smaller tab width makes it harder to see the difference between
// indentation levels
;
})
} }
function foo (arg) {
if ( Array.isArray(arg) ) {
.forEach((e) => {
arg// A bigger tab width makes the indentation use up a
// lot more of the line, and we generally want to keep
// the length of lines below a certain amount. If the
// indentation is bigger, then the actual code on
// the line has to be shorter.
;
})
} }
Agreeing on the right balance is hard.
However, there is an oddity to this decision: Often only tab widths of 2, 4, and 8 are considered.
Powers of 2 are important numbers in programming. But in this context, being a power of 2 is irrelevant. If code is more readable with a tab width of 1 or 3 or 5, there’s no reason not to use them.
This is significant because the two most common tab widths are 2 and 4. But nobody’s too happy with either. 2 seems a little too small, and 4 seems a little too big.
Now, this might come as a shock some, but there’s a number between 2 and 4. I discovered this number a few years into my programming career, and have been using it as my tab width ever since.
Tabs vs Spaces
A space is an ordinary space, input by the spacebar on most keyboards.
A tab is a special character that means “one bit of indentation”, which can be displayed as any number of spaces. It can be input in most editors by the tab key on most keyboards.
Arguments for tabs
Tabs let readers use their preferred tab width
This is the main argument for tabs.
Since people disagree about the best tab width to use, using tabs lets everyone use whichever they prefer. This is nice in theory.
In practice, code is viewed in too many different contexts, many of which don’t allow the reader to easily set the tab width. Nobody’s going to configure every program on every computer they use and invoke --tab-width=3
every time and memorise the variations of this invocation for every command line program that displays code.
It wouldn’t be so bad if the default tab width wasn’t usually 8.
Tabs are easier to input
Tab is one keypress. Spaces are many!
This isn’t a real argument. It’s just something the Silicon Valley show writers used as a visual gag.
Every code editor more sophisticated than Notepad.exe can turn a single tab keypress into any number of spaces, and make a single backspace keypress remove the same amount. This makes working with spaces almost identical to working with tabs.
Tabs make smaller files
I’m not sure if this is a real argument. The differences are negligible.
When compressed, they’ll be the same size:
input | compresses to |
---|---|
\s\s\s\s\s\s\s\s |
8\s |
\t\t |
2\t |
Code transferred over the network is usually compressed first, so it’s only disk space that’s affected. Only in very niche contexts is the few extra KB from spaces going to matter, when most disk space is measured in GB or TB.
Tabs are more precise
With spaces, you’re saying “this is a bunch of spaces”.
With tabs, you’re saying “this is a specific amount of indentation”, conveying more semantic information.
If you have, say, 8 spaces, you can’t know without context whether that’s 1, 2, 4, or 8 levels deep. Whereas with 2 tabs, you know for sure that’s 2 levels deep.
But the depth only exists within context. If the code is taken out of that context, then the indentation becomes nonsense regardless of how it’s expressed.
// With 24 spaces, are we at depth 3, 6, 8, 12, or 24?
//
// It doesn't matter! The purpose of indentation is
// readability, so the question is irrelevant.
//
// (The answer is 0, because we're in a new context.)
Tabs are easier to collaborate with
If you’re using spaces, then everyone’s editors need to be setup to use the same tab width. If different people have different tab widths, the code will get messed up or be inconsistent.
With tabs, everyone is inputting the same character, and merely deciding on their own how wide to display it.
This is a solved problem. Every editor more sophisticated than Notepad.exe auto-detects the tab width of opened files and changes its behaviour to match.
The coordination problem is one that needs addressing regardless. There’s more to code style than just indentation, and you’re gonna need config files for everyone’s preferred editor anyway.
Arguments for spaces
Spaces let writers use their preferred tab width
This is the main argument for spaces.
We generally want to keep lines below a certain length. If readers can have any tab width they please, we can’t really do that.
If you write code with tabs such that lines stay below a certain length, and assume that your reader might have a tab width of up to 8—not because they prefer it, but because that’s the default—then you’re quickly going to run out of space.
This is most notably an issue when viewing code in a web browser, such as on Github. Browsers have a default tab width of 8, and changing it isn’t easy.² There are many other contexts where you read code that isn’t your preferred editor, and almost all of them default to a tab width of 8.
The counter-argument to this is that you really shouldn’t be using that much nesting anyway and ought to rewrite the code. This is sometimes true. It doesn’t seem reasonable to pay this cost without some reason to use tabs in the first place.
The main issue with letting writers use their preferred tab width is nobody can agree on what tab width to use.
Spaces let you align code
Sometimes you want to align bits of code to make it look nicer:
function foo (arg) {
if ( Array.isArray(arg) ) {
.forEach((e) => {
arglet i = 0,
= 0;
j ;
})
} }
If you use tabs and someone has a different tab width than you, the alignment breaks:
function foo (arg) {
if ( Array.isArray(arg) ) {
␉ .forEach((e) => {
␉ ␉ arglet i = 0,
␉ ␉ ␉ = 0;
␉ ␉ ␉ ␉ j ;
␉ ␉ })
␉ } }
But tabs are only for indentation. Tab users can still use spaces for alignment:
function foo (arg) {
if ( Array.isArray(arg) ) {
␉ .forEach((e) => {
␉ ␉ arglet i = 0,
␉ ␉ ␉ = 0;
␉ ␉ ␉ j ;
␉ ␉ })
␉ } }
This way, changing the tab width doesn’t affect the alignment:
function foo (arg) {
if ( Array.isArray(arg) ) {
␉ .forEach((e) => {
␉ ␉ arglet i = 0,
␉ ␉ ␉ = 0;
␉ ␉ ␉ j ;
␉ ␉ })
␉ } }
So this is mostly not an issue.
However, mixing tabs and spaces can be a little harder than just knowing for sure every space in your code is a regular \x20
. It’s not too concerning, since any good editor should have an easy way to tell the difference between them. But if you mess up, you might not even think to check.
More practically, it’s a little bit easier to do this alignment when one keypress can add or remove multiple spaces. So ironically, the space users are the ones actually doing fewer keypresses.