From the blog
Tabs vs spaces: what actually matters
One tab
One character. Your tab width.Two spaces
Two characters. A fixed indent.When I first wrote this in 2015, my answer was pretty firm: spaces, with two per indent. I still like that for plenty of web projects. But it isn't a rule every language, team or codebase should follow.
The useful answer is less dramatic: follow the project, make the code easy to read, and let your tools keep it consistent.
The Tab key isn't the argument
Pressing Tab doesn't necessarily put a tab character in your file. Your editor can insert spaces instead, so you can use the same key whichever style your project has chosen.
The difference is what gets saved:
- A tab is one character. Your editor displays it by moving to the next tab stop, according to its settings.
- Spaces are individual characters. Two spaces are still two spaces, regardless of the editor's tab-width setting.
Turn on your editor's “render whitespace” or “show invisibles” option if you want to see which you're actually using. It's particularly useful when a pasted snippet looks right but doesn't quite line up.
There's a good case for both
Spaces make the indentation width predictable across editors. If a team wants two columns per level, two spaces give them that. It's also my usual preference when I'm starting a small web project without an existing convention.
Tabs leave more of the display choice to the person reading. One developer can view the same file with narrow indentation, while another uses a wider tab width to follow nested code more easily. That flexibility can be a real readability benefit; a different display width isn't automatically a problem to fix.
Neither choice makes the code itself better. Clear names, manageable nesting and sensible structure do a lot more for readability than winning this particular argument.
Follow the language and the project
Before changing an editor setting, look for a contribution guide, .editorconfig file or formatter configuration in the repository. The existing rules matter more than your personal defaults.
Language conventions differ, too. Python's PEP 8 recommends four spaces. Go uses tabs for indentation and provides gofmt to handle formatting. Two spaces across absolutely everything would be the wrong starting point.
Indentation and alignment aren't quite the same job, either. Prettier can indent with tabs while using spaces for alignment. That's deliberate—not the same as accidentally mixing indentation styles within a block.
Put the decision in the repository
An EditorConfig file lets a project describe its indentation rules without tying everyone to the same editor. Here's a small example for a web project that uses two spaces, with a four-space exception for Python:
# .editorconfig
root = true
[*.{js,jsx,ts,tsx,css,scss,html,json}]
indent_style = space
indent_size = 2
[*.py]
indent_style = space
indent_size = 4
Those file patterns are intentional: this isn't a blanket instruction to replace tabs in every file. Adapt it to your project, and check that your editor has EditorConfig support enabled; some need an extension.
If you're already using Prettier, it reads these indentation properties, unless its own configuration overrides them. Avoid having the editor and formatter fight over different settings.
Make consistency automatic
Use the project's formatter and enable format-on-save if it suits your workflow. A shared formatter version and a formatting check in your build or pull-request checks are more dependable than asking everyone to remember the same preferences.
For an existing codebase, keep formatting-only changes separate from bug fixes or new features. A review is much easier when a one-line fix hasn't also reindented the whole file. And leave third-party libraries and generated files alone unless you maintain the process that produces them.
My preference hasn't changed all that much. What has changed is how strongly I'd impose it: agree the convention, automate it, and get on with building something useful.
Keep exploring