If you’ve spent more than five minutes in a software engineering team, you’ve likely witnessed it: two developers, locked in passionate debate, arguing about the width of their indentation. One reaches for the Tab key with the righteousness of a crusader, while the other frantically taps the spacebar, refusing to yield. Their pull request sits in limbo, and the team watches, exhausted. Welcome to one of programming’s most gloriously pointless wars. But here’s the thing—and I say this as someone who has spent an embarrassing amount of time thinking about this: the war isn’t really about tabs and spaces. It’s about control, consistency, and the deep human need to believe that our choices are objectively correct. It’s about us.
The Semantic Argument: Or Why Tabs Are “Technically Right”
Let me start with what is arguably the most intellectually satisfying argument: tabs are semantically correct for indentation. Think about it. The humble Tab character (ASCII 0x09) was literally invented for indentation. Its entire existence is justified by the need to indent text. Spaces, on the other hand, were designed for… well, spacing things. Between words. Between concepts. Not for indenting entire blocks of code. “But,” you say, throwing your hands up, “if it’s so obviously right, why hasn’t everyone adopted tabs?” Ah, there’s the rub.
The Practical Problem: Or Why We Can’t Have Nice Things
Here’s where things get spicy. Let’s say you use tabs for indentation, and your coworker also uses tabs, but they view them as 2 characters wide while you view them as 4 characters wide. This is supposed to work beautifully. One character in the file means one logical indentation level, period. The visual representation is user preference.
// With tabs, both developers see this file correctly,
// just with different visual widths
int calculate() {
→multiply(); // That arrow is a tab in your editor
→→if (condition) {
→→→doSomething();
→→}
}
The magic of tabs: Johnny sees →→→ as taking up 6 characters width. Maria sees it as 12. The actual file stays exactly the same. Git diff doesn’t explode. Everyone is happy.
Except… not really.
Because the moment you need alignment—not indentation, but alignment—tabs fail catastrophically. And here’s where most tabs-vs-spaces debates derail.
The Alignment Nightmare: Where Theory Meets Reality
Consider this common scenario:
// Using only tabs (wrong way)
struct Config {
→int x;
→int very_long_variable_name;
→bool flag;
};
// Johnny views tabs as 4 chars, sees:
struct Config {
int x;
int very_long_variable_name;
bool flag;
};
// Maria views tabs as 2 chars, sees:
struct Config {
int x;
int very_long_variable_name;
bool flag;
};
This is fine because there’s no alignment—just indentation. But now let’s try to align these declarations:
// Using tabs for indentation AND spaces for alignment (correct way)
struct Config {
→int→ x;
→int→ very_long_variable_name;
→bool→ flag;
};
// This stays perfectly aligned regardless of tab width
This is the secret sauce: tabs for indentation, spaces for alignment. It’s not mixing tabs and spaces (a phrase that makes people shudder). It’s using the right tool for the right job.
The Money Problem: Or Why Your Career Might Depend on This
Here’s something that will make you uncomfortable: developers who use spaces for indentation make more money than developers who use tabs. The median space-using developer earned $59,140, while the median tab-using developer earned $43,750. That’s not a rounding error. Now, before tab enthusiasts start writing angry comments, understand what this probably means: spaces became the industry standard, and spaces are used by the majority of companies. Developers who conform to industry standards tend to work at larger, better-paying companies. It’s not that spaces cause higher salaries. It’s that spaces correlate with being in established, institutional tech environments. It’s the same reason everyone drives on the same side of the road—not because one side is physically superior, but because coordination matters more than correctness.
The Real War: Consistency vs Flexibility
This is where I think the actual debate lives, and where it gets interesting. Team A says: “We all use spaces. Always 4 spaces per indentation level. Everyone’s editor is configured identically. When someone new joins, they follow the convention. Done.” Team B says: “We use tabs for indentation. Everyone can configure their preferred visual width in their editor. New developers can read code exactly as they prefer. Flexibility!” Both strategies work. The question is: what do you value? If you value consistency and predictability, you choose spaces. Everyone sees exactly what’s on the screen. There’s no ambiguity. If you value flexibility and personal preference, you choose tabs. Your team can be more heterogeneous, and developers in different environments (small screens, accessibility needs, personal preferences) can adjust without changing the file itself.
The Practical Reality: What Actually Works
Here’s my honest take after years of working in teams ranging from 2 people to 200: The answer is spaces. Not because spaces are technically superior. Not because spaces are “more correct.” But because:
- Spaces are what the industry uses by default, and learning to work with industry standards is more important than being right
- Spaces avoid the “tab war” entirely in mixed environments
- Tools, linters, and formatters all expect spaces unless you specifically configure them otherwise
- New developers to your team will already know spaces from their previous jobs However—and this is important—if you’re in a team that commits to the tabs for indentation, spaces for alignment philosophy, it’s actually superior. You get the semantic correctness of tabs with the alignment precision of spaces.
# Using this correctly:
def function_with_complex_logic():
→result = some_function(argument1, argument2,
→ argument3, argument4)
→if result:
→→process(result)
→return result
(That’s a tab for indentation, then spaces to align the continuation line.)
Setting Up Your Project: A Practical Guide
If you’re starting a new project, here’s what I’d recommend:
Option 1: Spaces (The Safe Choice)
Step 1: Create a .editorconfig file in your project root
root = true
[*]
indent_style = space
indent_size = 2
[*.py]
indent_size = 4
Step 2: Configure your editor to use this config (most modern editors support EditorConfig out of the box) Step 3: Add a linter/formatter to enforce it:
- JavaScript/TypeScript: Prettier
- Python: Black
- Go: gofmt (built-in)
- Rust: rustfmt (built-in) Your team members literally cannot deviate, even if they try. It’s enforced at commit time.
Option 2: Tabs with Alignment (The Right-But-Harder Way)
Step 1: Create the .editorconfig file
root = true
[*]
indent_style = tab
indent_size = 4 # Controls visual width in editors
trim_trailing_whitespace = true
Step 2: Configure your editor to display tabs: For Vim/Neovim:
set list listchars=tab:>-,trail:.,extends:>,precedes:<
For VSCode, use the “Indent-Rainbow” extension or set:
"editor.renderWhitespace": "all"
Step 3: Educate your team. This is important. Document the rule: tabs for indentation, spaces for alignment, nothing else. Step 4: Add a pre-commit hook to catch violations
#!/bin/bash
# .git/hooks/pre-commit
if git diff --cached | grep -P "^\+.*[^\t] \t" > /dev/null; then
echo "Error: Found spaces before tabs (alignment issues)"
exit 1
fi
The Visualization: Understanding the Decision Space
already established?"} B -->|Yes| C["Use what they use"] B -->|No| D{"Do you want
flexibility?"} D -->|Yes| E{"Can you enforce
tabs+spaces rule?"} D -->|No| F["Use spaces, simpler"] E -->|Yes| G["Use tabs for indent
spaces for align"] E -->|No| F C --> H["Check .editorconfig
or team guidelines"] G --> I["Document clearly
Use tooling"] F --> J["Use prettier/black
Enforce with CI"]
Real Code Examples: Where It Actually Matters
Let’s look at where this distinction makes a genuine difference:
Example 1: Multi-line Function Calls
# With spaces (no visual distinction between logical levels)
def process_data(input_file):
results = combine_datasets(
first_dataset,
second_dataset,
third_dataset
)
return results
# With tabs+spaces (clear visual distinction)
def process_data(input_file):
→result = combine_datasets(
→ first_dataset,
→ second_dataset,
→ third_dataset
→)
→return result
See the difference? The → indicates tabs (indentation level), the spaces after are alignment. When you change tab width, the logical structure remains obvious.
Example 2: Aligned Comments or String Concatenations
// Spaces only - 2 spaces per level
const config = {
name: "MyApp",
version: "1.0.0",
description: "Does stuff"
};
// Tabs + spaces - the right way
const config = {
→name:→ "MyApp",
→version:→ "1.0.0",
→description: "Does stuff"
};
The Salary Question: Or Why Your Boss Probably Doesn’t Care
Remember that statistic about spaces making more money? Here’s the real takeaway: your employer doesn’t care which you use, as long as your team is consistent. What they care about is:
- Code reviews don’t get bogged down in formatting debates
- Pull requests show actual changes, not indentation changes
- New developers can contribute quickly
- The codebase remains readable The developers making more money are typically at companies with established standards and tooling. It’s not the spaces that pay the bills—it’s the professionalism and infrastructure.
The Uncomfortable Truth
Here’s what I’ll never say in a polite company of developers: this debate has no objectively correct answer. Tabs are semantically correct. Spaces are more practical. Both are defensible. What matters is that you:
- Choose one approach for your team/project
- Document it clearly (in README, CONTRIBUTING, or .editorconfig)
- Enforce it with tooling (linters, formatters, pre-commit hooks)
- Never let personal preference override team consistency The developers who achieve the most aren’t debating this—they’re shipping code.
The End State: Making Peace With The War
If you work on a team that uses spaces, use spaces. If they use tabs, use tabs. If they’re enlightened enough to use tabs for indentation and spaces for alignment, learn the rule and apply it consistently. The war will continue because fundamentally, it’s about human nature. We like being right. We like our choices being validated. But the developers who are actually productive? They’ve already made their choice and moved on to solving real problems. The beautiful irony is that this entire argument will likely become obsolete. Modern AI code assistants don’t care about your indentation philosophy. Neither should you—not enough to sacrifice team harmony. Use what your team uses. Format automatically. Move on. Because the real war in software engineering isn’t tabs vs spaces. It’s between shipping products and debating endlessly. And I know which side most of us are on.
What’s your stance? If you’ve gotten this far, you probably have thoughts. The discussion in the comments—respectfully—is where the real value lives.
