Остерегайтесь однодельных людей
Так чисто, что больно
Принцип единственной ответственности (Single Responsibility Principle) — одна из тех идей, которые звучат настолько разумно, что могут пройти мимо вашего критического мышления.
Делай одну вещь. Делай её хорошо. Держи модули сфокусированными. Дай коду причину для изменения. Хороший совет.
Затем кто‑то превращает совет в измерительную ленту и начинает объявлять, что любая функция более пяти строк — это запах кода.
Проблема не в SRP. Проблема в том, что «маленькое» воспринимается как замена «когезивному».
В этот момент вы встречаете людей с «единой целью»: разработчиков, которые не ошибаются в вопросе модульности, но перепутали полезные границы с максимальной фрагментацией.

I. Полезная идея под ней
Добавление одной галочки в форму должно, в идеале, затронуть только один файл. Не 8 файлов в 5 каталогах… Я смотрю на тебя, React/Redux.
Когда SRP применяется с рассудительностью, он действительно помогает. Юниты кода, сосредоточенные на одной концептуальной задаче, легче понять. Тесты могут проверять поведение на разумной границе. Чёткие модули упрощают изменение одной части системы, не тянув за собой остальное приложение.
Даже классические примеры из Unix более прагматичны, чем подразумевает слоган. ls выводит список файлов, да, но он также координирует вызовы вроде opendir, readdir, closedir и stat. Полезный юнит — это не самая маленькая возможная операция. Полезный юнит — это наименьшее связное целое, которое решает задачу.
Исходная философия Unix была о композиции и простой, а не о сведении всего к одной функции или файлу.
Это различие имеет значение. «Одна ответственность» — это не то же самое, что «одна строка поведения».
II. Over-Abstraction: When Simplicity Turns to Chaos
Наш архитектор считает, что любая функция длиннее 5 строк — это «запах кода». Теперь наш кодовый базис слегка пахнет безнадёжной отчаянностью.
Режим отказа легко заметить, когда он уже испортил вам неделю.
В кодовой базе стало больше файлов, но меньше структуры. Каждый вспомогательный элемент имеет своего помощника. Каждый концепт разбит по папкам, названным в честь технических ролей, а не смысловых названий продукта. Добавление чекбокса требует правки компонента, хука, селектора, действия, редьюсера, константы, тестовой фикстуры и «баррель‑экспорта», существующего в основном для того, чтобы пути импорта не выглядели виноватыми.

Что же всё это «чистое» принесло?
- Шрапнель файловой системы: Директории исходного кода разрастаются в ночные пейзажи бесчисленных крошечных файлов, часто содержащих одну, печально одинокую функцию. Навигация превращается в спелеологию.
- Запутанные зависимости: Сеть импортов и экспортов настолько плотна, что отслеживание выполнения требует большой доски и больше терпения, чем заслуживает фича. Файлы, импортированные ровно один раз, сидят там, притворяясь переиспользуемыми.
- Коварство тестов: Тесты становятся хрупкими, гипер‑специфичными стражами микроскопических деталей реализации. Меняете сигнатуру функции? Смотрите, как десятки тестов рушатся, как древняя керамика. Тестовый набор превращается из сетки безопасности в минное поле.
- Исчезновение скорости: Простые изменения метастазируют в эпопеи многократных правок файлов. Ввод новых разработчиков занимает недели, когда им раздают карты и компасы, лишь бы найти, где на самом деле живёт компонент
UserProfileна этой неделе. Прогресс замедляется до геологической ходьбы под тяжестью этой «организации».
Я смотрел в бездну кодовых баз, где простая 100‑строчная фича была рассекречена по более чем 15 файлам, каждый из которых был «чистым» маленьким ангелочком, содержащим одну‑две функции. Когнитивный радиус взрыва от попытки удержать этот беспорядок в голове полностью нивелировал любую теоретическую выгоду от разделения. Это не было проще; это просто было разбросано.
III. Цена совершенства: влияние на разработчиков
Мы тратим больше времени на дебаты о структуре файлов и конвенциях именования, чем на реальную поставку фич. Это Agile?

