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



1. Используйте понятные имена переменных и функций


Код гораздо легче читать, когда при его написании используют понятные, описательные имена функций и переменных. Вот не очень понятный код:

function avg (a) {
  let s = a.reduce((x, y) => x + y)
  return s / a.length
}

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

function averageArray (array) {
  let sum = array.reduce((number, currentSum) => number + currentSum)
  return sum / array.length
}

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

2. Пишите короткие функции, решающие одну задачу


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

function sumAndAverageArray(array) {
  let sum = array.reduce((number, currentSum) => number + currentSum)
  return sum / array.length
}

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

function sumArray(array) {
  return array.reduce((number, currentSum) => number + currentSum)
}

function averageArray(array) {
  return sumArray(array) / array.length
}

Признаком того, что функцию можно разбить на две, является возможность использования слова «and» в её имени.

3. Документируйте код


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

function areaOfCircle (radius) {
  return 3.14 * radius ** 2
}

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

const PI = 3.14 // Число Пи, округлённое до двух знаков после запятой

function areaOfCircle (radius) {
  // Функция реализует математическую формулу вычисления площади круга:
  // Число Пи умножается на квадрат радиуса круга
  return PI * radius ** 2
}

Этот код — всего лишь пример. Вероятно, в подобной ситуации, вместо введения собственной константы, хранящей число Пи, лучше будет воспользоваться стандартным свойством Math.PI.

Комментарии к коду должны отвечать на вопрос «почему».

Обратите внимание на то, что для целей документирования кода имеет смысл воспользоваться специальными инструментами и соответствующими им правилами комментирования кода. В применении к Python мне нравится Google Style Docstrings, в применении к JavaScript — JSDoc.

4. Подумайте об использований правил Сэнди Метц


Сэнди Метц отлично программирует на Ruby, делает интересные доклады и пишет книги. Она сформулировала четыре правила написания чистого кода в объектно-ориентированных языках. Вот они.

  1. Классы не должны быть длиннее 100 строк кода.
  2. Методы и функции не должны быть длиннее 5 строк кода.
  3. Методам следует передавать не более 4 параметров.
  4. Контроллеры могут инициализировать лишь один объект.

Рекомендую посмотреть её выступление, касающееся этих правил.

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

Обратите внимание на то, что данные правила — это лишь рекомендации, но их использование позволит сделать ваш код значительно лучше.

5. Применяйте выбранные правила последовательно


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

Рекомендую применять руководство по стилю кода и линтер, который позволяет приводить код к выбранному стандарту. Например, мне, для JavaScript, нравятся правила Standard JS, для Python — правила PEP8.

На самом деле, главное здесь — найти правила оформления кода и их придерживаться.

6. Помните о принципе DRY


Одна из первых идей, которую стараются донести до того, кто хочет стать программистом, звучит так: «не повторяйся» (Don’t Repeat Yourself, DRY). Если вы замечаете в своих проектах повторяющиеся фрагменты — используйте такие программные конструкции, которые позволят сократить повторы одного и того же кода. Я часто советую моим ученикам поиграть в игру SET для того, чтобы улучшить их навыки по распознаванию шаблонов.

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

В результате, не стоит повторяться, но и не стоит, в борьбе за соблюдение принципа DRY, видоизменять код до такой степени, что он может стать трудным для понимания.

7. Используйте идеи инкапсуляции и модульности


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

let name = 'Ali'
let age = 24
let job = 'Software Engineer'

let getBio = (name, age, job) => `${name} is a ${age} year-old ${job}`

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

class Person {
  constructor (name, age, job) {
    this.name = name
    this.age = age
    this.job = job
  }

  getBio () {
    return `${this.name} is a ${this.age} year-old ${this.job}` 
  }
}

А если в программе надо работать лишь с данными об одном человеке, то их можно оформить так, как показано ниже.

const ali = {
  name: 'Ali',
  age: 24,
  job: 'Software Engineer',
  getBio: function () {
    return `${this.name} is a ${this.age} year-old ${this.job}` 
  }
}

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

Итоги


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

