Хороший код = скучный код
Эта статья появилась в дополнение к стандартам кода в нашей компании. Стандартизацией и ревью кода мы начали заниматься спустя 2 года совместной работы, когда я стал тим-лидом команды и когда количество проектов над которыми мы работаем и которые были на поддержке стало расти. Где-то 2 года назад, ко мне подошел тим-лид и сказал: “Ну вот то что у нас стандарты есть и они доступны новичкам быстро и удобно это хорошо. Но это много и по-технарски, а главное, новичку не понятно зачем их соблюдать. Давай напишем для всех и понятным языком, почему эти стандарты им надо использовать.” Текучка джунов на тот момента была большой и мне идея понравилась. Мы договорились, подключили обоих сеньоров, и по одному представителю от мидлов и джунов, с которыми обсудили стандарты и попытались найти ответ почему эти стандарты нужно использовать.
Вот что у нас получилось.
[sendpulse-form id=”278″]
Программисту нравится думать, что он пишет хороший код. Ну или, что он пишет больше хорошего кода, чем плохого. В чем преимущества скучного хорошего кода?
Простота отладки, чтения, объяснения
Первая особенность, хорошего кода, которая всегда бросается в глаза — его скука. Предсказуемые выражения, одно за другим. Нет сюрпризов, трюков, крайне мало уникальных случаев. Вторая и не менее важная его особенность – легкость в отладке, чтении, объяснении. Этот код не страшно передать тому, кто в первый раз садится за задачу по этому проекту и он не будет по каждой строчке обращаться к тому, кто работал с проектом раньше.
Выносимость за пределы проекта
Скучный код не использует глобальных состояний, не порождает побочных эффектов и старается уменьшить свою связанность с проектом, в котором он живёт. Скучнейшим образом происходят присвоения значений, которые для каждой переменной случаются лишь раз и избавляют нас от возможности увлекательного расследования кто же и когда изменил вот это состояние. Скучный код делает лишь то, что должен и не полагается на какие-то неявно высказанные предположения.
Код, использующий неявное поведение, может быть основан на каком-нибудь недокументированном, но уже реализованном функционале. Например, в мире написана целая куча НЕВЕРНОГО кода, который полагается на то, что функция файловой системы, возвращающая список директорий, вернёт их в отсортированном по алфавиту порядке. Это и вправду часто работает именно так, но ровно до того момента, пока не ломается по «непонятным» причинам. А на самом деле просто никто никогда этой сортировки не гарантировал.
Если написанный вами код опирается на какие-то неявные предположения — вам нужно держать их в голове, что уже само по себе усложняет поддержку такого кода. Вам просто не хочется возвращаться к работе над ним, ведь кроме чтения написанных срок придётся также строить в голове общую картину (с учётом её неявных частей). Это сложно. А ещё иногда неявные предположения ломаются из-за внешних факторов и тогда начинается мучительный процесс согласования кода с новой картиной мира.
К сожалению, лишенный неявности код тоже имеет свою цену. Это, как правило, избыточность. Приходится повторять некоторые паттерны, обрабатывать похожие ситуации похожим образом. Код, основанный на неявности, несёт в себе хрупкость и ужасные последствия в далёкой перспективе. Противоположный ему явный код таит меньше стратегических угроз, но требует усердия здесь и сейчас.
Скучный код берет лучшие практики из разных языков программирования
Переусердствовать с многословностью достаточно легко, но я всё же согласен с Питоновским Дзеном: «Явное лучше, чем неявное». Java, возможно, заходит в многословности слишком далеко и каждый раз, когда нам нужно прочитать все строки файла, приходится писать одни и те же несколько строк кода. Альтернативой этому будет какая-нибудь обёртка, которая возьмёт на себя эту обязанность, но лишит нас некоторой гибкости (а если нужно прочитать не все строки? а если не по порядку? а если не с начала? и т.д.).
Я пытаюсь взять лучшее из обоих миров, разделяя мой API на слои. «Нижний» слой написан в Java-стиле: маленькие компоненты, простое поведение, но требуются некоторые усилия, чтобы собрать из них что-то действительно полезное. «Верхний» слой ставит во главу человекочитаемость и практичность использования: пользователю будет легко использовать API правильным образом, поскольку сама его структура располагает к этому.
Скучный код – модульный или разделимый
Основной костяк механик кода назовем «модулями», а выполняющие вспомогательные задачи куски кода — «библиотеками». Помимо этого, удобно разделять код приложений на компоненты и фреймворки.
Компонент — это место, в котором живёт бизнес-логика приложения или сервиса, а фреймворк — это тонкий слой клея, который связывает воедино различные компоненты. Правильная декомпозиция приложения на отдельные компоненты — нетривиальная задача: нужно не только разбить некоторую сущность на несколько отдельных, но также чётко понимать почему это должно помочь.
Перефразируя отличное объяснение Дэвида Парнаса: компонент существует для того, чтобы спрятать от остальных компонентов системы какое-то сложное решение или решение, которое с высокой вероятностью может измениться в будущем.
Бизнес-логика, даже достаточно запутанная, не обязательно является тем Гордиевым узлом, который следует бросаться разрубать. Если вы не можете легко заменить компонент, возможно, не стоит начинать с разделения его на отдельные компоненты, ведь более важная его проблема в том, что он не скрывает достаточного от остальной системы.
Компоненты должны скрывать друг от друга бизнес-логику, модули должны скрывать свою реализацию, библиотеки должны прятать свои алгоритмы, а фреймворк не должен показывать все связывающие это воедино нити.
На практике границы между этими частями не всегда удаётся выдержать столь точно, как я их описал. Библиотеки влияют на модули, во фреймворки прокрадывается небольшая часть бизнес-логики, а некоторые компоненты не достаточно хорошо скрывают свои внутренности.
Подводя итоги
Старайтесь писать код, который не требует от вас держать в голове его полностью при чтении или изменении. Разделения кода на слои и дробление бизнес-логики на компоненты в этом помогает, но на самом деле просто старайтесь не писать код, который бы со временем вас смутил или, того хуже, заставил ужаснуться.
Старайтесь писать скучный код.