Если вы провели в команде разработчиков программного обеспечения больше пяти минут, то, вероятно, стали свидетелем такого зрелища: два разработчика, увлечённые жарким спором, обсуждают ширину отступов. Один нажимает на клавишу Tab с праведностью крестоносца, а другой лихорадочно стучит по пробелу, не желая уступать. Их запрос на включение в репозиторий зависает в неопределённости, а команда наблюдает за этим, устав от всего этого.

Добро пожаловать в одну из самых бессмысленных войн в программировании.

Но вот в чём дело — и я говорю это как человек, который потратил немало времени на размышления об этом: война ведётся не столько из-за табуляции и пробелов, сколько из-за контроля, последовательности и глубокой человеческой потребности верить, что наш выбор объективно правильный. Это о нас.

Семантический аргумент: или почему табуляция «технически правильная»

Начну с того, что, возможно, является наиболее интеллектуально удовлетворяющим аргументом: табуляция семантически корректна для отступов.

Подумайте об этом. Скромный символ табуляции (ASCII 0x09) был буквально изобретен для отступов. Всё его существование оправдывается необходимостью отступать текст. Пробелы же, с другой стороны, были разработаны для… ну, для размещения пробелов между словами, между понятиями, а не для отступа целых блоков кода.

«Но», скажете вы, всплеснув руками, «если это так очевидно правильно, почему все не перешли на табуляцию?» Ах, вот в чём загвоздка.

Практическая проблема: или почему мы не можем иметь хорошие вещи

Здесь всё становится интереснее. Допустим, вы используете табуляцию для отступов, и ваш коллега также использует табуляцию, но он считает её шириной в 2 символа, а вы — в 4 символа. Предполагается, что это должно работать прекрасно. Один символ в файле означает один логический уровень отступа, и точка. Визуальное представление — это предпочтение пользователя.

// С табуляцией оба разработчика видят этот файл правильно, просто с разной визуальной шириной
int calculate() {
multiply();  // Эта стрелка — табуляция в вашем редакторе
→→if (condition) {
→→→doSomething();
→→}
}

Магия табуляции: Джонни видит →→→ занимающим ширину в 6 символов. Мария видит это как 12. Фактический файл остаётся точно таким же. Git diff не взрывается. Все довольны.

За исключением… не совсем.

Потому что в тот момент, когда вам нужно выравнивание — не отступ, а именно выравнивание — табуляция катастрофически терпит неудачу. И здесь большинство споров о табуляции и пробелах заходят в тупик.

Кошмар с выравниванием: где теория встречается с реальностью

Рассмотрим этот распространённый сценарий:

// Используя только табуляцию (неправильный способ)
struct Config {
int x;
int very_long_variable_name;
bool flag;
};
// Джонни видит табуляцию как 4 символа, видит:
struct Config {
    int x;
    int very_long_variable_name;
    bool flag;
};
// Мария видит табуляцию как 2 символа, видит:
struct Config {
  int x;
  int very_long_variable_name;
  bool flag;
};

Это нормально, потому что нет выравнивания — только отступ. Но теперь попробуем выровнять эти объявления:

// Используя табуляцию для отступов и пробелы для выравнивания (правильный способ)
struct Config {
int          x;
int          very_long_variable_name;
bool         flag;
};
// Это остаётся идеально выровненным независимо от ширины табуляции

Это секретный соус: табуляция для отступов, пробелы для выравнивания. Это не смешивание табуляции и пробелов (фраза, которая заставляет людей содрогаться). Это использование правильного инструмента для правильной работы.

Денежный вопрос: или почему ваша карьера может зависеть от этого

Вот что-то, что заставит вас чувствовать себя некомфортно: разработчики, которые используют пробелы для отступов, зарабатывают больше, чем разработчики, которые используют табуляцию.

Медианный доход разработчика, использующего пробелы, составил 59 140 долларов, в то время как медианный доход разработчика, использующего табуляцию, составил 43 750 долларов. Это не погрешность округления.

Теперь, прежде чем энтузиасты табуляции начнут писать гневные комментарии, поймите, что это, вероятно, означает: пробелы стали отраслевым стандартом, и их используют большинство компаний. Разработчики, которые соответствуют отраслевым стандартам, как правило, работают в крупных компаниях с более высокой оплатой. Не то чтобы пробелы причиняли более высокие зарплаты. Просто они коррелируют с работой в устоявшихся, институциональных технологических средах.