Уважаемые читатели! Каких правил оформления кода придерживаетесь вы?

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


  1. peresada
    30.07.2018 13:54
    +3

    Вы серьезно? Если бы хоть что-нибудь новое было в статье — было бы полезно, а так — это масло кажется уже десятки лет мажется и мажется. мажется и мажется.

    Продолжение
    мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. мажется и мажется. м


    1. stepmex
      30.07.2018 14:01
      +3

      Да ладно вам, человек старался, переводил, а вы так на него нападаете))
      Меня вот позабавило. Особенно мне понравилось

      const PI = 3.14 // Число Пи, округлённое до двух знаков после запятой
      Math.PI для слабаков, только свои константы, только хардкод!


      1. peresada
        30.07.2018 14:06

        На переводчика нападать обычно имеет смысл, только если есть косяки в переводе, это скорее крик души, нежели чем, реальная критика :)


        1. k4ir05
          31.07.2018 09:26

          А как насчёт JavaScript в заголовке? В оригинале его нет, и к содержанию он отношения не имеет никакого.


      1. shersh1k
        30.07.2018 15:09

        в образовании очень часто ответ может не совпадать если юзать константы или нечто подобное в предварительно не сокращенном виде как того требует задача. Навсегда это запомнил, когда прогорел на паре задач по физике в далеком 2011 году :)


    1. staticlab
      30.07.2018 15:45

      Потому что статья лишь повод прорекламировать свои сервисы.


  1. Iqorek
    30.07.2018 14:28
    +1

    Используйте понятные имена переменных и функций

    function averageArray (array) {

    Используйте максимально короткие и при этом максимально информативные имена.
    function average(array)


    Пишите хорошую документацию к своему коду

    Только если код нельзя написать читабельным или есть необходимость предостеречь потомков о каких то неочевидных вещах. В остальных случаях пишите читабельный код без комментариев.

    4. Подумайте об использований правил Сэнди Метц

    Есть только 2 правила, которые работают всегда:
    1. Код должен делать то, что должен.
    2. Код не должен делать то, что не должен.
    Все остальные правила применяемые в тупую, ничего кроме ущерба не приносят.

    6. Помните о принципе DRY

    Есть только один принцип: код должен работать. Если вы будете ему следовать, никто никогда не спросит, каким принципам и правилам вы следовали.


    1. nafgne
      30.07.2018 14:39

      Есть только один принцип: код должен работать.
      Да-аа, а потом начинается вот такое :C


      1. Iqorek
        30.07.2018 15:36

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


        1. nafgne
          30.07.2018 16:07

          И поэтому давайте будем называть функции First, Second, Third, Second_1, Second_2?


          1. Iqorek
            30.07.2018 17:20
            -2

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


            1. nafgne
              30.07.2018 18:47

              Какой-то антимаксимализм..


  1. Sirion
    30.07.2018 14:34
    +1

    Смешались в кучу пони, люди… По степени структурированности очень напоминает классификацию животных Борхеса.
    Кстати, что из этих правил специфично именно для JS?


  1. mapron
    30.07.2018 14:35

    Мне одному кажется, что если убрать из названия Javascript, и написать там PHP, или C++, скажем — ничего не изменится? Это какие-то базовые гигиенические принципы работы с любым кодом.


  1. mynameco
    30.07.2018 14:37
    +2

    Документировать нужно только если код делает чтото непонятное, страшное и неочевидное.


    1. gnaeus
      30.07.2018 15:29

      Документировать (в смысле JSDoc и т.п.) нужно всегда, когда есть время. То, что очевидно Вам после месяцев работы над проектом, может быть совершенно непонятно Вашему коллеге через год.

      «Пишите код так, как будто сопровождать его будет склонный к насилию психопат, который знает, где вы живёте» ©


      1. mynameco
        30.07.2018 15:45
        +1

        Все мы знаем последнюю цитату, но она к коментариям не относится. Опыт показывает что коментарии устаревают после первого же рефакторинга. Т.е. они не то что бесполезны, они вводят в заблуждение. Редко кто читая коментарий проверяет что делает функция.


        1. gnaeus
          30.07.2018 16:18

          Ну так и я не про комментарии, а про документацию в коде. То, из чего потом можно сделать swagger или wiki-странички с помощью различных инструментов. И как раз такие докстринги и позволяют поддерживать документацию актуальной.


          Примеры:


          • Докстринги для типов и полей доменных объектов будут служить глоссарием для программного модуля. В противном случае это будет вынесено в какой-нибудь вордовский файл или wiki. И через несколько итераций полностью потеряет связь с реальностью.
          • Докстринги к экспортируемым функциям будут служить дополнительным описанием контракта модуля. Иначе, чтобы понять что делает модуль нам нужно либо прочитать его код, либо прочитать его юнит-тесты.
          • Из докстрингов к props для React-компонента можно слепить сайт с документацией и примерами использования.


          1. biziwalker
            30.07.2018 20:02

            По поводу описания типов в докстрингах, если есть время упариваться, то лучше всё-таки вместо это выбрать Flow или TypeScript — так актуальность типов будет лучше поддерживаться, да и получите хорошие инструменты статического анализа :)


            1. gnaeus
              31.07.2018 09:59

              Так я уже несколько лет так и делаю :)

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


  1. olvin_hh
    30.07.2018 15:16

    Методы и функции не должны быть длиннее 5 строк кода.

    Да ладно вам, разве это возможно?)


    1. Sirion
      30.07.2018 15:18
      +3

      Возможно, но я бы скорее назвал это методом обфускации.


    1. faiwer
      30.07.2018 15:46

      Бывает, что code-style не приемлет египетских кавычек.


      function someFunction(something)
      {
          if(something)
          {
              handleOne();
          }
          else
          {
              handleTwo();
          }
      }

      Простейшая функция с if-else вышла в 11 строк :)


      1. dom1n1k
        30.07.2018 18:05
        +1

        Ой да ладно, вполне очевидно, что там имеются в виду содержательные строки без скобок. И скорее всего только внутренности функции без заголовочной строки/строк с именем и параметрами.

        Хотя согласен, что 5 строк всё равно жесковато. Я лично для себя вывел следующие пороги:
        а) ~10 sloc — стоит подумать о декомпозиции, вполне возможно, она тут уместна;
        б) ~30 sloc — декомпозиция нужна с вероятностью 99%.


      1. Raftko
        30.07.2018 18:09
        +1

        Мне кажется здесь имеются ввиду «информативные» строчки


    1. Tyusha
      31.07.2018 04:30

      Если задаться такой целью, то конечно можно писать методы в 5 строк. Вот только станет ли лучше: короче и понятнее. Вероятно нет.


  1. amaksr
    30.07.2018 17:14

    Что-то не удалось найти на гитхабе автора никаких коммитов в реальный код сложных проектов, только блог и учебные материалы для студентов. Может не стоит все, что увидели, тащить на Хабр?


  1. a-tk
    30.07.2018 19:21

    Первые 3-4 — это скорее антипаттерны даже.
    // это мост.


  1. sokal32
    31.07.2018 01:38

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


    1. torbasow
      31.07.2018 15:26

      У аутистов, как будто, с площадью круга проблем быть не должно.


  1. Tyusha
    31.07.2018 04:38

    Оформление кода это о том писать

    myFunction (arg)

    или
    myFunction( arg )

    а не о стиле программирования