Хорошо это или плохо, но компьютеру все равно, как выглядит ваш код. Разница между

function sumNumbers(...listOfNumbers) {
  let sum = 0;

  listOfNumbers.forEach(
    number => {
      sum += number;
    }
  );

  return sum;
}

и

function sumNmb  (...a) 
{
    let b = 0;
  for (c= 0; c <   a.length; 
    c ++) {
  
b = a[c] + b
  } return b
}

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

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

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

Компьютеру все равно

Неудивительно, что компьютеры думают иначе, чем мы, люди. Компьютеры отлично следуют инструкциям в точности так, как они перечислены, но довольно плохо интерпретируют расплывчатые инструкции. Люди не так хорошо точно следуют инструкциям (особенно далеко не так быстро), но мы способны понимать концепции разными способами.

Например, если ввести в консоль REPL (цикл чтения-оценки-печати):

> 1 + 1

Мне бы дали возврат:

> 1 + 1
2
> 

Но подумайте, если бы кто-то задал тот же вопрос, но в другом формате:

> add 1 and 1
ERR: ...

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

Люди также склонны искать закономерности даже там, где их быть не должно. Например, можно легко сбить кого-то с толку следующим трюком:
«Обжаривание заклинаний».
«Р-О-А-С-Т»
«Побережье заклинаний».
«К-О-А-С-Т»
«Что вы кладете в тостер?»
Если субъект не обращал пристального внимания на предоставленные вами подсказки, он мог нечаянно выпалить «Тост» в ответ на последний вопрос. Наш мозг улавливает закономерности, такие как рифмование слов жаркое и берег, что может препятствовать нашей способности точно следовать инструкциям.

Точно так же код, который содержит непоследовательную организацию или стиль, может заставить читателей уловить шаблоны, которых на самом деле не существует. Отступы — один из самых простых, но самых надежных способов обеспечить удобочитаемость кода. Учитывая первый пример, приведенный во введении, чистая версия содержит согласованные отступы, чтобы показать, на каком уровне находятся строки кода (внутри функции, внутри параметров forEach, внутри обратного вызова forEach и т. д.). Беспорядочный пример имеет отступы повсюду, что очень затрудняет группировку фрагментов кода, которые должны быть вместе. Но на интерпретацию этого кода компьютером не влияют ни отступы, ни имена переменных. Оба примера работают и возвращают одни и те же решения, добавляя каждое из переданных им чисел в качестве параметров.

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

Примечание. Некоторые языки, например Python, учитывают отступы при выполнении кода. Свобода делать отступы, не опасаясь вмешательства программы, НЕ является универсальной истиной.

В знак признательности за «хороший» код

Итак, в чем может быть польза от написания «хорошего» кода? Какая разница, если это работает? Что, если вы единственный человек, который когда-либо будет смотреть на это? К сожалению, человеческая память, даже о нашей собственной работе, слаба. Я просмотрел проекты, написанные всего несколько недель назад, и мне пришлось перечитать их, чтобы снова понять. Кроме того, написание организованного и аккуратного кода чрезвычайно полезно для будущей отладки, новых дополнений, обмена с другими, снижения потребности в комментариях, снижения стресса и ускорения разработки.

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

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

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

// Returns the sum of a list of numbers.
function sumNmb  (...a) 
{
    let b = 0; // sets initial value of sum to 0
  for (c= 0; c <   a.length; 
    c ++) { // iterates through the list of numbers
  
b = a[c] + b // adds each number to the sum
  } return b // returns the sum
}

Конечно, теперь читатель может лучше понять, что делает код, но теперь это требует гораздо больше работы со стороны кодировщика (написание комментариев) и немного больше времени и терпения со стороны читателя (чтение кода). себя и комментарии). Не говоря уже о том, что этот код выглядит еще более грязным и пугающим со всеми этими дополнительными символами из добавленных комментариев!

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

// Returns the sum of a list of numbers.
function sumNumbers(...listOfNumbers) {
  let sum = 0;

  listOfNumbers.forEach(
    number => {
      sum += number;
    }
  );

  return sum;
}

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

Читатели могут легко увидеть, что переменная суммы объявляется и инициализируется значением 0, затем список чисел (в отличие от «a», как это было названо в запутанном примере) повторяется с помощью метода JavaScript forEach. Каждое число в списке чисел затем добавляется к сумме. Наконец, сама сумма возвращается из функции. Даже название функции (sumNumbers vs sumNmb) легче понять!

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

Простые способы улучшить организацию кода

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

Разрывы строк и интервалы
Большая часть организации связана с разделением. Самый простой способ разделить разделы кода — использовать разрывы строк или пробельные символы. Ниже приведены несколько основных примеров того, где может быть полезно простое использование пробелов.

Между операторами и операндами:

var i=1+2*4/(4-1);

// VS

var i = 1 + 2 * 3 / (4 - 1);

Это помогает различать отдельные части выражения и предотвращает путаницу символов.

В — между блоками сгруппированного кода:

function example(param) {
  let variable = 1;
                      // whitespace before for loop
  for (let i = 1; i <= param; i++) {
    variable += i;
  }
                      // whitespace after for loop, before other code
  variable -= 3 * param;
                      // whitespace between other code and return statement
  return variable;
}

Это помогает визуально сгруппировать строки кода, которые тесно связаны друг с другом, и разделить другие разделы/шаги с пробелом между ними.

Разрывы строк, чтобы избежать переполнения:

// one line
if (i !== 15 && 15 % 10 + i < 12 || example(i + 12) / 2 < 34) console.log("The condition is true!");

