Узнайте, как очистить свой код, как настоящий воин

Эй, воины кода!

Вы устали бродить по морю спагетти-кода?

Вы жаждете сладкого, сладкого объятия чистого кода (что бы это ни значило)?

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

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

Что такое чистый код и почему это важно?

Все мы знаем, что писать код легко. Но писать чистый, поддерживаемый код? Ну, это другая история.

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

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

Шутки в сторону, «чистый код» — это хорошо организованный, хорошо документированный и свободный от ненужного беспорядка код. Чистый код — это код, который сможет понять даже ваша бабушка (если, конечно, она умеет программировать).

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

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

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

Поговорим о «переменных».

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

Но это не означает, что вы можете бесплатно называть свои переменные в честь своего кота или любимого аниме-персонажа — хотя как здорово было бы получить задачу по устаревшему коду, который использует такие имена, как goku, vegeta и cell? — Увы, я отвлекся, даже если это звучит круто, это не так.

Итак, вот в чем дело: когда дело доходит до переменных, вам нужно давать им имена, которые действительно что-то значат. И под «чем-то» мы подразумеваем что-то, что имеет смысл в контексте вашего кода. Не называйте свои переменные просто «x», «y» или «z» и заканчивайте. Это просто лень.

Вместо этого используйте осмысленные имена, описывающие, что на самом деле делает переменная. Например, если вы сохраняете имя пользователя, вызовите переменную «userName» или «fullName». Если вы сохраняете адрес электронной почты пользователя, вызовите переменную «userEmail». Видите, как это легко?

Теперь мы знаем, о чем вы думаете. «Но как насчет всех этих длинных имен переменных? Не усложнят ли они написание моего кода?» Вот в чем дело: если вы используете хорошую IDE или используете типизированный язык с полуприличной IDE, у вас будет автозаполнение. Это означает, что вам не нужно каждый раз вводить полное имя переменной. Кроме того, ваши коллеги будут благодарны вам за использование описательных имен переменных, поскольку им не придется часами пытаться понять, что означает «xyz».

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

Пришло время обсудить «функции»

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

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

Что именно это значит? Это означает, что ваши функции должны делать одно и только одно. Они должны быть сфокусированными, краткими и простыми для понимания. В идеале они должны быть не более 10–20 строк (хотя это может варьироваться в зависимости от используемого вами языка).

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

Теперь я знаю, о чем вы думаете. «Но что, если моя функция должна делать много вещей?» Это справедливый вопрос. А ответ прост: вы ошибаетесь.

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

Давайте посмотрим на быстрый пример, проверьте следующую функцию:

function calculateSum(a, b, c, d, e, f) {
  let result = 0;
  result = a + b + c + d + e + f;
  if (result > 100) {
    console.log("The sum is greater than 100!");
  }
  return result;
}

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

Вот как мы можем очистить это:

function calculateSum(...numbers) {
  return numbers.reduce((acc, num) => acc + num, 0);
}

const sum = calculateSum(1, 2, 3, 4, 5, 6);

if (sum > 100) {
  console.log("The sum is greater than 100!");
}

А, намного лучше. Давайте разберем это.

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

Затем мы используем метод reduce(), чтобы сложить все числа. Это намного чище, чем запись result = a + b + c + d + e + f;, а также более масштабируемо, поскольку может обрабатывать любое количество аргументов.

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

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

Итак, подведем итог: пусть ваши функции будут короткими и приятными. Сосредоточьтесь на том, чтобы делать что-то одно и делать это хорошо. Не пытайтесь сделать слишком много в одной функции. И если вам нужно сделать много вещей, разбейте их на более мелкие функции. Ваш код (и ваше будущее «я») поблагодарит вас за это.

Комментарии — ваш друг, но не лучший друг

Теперь некоторые из вас могут подумать: «Но подождите, разве чистый код не сводится к удалению комментариев?» Ну да и нет. Цель чистого кода — сделать ваш код понятным, чтобы комментарии не были необходимы для понимания того, что происходит. Однако бывают ситуации, когда комментарии могут быть полезны, например:

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

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

Давайте рассмотрим пример плохого комментария:

// This function calculates the sum of two numbers
function add(a, b) {
  return a + b;
}

Фу, бесполезный комментарий. Мы ясно видим, что эта функция складывает два числа вместе, поэтому комментарий не добавляет никакого значения. Это просто шум, который загромождает код.

С другой стороны, вот пример хорошего комментария:

