Начнём с наведения аксиоматики, потому что без неё любая дискуссия сводится к измерению физиологических особенностей организма, помешавших всем нам стать примами Большого театра. Роберт Мартин, тот самый дядюшка Боб с благостной улыбкой гуру, провозглашает: чистый код должен быть элегантным и эффективным, прямолинейным, как хорошая проза. Грейди Буч добавляет, что чистый код читается как хорошо написанный текст, он не заставляет ваш мозг кипеть от усилий понять замысел автора. Бьёрн Страуструп, создатель C++, того самого языка, который уже лет 15 в должной мере не знает никто в мире, и где шаблоны могут занимать полстраницы, утверждает, что чистый код делает одну вещь, но делает её хорошо. А Уорд Каннингем завершает этот хор оптимистов заявлением, что код чист, когда каждая функция делает именно то, что вы ожидали.

Прекрасно. Теперь, когда мы определились с тем, что чистый код — это единорог программирования, существующий в параллельной вселенной, где менеджеры не торопят с дедлайнами, а заказчики точно знают, чего хотят, давайте поговорим о реальности.

Я позаимствовал КДПВ с сайта «Как работает гомеопатия», потому что лучше на этот вопрос ответить невозможно:

Как работает «Чистый код»?
Как работает «Чистый код»?

Давайте пройдёмся по пунктам.

Чистый код экономит время в долгосрочной перспективе

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

Названия должны быть самодокументирующими

Ах, эти волшебные имена переменных. UserAccountManagerFactoryBuilderSingleton — теперь всем сразу понятно, что делает этот класс, правда? Не нужно никакой документации, когда у вас такие говорящие названия. Проблема в том, что код превращается в словесный понос, где каждая строчка расползается за границы экрана, а вы скроллите горизонтально, пытаясь понять, где же кончается это проклятое имя метода. И знаете что самое смешное? Через месяц выясняется, что UserAccountManager на самом деле только отправляет письма, а управлением занимается UserService, но переименовать уже страшно, потому что это сломает половину проекта.

Функции должны быть короткими

Дядюшка Боб говорит: функция не должна быть длиннее пяти строк. Прекрасный совет, если вы пишете калькулятор для начальной школы. В реальном мире следование этому правилу превращает вашу кодовую базу в матрёшку: функция вызывает функцию, которая вызывает функцию, которая вызывает функцию. Чтобы понять, что же на самом деле происходит при нажатии кнопки «отправить», — вам нужно нырнуть на семь уровней вглубь, открыть двадцать файлов и составить блок-схему на ватмане. Поздравляю, ваш код теперь чист как слеза, и столь же бесполезен для понимания логики.

Комментарии — признак плохого кода

«Если вам нужно написать комментарий, значит ваш код недостаточно выразителен.»

Это — мантра чистого кода, которую повторяют как заклинание. Результат предсказуем: вы смотрите на битовые операции, на хитрый алгоритм оптимизации или на обход бага в стороннем API, и нигде ни слова объяснения, почему это работает именно так. Предыдущий разработчик был уверен, что его код самодокументируется, а вы три часа гуглите, пытаясь понять, зачем он сдвигает биты влево и складывает их с какой-то магической константой. Спойлер: это был обход бага в драйвере, но теперь драйвер обновился, код можно упростить, но никто этого не знает, потому что комментариев нет.

DRY (Don’t Repeat Yourself) священен

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

Тут я должен оговориться, ведь проницательные читатели обязательно припомнят мне, как я триста раз говорил: «думайте на шаг вперёд, YAGNI — враг, лучше один раз отмерить сейчас, чем семь раз перекраивать потом». Всё так, тут нет противоречий. Если кусок кода выглядит как отчуждаемый в абстракцию, и крякает так же — не нужно ждать второго повторения, выносите сразу. Если же нет — лучше копипаста, чем сторонний модуль utils с кучей одноразовых, как презервативы, функций. Просто надо руководствоваться здравым смыслом, а не принципами «чистого кода».

Тесты делают код чище

TDD фанатики обещают, что если вы пишете тесты до того, как приступаете к реали��ации, — ваш код автоматически становится чище. Практика показывает, что вы просто получаете две кодовых базы вместо одной, и обе требуют поддержки. Половина тестов проверяет очевидные вещи вроде того, что два плюс два равно четыре, другая половина настолько хрупкая, что ломается от любого рефакторинга. А покрытие в девяносто процентов даёт вам ложное чувство безопасности, пока в продакшене не выясняется, что оставшиеся десять процентов — это как раз критичная логика оплаты.

Покрытие вообще — метрика так себе. У меня есть библиотека со 100% покрытием, и я там буквально на днях нашел бажину размером с тропического таракана. Один из наших старых микросервисов, написанных мной 10 лет назад за неделю и приносящий десятки миллионов ежегодно — не покрыт юнитами вовсе, потому что он полностью завязан на внешние источники, и единственный вариант его тестирования — элегантный отказ в продакшене с очень подробным логом и немедленная подстройка под изменившийся без объявления войны интерфейс третьесторонних API.

SOLID принципы решают все проблемы

Ой, ну да, серебряная пуля. Единственная ответственность, открытость для расширения, подстановка Лисков, разделение интерфейсов, инверсия зависимостей — звучит это всё как заклинания из Гарри Поттера, и примерно настолько же применимо в реальной жизни. Попытка следовать всем пяти принципам одновременно превращает простой CRUD в архитектурный шедевр из двадцати слоёв абстракции, где для добавления одного поля в форму нужно изменить семь интерфейсов, создать три новых класса и обновить конфигурацию dependency injection контейнера. А потом приходит новый разработчик, смотрит на всё это великолепие и спрашивает: «А нельзя было просто поле в базу добавить?».


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

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

Вот только формализуется такой подход очень плохо (именно этому факту мы обязаны появлением такого странного термина, как «идиоматический код»). Продать книжку миллионными тиражами, или курсы, или консалтинг — практически невозможно. Поэтому гораздо выгоднее выдумать термин, построить вокруг него теорию, показать на игрушечных примерах, что всё работает… — И заняться чем-то ещё, например, начать разводить тритонов.

Кто узнал отсылку из предыдущего предложения — тот трухакер.