The Case for Tabs: Indentation as Interface

Million Lights Media

Every few years, the tabs-vs-spaces debate resurfaces — usually triggered by a linter config, a new hire, or someone committing with the wrong editor settings. The discourse predictably collapses into aesthetics and preference. But framing it as preference misses the actual argument, and the actual argument strongly favors tabs.

Let me be specific. This isn't a case for tabs everywhere, for all purposes. Inline alignment — lining up assignment operators, padding comment columns, constructing ASCII tables — should use spaces. But for indentation, the structural signal that communicates nesting depth, tabs are objectively better suited to the task. Not because they look better, but because of what they are.

What indentation actually is

Indentation is a unit of structural depth. One level of nesting equals one unit of indent. That unit has no inherent width — width is a display concern, not a semantic one. When you write indentation with spaces, you're conflating the structural signal with an aesthetic choice, baking a visual opinion into the source file itself.

A tab character is a single byte that means indent one level. Four space characters are four bytes that mean move right 32 pixels, assuming 8px per character. One is a semantic signal. The other is a hardcoded layout instruction.

"A tab character is a single byte that means indent one level. Four space characters mean move right 32 pixels. One is semantic. The other is layout."

This distinction matters because source code is consumed in multiple contexts: editors, terminals, code review tools, diff viewers, documentation generators, screen readers. Each context has different display constraints. Tabs let each context render indentation at whatever width makes sense. Spaces don't.

The configurable width argument

The most common objection to tabs is that they "look different on everyone's machine." This is usually deployed as a bug report against tabs. It is actually their primary feature.

A developer who wants compact code can set tab width to 2. A developer who prefers generous visual separation sets it to 4. A developer with a visual impairment who finds narrow indentation hard to track might set it to 6 or 8. None of these settings change the underlying source file. The semantic structure is preserved. The display is adapted to the individual.

Spaces make this impossible. Four-space indentation is four-space indentation for every reader, in every context, on every display, forever. The original author has made a permanent aesthetic decision on behalf of every future reader of that code.

// Tab-indented: each reader sees width they prefer
function process(items) {
	for (const item of items) {
		if (item.active) {
			transform(item);
		}
	}
}

// Space-indented: one width, enforced on everyone
function process(items) {
	for (const item of items) {
		if (item.active) {
			transform(item);
		}
	}
}

Adoption across the industry

Tabs are not a fringe preference. Some of the most disciplined and widely-studied codebases in existence default to tabs — often deliberately, and with documented rationale.

Project / Standard Style Notes
Linux kernel Tab Linus Torvalds, explicitly documented
Go (golang) Tab Enforced by gofmt, non-negotiable
WordPress Tab PHP, JS, and CSS coding standards
jQuery Tab Core and plugins
Python (PEP 8) 4 spaces Community standard, tabs allowed but discouraged
PHP-FIG PSR-2 4 spaces Framework interoperability standard
Ruby community 2 spaces Influenced by Rails
Twitter Bootstrap 2 spaces Front-end CSS/JS

The Go ecosystem is the most instructive case. When Google designed Go, they made tab indentation non-negotiable and enforced it via gofmt, the language's official formatter. There is no debate in Go codebases because the toolchain removes it. The Go team's reasoning aligned precisely with the semantic argument above: gofmt was about separating style enforcement from style preference.

Linus Torvalds, whose Linux kernel coding standards document is notable for its directness on this subject, simply states that indentation is "8 characters." The message is that indentation should signal nesting depth clearly and unambiguously — and deep nesting is a problem to be refactored, not hidden by narrow indentation.

The efficiency argument isn't trivial

Tabs require a single keystroke per indentation level. Spaces require between two and eight, depending on your settings. At scale, across a team, across a codebase, this is not negligible. It is also not the strongest argument for tabs — but dismissing it entirely misses what it signals.

The deeper point is that tab-indented code is easier to manipulate. Deleting one indent level on a block of code means deleting one character per line. Doing the same with four-space indentation means a find-and-replace operation or editor-specific tooling. Tabs compose more cleanly with programmatic text manipulation — which is relevant if you write scripts that process source files, generate code, or diff patches.

Addressing the counterarguments

The honest version of the spaces argument has two components: consistency and tooling. The consistency argument holds that if everyone uses spaces, the code looks the same everywhere. This is true. It is also a reason to prefer tabs, not spaces — if your team agrees on tab width in their editor configs. The only case where spaces guarantee visual consistency over tabs is when you don't trust your collaborators to configure their editors. That is an onboarding and documentation problem, not a tabs problem.

The tooling argument historically had more force. Some older tools and web-based interfaces rendered tabs poorly or inconsistently. GitHub, for a period, would expand tabs to eight characters by default, which made tab-indented code appear to have extreme indentation. This is less true now — most platforms have improved tab handling or added configuration. More practically: nobody writes production code in a browser-based text input. The relevant rendering contexts are editors, and every major editor has had configurable tab width for decades.

The alignment exception

There is one legitimate case for spaces, and it is worth being precise about it: column alignment. If you are aligning the values in a multi-line assignment block, or padding elements in a manually-formatted table, you need spaces — because you are making a layout decision that should be stable regardless of tab width.

// This alignment depends on exact character positions: use spaces
const firstName = "Ada";
const lastName = "Lovelace";
const birthYear = 1815;

// This indentation is structural: use tabs
if (condition) {
	doSomething();
	doSomethingElse();
}

The practical recommendation is the one the Linux kernel documentation implies: use tabs for indentation, spaces for alignment, and never mix them within a single construct. Most linters and formatters can enforce this distinction. The argument is not "tabs everywhere" — it's "tabs for the thing tabs are designed for."

A word on standards

The proliferation of competing standards — PSR-2, PEP 8, the Airbnb style guide, Google's various language guides — creates real friction in teams that work across languages and ecosystems. The instinct to resolve this friction by picking a single style for everything is understandable but probably wrong. Python's parser has historically been sensitive to mixing tabs and spaces in ways that caused genuine bugs; following PEP 8 there has a stronger rationale than just aesthetics. Go makes the decision for you. PHP's PSR standards exist to enable framework interoperability, and following them is a practical decision about ecosystem participation.

The point is not that everyone should use tabs regardless of context. It's that the default intellectual position — "spaces are the professional standard, tabs are for miscreants" — does not survive contact with the actual reasoning. The professional standard, across a significant portion of serious codebases, is tabs. The reasoning behind that standard is sound. The burden of justification should sit with the status quo, not with the alternative.

"The default position — spaces are professional, tabs are for miscreants — does not survive contact with the actual reasoning."

Configure your editor. Set your tab width. Pick the display that serves your reading preferences. That is exactly the point.


References: Linux Kernel Coding Style (kernel.org); Go FAQ — Formatting (go.dev); WordPress Coding Standards (developer.wordpress.org); PHP-FIG PSR-2 (php-fig.org); PEP 8 — Style Guide for Python Code (python.org)