Эта патологическая фрагментация — не просто эстетическая проблема. Она меняет то, как разработчики расходуют своё внимание.
Утечка продуктивности: Забудьте о техническом долге; это организационный долг, накопившийся из‑за навязчивого вложения каталогов. Любая небольшая правка превращается в археологическую раскопку сквозь слои абстракций. Время исчезает в чёрной дыре cd .. и grep.
Налог на тестирование: Вместо того чтобы давать уверенность, набор тестов становится источником трения. Часы тают, пока исправляют тесты, сломанные тривиальными рефакторами, тесты, которые были слишком тесно связаны с микроскопическими деталями, которые они должны были проверять.
Когнитивная нагрузка: У человеческого мозга есть жёсткий предел количества разрозненных кусочков информации, которые он может одновременно держать в голове. Заставляя разработчиков собирать поток программы из дюжины разбросанных файлов, мы активно препятствуем пониманию и усложняем уверенные изменения.
IV. Принятие прагматизма: практическая альтернатива
Я предложил поместить две связанные функции в один файл. Комната отреагировала так, будто я предложил удалить staging. — Читатель‑пурист, находящийся в процессе восстановления
Выход из ситуации — не отказ от SRP. Ответ заключается в применении его на правильном уровне смысла.
Вот как это выглядит на практике:
- Сосредоточьтесь на связности, а не на атомах: Группируйте вещи, которые изменяются вместе и логически принадлежат друг другу. Модуль может обрабатывать несколько связанных аспектов аутентификации пользователя. Это нормально. Скорее всего, это лучше, чем шесть отдельных файлов, каждый из которых содержит одну функцию, связанную с состоянием входа.
- Держите родственные элементы вместе: Не разделяйте связанный код, если только нет очевидной, ощутимой выгоды — например, реальной переиспользуемости на практике, а не в гипотетическом будущем, которое никогда не наступит. Близость важна для понимания.
- Пусть реальность диктует: Организуйте структуру, исходя из фактических функций и рабочих процессов вашего приложения, а не из абстрактного идеала функциональной чистоты³. Делает ли такая структура проще или сложнее понять и изменить
Feature X? - Помните о «мясном» программном обеспечении: Не забывайте о бедном разработчике. Какая организация минимизирует умственное жонглирование, необходимое для работы с кодом? Оптимизируйте под человеческое понимание.
- Тестируйте то, что важно: Пишите тесты, проверяющие поведение на разумных границах, а не тесты, которые плотно привязаны к внутренней проводке каждой крошечной функции. Стремитесь к уверенности, а не к театральному проценту покрытий.
Цель — не теоретическое совершенство, достойное кандидатской диссертации; а создание кода, по которому ваши коллеги (и будущий вы) смогут ориентироваться, понимать и изменять его, не желая поджигать здание.
Иногда это означает, что файл будет длиной 200 строк вместо 50. Иногда функция будет одновременно получать данные и слегка их трансформировать. Иногда класс будет иметь две ответственности, настолько тесно связанные, что их следует держать вместе. Если это упрощает работу с системой в целом, скорее всего, это правильное решение.
Оставайтесь безустанно сосредоточенными на практических вопросах:
- Может ли новичок быстро сориентироваться?
- Можем ли мы изменить
X, не ломая несвязанныйY? - Действительно ли этот тест показывает, работает ли функция?
- Мы поставляем ценность или просто переставляем папки?
V. Заключение: Содействие связному и поддерживаемому коду
Принцип единственной ответственности — полезный инструмент. Это не приказ разбивать ваш код на атомарный пыль. Как любой инструмент, его ценность определяется суждением того, кто им пользует.
Поэтому, когда вы сталкиваетесь с «людьми‑единой цели», готовыми вести войну против любой функции, осмелевающей превысить три строки, сделайте вдох. Вспомните чек‑лист из 12 файлов.
Наша задача — не создавать теоретически безупречные «снежинки‑функции». Наша задача — писать программное обеспечение, которое работает, решает задачи и не наказывает следующего человека, которому придётся с ним работать.
Оставайтесь прагматичными. Сосредоточьтесь на результатах. Не позволяйте погоне за идеальной чистотой стать врагом поддерживаемого кода. От этого зависят ваше здравие и скорость команды.
¹ Ирония в том, что достижение реального единственного назначения на самых низких уровнях требует огромной сложности, скрытой чуть ниже поверхности.
² Здесь речь идёт о концептуальной чистоте: идея, что функция должна выполнять лишь «одну вещь» логически. Не путайте это с понятием «чистой функции» в функциональном программировании, у которой нет побочных эффектов — это другая, хотя иногда и смежная, идея.