Как я пишу код

Original author: tef
  • Translation
Мне нравится думать, что я пишу хороший код. Ну или, что я хотя бы пишу больше хорошего кода, чем плохого.

Моя любимая особенность хорошего кода — это его скука. Предсказуемые выражения, одно за другим. Никаких сюрпризов, никаких трюков, никаких уникальных случаев. Никакого мета-программирования, конечно! Скучный код очень легко отлаживать, читать, объяснять.

Скучный код не использует глобальных состояний, не порождает побочных эффектов и старается уменьшить свою связанность с проектом, в котором он живёт. Скучнейшим образом происходят присвоения значений, которые для каждой переменной случаются лишь раз и избавляют нас от возможности увлекательного расследования кто же и когда изменил вот это состояние. Скучный код делает лишь то, что должен и не полагается на какие-то неявно высказанные предположения.

Код, использующий неявное поведение, может быть основан на каком-нибудь недокументированном, но уже реализованном функционале. Например, в мире написана целая куча НЕВЕРНОГО кода, который полагается на то, что функция файловой системы, возвращающая список директорий, вернёт их в отсортированном по алфавиту порядке. Это и вправду часто работает именно так, но ровно до того момента, пока не ломается по «непонятным» причинам. А на самом деле просто никто никогда этой сортировки не гарантировал.

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

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

Переусердствовать с многословностью достаточно легко, но я всё же согласен с Питоновским Дзеном: «Явное лучше, чем неявное». Java, возможно, заходит в многословности слишком далеко и каждый раз, когда нам нужно прочитать все строки файла, приходится писать одни и те же несколько строк кода. Альтернативой этому будет какая-нибудь обёртка, которая возьмёт на себя эту обязанность, но лишит нас некоторой гибкости (а если нужно прочитать не все строки? а если не по порядку? а если не с начала? и т.д.).

Я пытаюсь взять лучшее из обоих миров, разделяя мой API на слои. «Нижний» слой написан в Java-стиле: маленькие компоненты, простое поведение, но требуются некоторые усилия, чтобы собрать из них что-то действительно полезное. «Верхний» слой ставит во главу человекочитаемость и практичность использования: пользователю будет легко использовать API правильным образом, поскольку сама его структура располагает к этому.

Два слоя API — это то, что можно увидеть в одной из моих любимых Python-библиотек — «requests». Она предоставляет невероятно выразительный и человекочитаемый API, который покрывает большинство вариантов использования, которые могут понадобиться на практике. Но внутри библиотека использует urllib3, где и происходит основная работа с протоколом HTTP. Да, спроектировать такую систему было непросто, и во внутренней реализации requests, возможно, есть некоторая избыточность, но какое удобство использования это дало её пользователям!

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

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

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

Бизнес-логика, даже достаточно запутанная, не обязательно является тем Гордиевым узлом, который следует бросаться разрубать. Если вы не можете легко заменить компонент, возможно, не стоит начинать с разделения его на отдельные компоненты, ведь более важная его проблема в том, что он не скрывает достаточного от остальной системы.

Компоненты должны скрывать друг от друга бизнес-логику, модули должны скрывать свою реализацию, библиотеки должны прятать свои алгоритмы, а фреймворк не должен показывать все связывающие это воедино нити.

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

Я стараюсь писать код, который не требует от меня держать в голове его полностью при чтении или изменении. Мне кажется, что разделения кода на слои и дробление бизнес-логики на компоненты в этом помогает, но на самом деле я просто стараюсь не писать код, который бы со временем меня смутил или, того хуже, заставил ужаснуться.

Я стараюсь писать скучный код.
Инфопульс Украина
Creating Value, Delivering Excellence
AdBlock has stolen the banner, but banners are not teeth — they will be back

More
Ads