Это как если бы все ездили по одной стороне дороги — не потому, что одна сторона физически лучше, а потому, что координация важнее правильности.

Настоящая война: согласованность против гибкости

Здесь, я думаю, и живёт настоящая дискуссия, и здесь становится интересно.

Команда А говорит: «Мы все используем пробелы. Всегда 4 пробела на уровень отступа. Конфигурация редактора у всех одинаковая. Когда кто-то новый присоединяется, он следует соглашению. Готово».

Команда B говорит: «Мы используем табуляцию для отступов. Каждый может настроить предпочтительную визуальную ширину в своём редакторе. Новые разработчики могут читать код именно так, как им удобно. Гибкость!»

Обе стратегии работают. Вопрос в том, что вы цените?

Если вы цените согласованность и предсказуемость, вы выбираете пробелы. Все видят ровно то, что находится на экране. Нет двусмысленности.

Если вы цените гибкость и личные предпочтения, вы выбираете табуляцию. Ваша команда может быть более неоднородной, и разработчики в разных средах (маленькие экраны, потребности доступности, личные предпочтения) могут настраивать параметры без изменения самого файла.

Практическая реальность: что на самом деле работает

Вот мой честный взгляд после многих лет работы в командах от 2 человек до 200:

Ответ — пробелы.

Не потому, что пробелы технически превосходят. Не потому, что пробелы «более правильные». А потому, что:

  1. Пробелы — это то, что индустрия использует по умолчанию, и научиться работать с отраслевыми стандартами важнее, чем быть правым.
  2. Пробелы полностью исключают «войну за табуляцию» в смешанных средах.
  3. Инструменты, линтеры и форматеры ожидают пробелы, если вы специально не настроите их иначе.
  4. Новые разработчики в вашей команде уже будут знать пробелы по предыдущим местам работы.

Однако — и это важно — если вы в команде, которая придерживается философии табуляция для отступов, пробелы для выравнивания, это на самом деле лучше. Вы получаете семантическую корректность табуляции с точностью выравнивания пробелов.

# Используя это правильно:
def function_with_complex_logic():
result = some_function(argument1,  argument2,
                       argument3,  argument4)
if result:
→→process(result)
return result

(Это табуляция для отступа, затем пробелы для выравнивания продолжения строки.)

Настройка вашего проекта: практическое руководство

Если вы начинаете новый проект, вот что я бы порекомендовал:

Вариант 1: Пробелы (безопасный выбор)

Шаг 1: Создайте файл .editorconfig в корне вашего проекта

root = true
[*]
indent_style = space
indent_size = 2
[*.py]
indent_size = 4

Шаг 2: Настройте ваш редактор для использования этой конфигурации (большинство современных редакторов поддерживают EditorConfig «из коробки»)

Шаг 3: Добавьте линтер/форматер для обеспечения соблюдения:

  • JavaScript/TypeScript: Prettier
  • Python: Black
  • Go: gofmt (встроен)
  • Rust: rustfmt (встроен)

Члены вашей команды буквально не смогут отклониться, даже если попытаются. Это обеспечивается при коммите.

Вариант 2: Табуляция с выравниванием (правильно, но сложнее)

Шаг 1: Создайте файл .editorconfig

root = true
[*]
indent_style = tab
indent_size = 4  # Управляет визуальной шириной в редакторах
trim_trailing_whitespace = true

Шаг 2: Настройте отображение табуляции в вашем редакторе:

Для Vim/Neovim:

set list listchars=tab:>-,trail:.,extends:>,precedes:<

Для VSCode используйте расширение «Indent-Rainbow» или установите:

"editor.renderWhitespace": "all"

Шаг 3: Проведите обучение вашей команды. Это важно. Документируйте правило: табуляция для отступов, пробелы для выравнивания, ничего другого.

Шаг 4: Добавьте хук перед коммитом для обнаружения нарушений

#!/bin/bash
# .git/hooks/pre-commit
if git diff --cached | grep -P "^\+.*[^\t] \t" > /dev/null; then
  echo