// This function uses a binary search algorithm to find the index of an element in an array
function binarySearch(arr, target) {
  let left = 0;
  let right = arr.length - 1;

  while (left <= right) {
    const mid = Math.floor((left + right) / 2);

    if (arr[mid] === target) {
      return mid;
    } else if (arr[mid] < target) {
      left = mid + 1;
    } else {
      right = mid - 1;
    }
  }

  return -1; // element not found
}

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

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

Другие практики чистого кода

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

Избегайте глобальных переменных

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

Вот пример кода с глобальной переменной:

let totalSales = 0;

function addSale(amount) {
  totalSales += amount;
}

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

Чтобы избежать этой проблемы, мы можем инкапсулировать состояние в замыкание или класс:

function createSaleCalculator() {
  let totalSales = 0;

  function addSale(amount) {
    totalSales += amount;
  }

  function getTotalSales() {
    return totalSales;
  }

  return {
    addSale,
    getTotalSales,
  };
}

const saleCalculator = createSaleCalculator();

saleCalculator.addSale(100);
console.log(saleCalculator.getTotalSales()); // output: 100

В этом примере мы создали замыкание с createSaleCalculator(), которое инкапсулирует состояние totalSales. Состояние теперь приватное и доступно только через методы addSale() и getTotalSales(). Это упрощает тестирование addSale() изолированно и предотвращает непреднамеренные побочные эффекты.

Разделение проблем

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

Вот пример кода, нарушающего принцип разделения ответственности:

function saveUser(name, email, password) {
  // validation logic
  if (!name || !email || !password) {
    throw new Error('Missing required fields');
  }

  // database logic
  const user = {
    name,
    email,
    password,
  };
  database.save(user);
}

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

function validateUser(name, email, password) {
  if (!name || !email || !password) {
    throw new Error('Missing required fields');
  }
}

function createUser(name, email, password) {
  const user = {
    name,
    email,
    password,
  };
  database.save(user);
}

function saveUser(name, email, password) {
  validateUser(name, email, password);
  createUser(name, email, password);
}

В этом примере мы разделили код на три функции: validateUser(), createUser() и saveUser(). Каждая функция отвечает за определенную задачу, а код легче понять и модифицировать. Кроме того, каждую функцию можно тестировать отдельно, что делает тестирование проще и эффективнее.

Написание тестируемого кода

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

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

function getWeatherForCity(city) {
  const apiKey = '12345';
  const url = `https://api.weather.com/${city}?apikey=${apiKey}`;
  return fetch(url)
    .then((response) => response.json())
    .then((data) => {
      return {
        temperature: data.temperature,
        description: data.description,
      };
    });
}

В этом коде мы делаем сетевой запрос, чтобы получить данные о погоде для определенного города. Этот код нелегко протестировать, поскольку он зависит от внешнего ресурса (API погоды).

Чтобы сделать этот код более тестируемым, мы можем разделить проблемы и сделать код более модульным:

function getWeatherUrl(city, apiKey) {
  return `https://api.weather.com/${city}?apikey=${apiKey}`;
}

function fetchWeatherData(url) {
  return fetch(url).then((response) => response.json());
}

function parseWeatherData(data) {
  return {
    temperature: data.temperature,
    description: data.description,
  };
}

function getWeatherForCity(city, apiKey) {
  const url = getWeatherUrl(city, apiKey);
  return fetchWeatherData(url).then(parseWeatherData);
}

В этом примере мы разделили код на четыре функции: getWeatherUrl(), fetchWeatherData(), parseWeatherData() и getWeatherForCity(). Каждая функция отвечает за определенную задачу, а код стал более модульным и его легче тестировать. Теперь мы можем тестировать parseWeatherData() и getWeatherForCity() изолированно, не завися от внешнего ресурса.

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

Понравилось ли вам то, что вы прочитали? Подпишитесь на мой БЕСПЛАТНЫЙ информационный бюллетень, где я делюсь со всеми своим 20-летним опытом работы в ИТ-индустрии. Присоединяйтесь к Бродяге старого разработчика!

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

Помните, что чистый код — это не просто набор правил, а образ мышления. Речь идет о стремлении к совершенству во всем, что вы делаете, включая написание кода. Итак, в следующий раз, когда вы сядете за программирование, спросите себя: «Это чисто? Могу ли я сделать это лучше?»

И всегда помните: написание чистого кода — это не только способ произвести впечатление на ваших коллег, но и облегчить вашу жизнь как разработчика.

Итак, удачного кодирования и да прибудет с вами чистый код!