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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Я стараюсь писать скучный код.
Поделиться с друзьями
-->

Комментарии (24)


  1. Lailore
    27.04.2017 13:02
    -2

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


  1. Idot
    27.04.2017 13:29
    +8

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


  1. Indemsys
    27.04.2017 13:50
    -11

    Почему когда начинают про хорошесть кода, не говорят об IDE и просто редакторах?
    А мне кажется, что именно IDE и определяет как будет писаться код и почему он выглядит у других так или иначе.


    1. shamyyl
      27.04.2017 16:14
      +4

      Наверно потому, что IDE может помочь только с проблемой «читаемости». Но абсолютно бесполезно, если код оброс зависимостями.
      Под «хорошим» же кодом подразумевают, как и удобность чтения, так и удобность внесения изменений (то бишь меньшую зависимость компонентов друг от друга).


      1. Indemsys
        27.04.2017 16:45
        -6

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

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


        1. mayorovp
          27.04.2017 17:02

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


          1. Indemsys
            27.04.2017 17:24
            -4

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

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

            Хотя может тут стоит спорить о термине «зависимость».
            Я честно в контексте статьи перестал его понимать.


            1. poxvuibr
              28.04.2017 09:43
              +1

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

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


  1. 80x86
    27.04.2017 14:17
    +20

    «Не надо трёпа. Просто покажи свой код.»


  1. senpay
    27.04.2017 14:56

    А можно ссылку на оригинал?


    1. Ivanq
      27.04.2017 15:17
      +3

      1. Crandel
        27.04.2017 15:57
        +18

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


        1. tangro
          27.04.2017 16:15
          +8

          Уже не первое десятилетие подобные идеи высказываются :)


        1. MacIn
          27.04.2017 16:25

          Плюсую. Очень радуют авторы, которые в первой строке указывают ссылку — это, мол перевод вот этой статьи.


          1. tangro
            27.04.2017 18:16
            +2

            Это ведь как-раз о том, о чём и статья: такой подход со ссылкой вначале — удобен, но избыточен. Вариант Хабра неизбыточен, но основан на предположении, что все читатели понимают дизайн Хабра :)


            1. MacIn
              28.04.2017 22:17

              Есть баланс. Все можно есть ложкой, но вилки не отменяют.


          1. domix32
            28.04.2017 11:04

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


            1. MacIn
              28.04.2017 22:17

              Да кто угодно и как угодно. Удобно, только и всего.


        1. TimsTims
          27.04.2017 21:47
          +12

          НЛО прилетело и развело руками здесь


      1. senpay
        27.04.2017 18:52

        Спасибо!
        Все глаза проглядел в поиске доступной ссылки, и ни за что бы не догадался до этого варианта. Скорее всего завтра же забуду где таки тыкать :( Прощай карма.


  1. mayorovp
    27.04.2017 16:41
    +3

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


    1. tangro
      27.04.2017 18:21
      +1

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


  1. lxsmkv
    28.04.2017 02:00
    +3

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


  1. AIxray
    30.04.2017 23:20

    А где пример кода?