Авторизоваться
Аким Солянкин 5 дней назад Опубликована

5 уроков, которые я извлек за первые два года программирования

Мне платят за написание кода более двух лет. За это время мои взгляды на программирование сильно изменились.

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

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

Вы пишете код для людей, а не для компьютеров

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

for(let i=0;i<100;)console.log((++i%3?'':'Fizz')+(i%5?'':'Buzz')||i)

Вы, вероятно, узнаете, что это реализация классической задачи FizzBuzz. Это работает, но не сразу понятно, как работает код. Как насчет этого?

const { log } = console;
for (let i = 0; i < 100; i++) {
  if (i % 15 === 0) log("FizzBuzz");
  else if (i % 3 === 0) log("Fizz");
  else if (i % 5 === 0) log("Buzz");
  else log(i);
}

Первый пример может быть более умным, но, как мне кажется, второй намного легче читать, понимать и поддерживать. В большинстве случаев наша цель - последнее. Конечно, этот пример довольно надуманный, но по мере того, как требования нашего кода становятся более сложными, потребность в ясности становится только более важной.

Следовательно, хороший код - это код, написанный для других людей. У нас больше общего с традиционными писателями, чем мы думаем!

Люди не согласны с тем, что делает код читабельным

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

В моей текущей работе мы используем сокращения преобразования типов, например, + для числа и !! для логического значения. Но в своей предыдущей работе я научился избегать этого в пользу конструкторов Number и Boolean. Я также был свидетелем дебатов по поводу asyncawait против thencatch!

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

Вот пара более конкретных мыслей о читабельности кода.

1. Когнитивная сложность

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

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

В качестве быстрого примера, эта функция isPrime имеет когнитивную сложность 4:

const isPrime = num => {
  if (num <= 1) {                       // + 1
    return false;
  }
  for (let i = 2; i < num; i++) {       // + 1
    if (num % i === 0) {                // + 2
      return false;
    }
  }
  return true;
}

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

2. Комментарии

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

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

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

Вот простой пример:

const productCodes = [
  // t-shirts
  "c0c0c72b-cb60-4ff6-ac7c-623cc4497c9d",
  "169ce45b-03b9-4161-a9e3-aa319210c827",
  // hoodies
  "bd04d570-009f-4a4d-b8f5-58e2be0d44a2",
  "8067be8d-b302-457b-bd97-e297d3383118",
];

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

const tshirts = [
  "c0c0c72b-cb60-4ff6-ac7c-623cc4497c9d",
  "169ce45b-03b9-4161-a9e3-aa319210c827",
];
const hoodies = [
  "bd04d570-009f-4a4d-b8f5-58e2be0d44a2",
  "8067be8d-b302-457b-bd97-e297d3383118",
];
const productCodes = [...tshirts, ...hoodies];

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

Программирование - это все о данных

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

Конечно, сведение всего к этим основам не означает, что это всегда просто. По сути, создание самолета - это подъемная сила, лобовое сопротивление, тяга и вес. Я знаю это, но мне было бы бесполезно проектировать самолет!

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

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

Производительность не имеет значения (кроме случаев, когда это имеет значение)

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

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

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

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

Я также попадал в неприятные ситуации из-за слишком свободного использования Promise.all. Возьмите приведенную ниже функцию (основанную примерно на каком-то подлинном коде, который я написал):

async function purgeTestData() {
  await Promise.all([
    makeLotsOfDatabaseChanges(),
    makeLotsOfDifferentDatabaseChanges(),
    doSomethingElseWhichAlsoAffectsTheDatabase(),
  ]);
}

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

async function purgeTestData() {
  await makeLotsOfDatabaseChanges();
  await makeLotsOfDifferentDatabaseChanges();
  await doSomethingElseWhichAlsoAffectsTheDatabase();
}

Часто самый высокий уровень производительности требует высочайшего уровня контроля. Например, если бы мы хотели выжать из веб-сайта максимальную теоретическую производительность, нам, вероятно, было бы лучше без каких-либо библиотек или фреймворков. Или, может быть, нам следует использовать веб-сборку для написания всего кода на C ++? Есть веская причина, по которой крупные сложные проекты редко выбирают такой подход: он требует слишком много времени для реализации и слишком длительного времени для обслуживания.

В пользовательском интерфейсе много дыма и зеркал

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

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

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

Коментарии
Авторизоваться что-бы оставить комментарий
Присоединяйся в тусовку
Наш сайт использует файлы cookie для вашего максимального удобства. Пользуясь сайтом, вы даете свое согласие с условиями пользования cookie