Comments 25

    –2
    Не показывайте эту статью фанатам перфоманса в попугаях :)
      +8

      Где пример кода?

        –11
        Почему когда начинают про хорошесть кода, не говорят об IDE и просто редакторах?
        А мне кажется, что именно IDE и определяет как будет писаться код и почему он выглядит у других так или иначе.
          +4
          Наверно потому, что IDE может помочь только с проблемой «читаемости». Но абсолютно бесполезно, если код оброс зависимостями.
          Под «хорошим» же кодом подразумевают, как и удобность чтения, так и удобность внесения изменений (то бишь меньшую зависимость компонентов друг от друга).
            –6
            Но хороший редактор может упростить прослеживание зависимостей до такой степени что даже можно от них не избавляться.
            Опять же зависимости укорачивают код.
            Не факт что более длинный код без зависимостей будет читаться лучше чем короткий с зависимостями.

            Можно ведь еще сказать все что не помещается на один экран — это код с неявными зависимостями.
            Значит чем больше экран редактора тем меньше неявных для читателя зависимостей.
            Опять упираемся в редактор.
              0

              У вас ложная дихотомия. Обычно как раз получается выбор между длинным кодом с зависимостями и коротким без них: каждая ненужная зависимость приводит к удлинению кода.

                –4
                Неплохо бы аргументировать ложность дихотомии.

                Я скажем приведу такой сценарий зависимости:
                Программному модулю нужно получить данные из другого программного модуля.
                Простейший путь — объявить их публично доступными и обращаться к ним из всех других модулей.
                Это создание зависимости.
                А ее ликвидацией будет реализация кода явной передачи данных нуждающимся в них модулям неким дополнительным механизмом.
                Но хороший редактор вам покажет все зависимости по месту объявления этих данных. Так зачем делать лишнюю работу?

                Хотя может тут стоит спорить о термине «зависимость».
                Я честно в контексте статьи перестал его понимать.
                  +1
                  Например, в мире написана целая куча НЕВЕРНОГО кода, который полагается на то, что функция файловой системы, возвращающая список директорий, вернёт их в отсортированном по алфавиту порядке.

                  Вот например классическая зависимость.

          +20
          «Не надо трёпа. Просто покажи свой код.»
            0
            А можно ссылку на оригинал?
              +3
                +18

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

                  +8
                  Уже не первое десятилетие подобные идеи высказываются :)
                    0
                    Плюсую. Очень радуют авторы, которые в первой строке указывают ссылку — это, мол перевод вот этой статьи.
                      +2
                      Это ведь как-раз о том, о чём и статья: такой подход со ссылкой вначале — удобен, но избыточен. Вариант Хабра неизбыточен, но основан на предположении, что все читатели понимают дизайн Хабра :)
                        0
                        Есть баланс. Все можно есть ложкой, но вилки не отменяют.
                        0

                        Так обычно пишут люди из песочницы, ибо не имеют возможности обозначить статью как перевод

                          0
                          Да кто угодно и как угодно. Удобно, только и всего.
                        +12
                        НЛО прилетело и развело руками здесь
                        0
                        Спасибо!
                        Все глаза проглядел в поиске доступной ссылки, и ни за что бы не догадался до этого варианта. Скорее всего завтра же забуду где таки тыкать :( Прощай карма.
                      +3

                      В целом согласен, но за что досталось метапрограммированию?..

                        +1
                        Тяжело об этом рассуждать без привязки к языку и платформе, но его часто используют для грязных хаков (добраться куда не предполагалось) и чтобы понтануться (глядите как я умею). Полезные применения тоже, конечно, имеются, но имидж метапрограммирования испорчен первыми двумя случаями.
                        +3
                        Я пишу код так, чтобы я за один перекур мог объяснить архитектуру от начала до конца любому новобранцу.
                          0
                          А где пример кода?
                            +2
                            Слово «скучный» мне в данном контексте не понравилось.

                            Когда написал хорошее решение, которое изящно, штатно, без нагромождений и ненужных зависимостей решает твою задачу так, словно все ее решали за тебя, а тебе осталось только взять и положить,
                            Когда чувствуешь, что ты нашел то самое, где не надо париться, а все уже было сделано и надо было просто узнать что готовое решение лежало вот в той библиотеке.
                            Когда с первого раза после написанного у тебя build succesfull и при этом 100% test passed

                            Это не скучно, это хочется похвастаться, но блин, то что только что произошло, поймет 2.5 человека, а из-за NDA им и детали рассказать нельзя.

                            Only users with full accounts can post comments. Log in, please.