// a few lines
if (i !== 15 && 15 % 10 + i < 12 
    || example(i + 12) / 2 < 34) {
      console.log("The condition is true!");
    }

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

Функционализация
Функционализация кода — это процесс разбиения похожих фрагментов кода на многократно используемые функции. Стандартной практикой является сокращение избыточности кода за счет функционализации, но функции также можно использовать для улучшения удобочитаемости путем абстрагирования логики, чтобы уменьшить переполнение. Например, можно расширить функцию sumNumbers до функции getAverage, добавив немного в строку возврата:

// Return the average of a list of numbers
function getAverage(...listOfNumbers) {
  let sum = 0;

  listOfNumbers.forEach(
    number => {
      sum += number;
    }
  );

  return sum / listOfNumbers.length;
}

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

// Return the average of a list of numbers
function getAverage(...listOfNumbers) {
  const sum = sumNumbers(...listOfNumbers);
  return sum / listOfNumbers.length;
}

Теперь читателю предоставляется гораздо более сжатая и абстрактная функция для чтения.

Имена переменных
Имена переменных, как и многие другие организационные концепции, не имеют большого значения для компьютера, если они различны, соответствуют правилам языка программирования для имен переменных и имеют правильный тип. . Однако для людей имена переменных могут улучшить или испортить удобочитаемость вашего кода. В исходном запутанном примере использовались идентификаторы переменных a, b и c. Хотя они могут быть компактными, эти имена не дают контекста относительно того, что они делают. Затем читатели должны будут сами определить, что есть что, и даже у автора оригинала могут возникнуть проблемы с запоминанием того, что есть что. Чистый пример использует гораздо более описательные, но все же достаточно краткие имена для переменных. Нет никаких сомнений в том, на что могут ссылаться переменные listOfNumbers, sum или number. Учтите, что имена переменных являются одной из немногих частей кода, которые могут быть написаны на простом языке, поэтому они могут быть очень полезными для человека, который думает в основном на простом языке.

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

Краткие и описательные комментарии
Комментарии отлично подходят для прямой передачи информации о коде самому себе или другим разработчикам в будущем. Как упоминалось ранее, их следует ограничить. Явный признак плохого кода — слишком много комментариев, код должен говорить сам за себя. Лучше всего, чтобы комментарии были краткими, не слишком длинными, но выполняющими свое назначение. Рассмотрим следующие два комментария, которые могут предшествовать функции sumNumbers:

// This function takes in a list of numbers and stores them as an array.
// Then, it parses through the array to find the sum of all numbers in
// the array. Lastly, it returns that sum.

// VS

// Returns the sum of a list of numbers.

// VS

// Returns the sum.

Все три комментария правильно описывают функцию на каком-то уровне, но второй делает это лучше всего. Первый комментарий — это точное описание процесса суммирования чисел в списке. Третий комментарий не дает достаточного контекста относительно того, что делает функция. Он сообщает читателю, что возвращает сумму, но сумму чего? Второй комментарий представляет собой абстракцию общей цели функции, включая всю информацию, которую должен знать потенциальный пользователь функции. Добавляя комментарии, обязательно учитывайте, что будущему пользователю (вам или другому разработчику) необходимо знать на поверхностном уровне, чтобы понять эту функцию.

Псевдокод и планирование на будущее
При написании больших и сложных программ может быть трудно понять, с чего начать. Написание больших программ без конкретного плана может привести к отсутствию организации или согласованности между различными частями программы. Возможно, у разработчика могут быть разные представления о том, как структурировать код при разработке разных частей проекта. Чтобы помочь в последовательности, обязательно найдите время, чтобы спланировать широкомасштабную организацию программ, которые вы пишете. Подумайте о том, чтобы составить список всех функций, которые вы планируете добавить, прежде чем начать, и отложите добавление новых до тех пор, пока существующий список не будет завершен (см. Расползание функций). Это поможет вам, разработчику, спланировать проект и узнать, что необходимо в каждой части для достижения этого списка. Когда список функций будет готов, следующий шаг зависит от того, что вы планируете создавать и какие языки/фреймворки планируете использовать. Например, при создании веб-сайта в React.js можно создать подробную иерархию компонентов, чтобы управлять их дизайном. Наличие надежного и подробного плана поможет вам понять, что необходимо сделать, уменьшить потребность в редизайне и разбить проект на более управляемые части.

В меньшем масштабе рассмотрите псевдокод или написание кода для решения проблем на простом языке. Псевдокод — это, по сути, написание плана решения проблемы кодирования. Например, псевдокод для функции sumNumbers может выглядеть так:

// 1. create sum variable, set to 0
// 2. Iterate through each number in the list
  // 2a. add each number to the sum
// 3. Return the sum.

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

Заключение

Это строгая иерархия приоритетов, которой нужно следовать при программировании:

  1. Мой код работает? Решает ли это необходимые проблемы?
  2. Читается ли мой код? Легко ли понять мою логику?
  3. Эффективен ли мой код?

Очевидно, что основная цель программирования состоит в том, чтобы иметь работающий код, успешно взаимодействовать с компьютером. Но разница между хорошим и плохим кодом больше в способности сделать этот код понятным для других людей. Когда код чист и организован, его намного легче редактировать и сделать более эффективным (и гораздо сложнее сделать наоборот). Тем, кто заинтересован в дополнительных идеях по улучшению организации кода, рекомендуется посетить следующие ресурсы:

4 стратегии организации кода

Как организовать код (для начинающих)

10 советов по написанию более чистого кода

6 заповедей написания чистого кода

Спасибо за ваше время в чтении!