Научете как да почиствате кода си като истински воин

Здравейте, бойци на кода!

Уморихте ли се да газите през море от спагети код?

Копнеете ли за сладката, сладка прегръдка на чистия код (каквото и да означава това)?

Е, попаднахте на правилното място. Защото днес ще ви покажа единственото нещо, което отличава професионалистите от аматьорите: чист код.

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

Какво е Clean Code и защо има значение?

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

Защото нека си признаем: разликата между добрия разработчик и страхотния разработчик е цялата в кода (е, най-вече във всеки случай). И тук идва чистият код.

И така, какво е чист код? Чистият код е код, който е лесен за четене, лесен за разбиране и лесен за поддръжка. С други думи, това е идея, а не действително „нещо“. В математиката бихме го нарекли тенденция, подобно на безкрайността, числата могат да „клонят“ към безкрайността, но никога не могат да я достигнат. И вашият код може да се доближи много до „чист“, но никога няма да го достигне. Защо? Ще видим след минута.

Шегата настрана, „чистият код“ е код, който е добре организиран, добре документиран и без ненужни бъркотии. Чистият код е код, който дори баба ви може да разбере (ако приемем, че знае как да кодира, разбира се).

Но защо чистият код има значение? Е, като за начало, чистият код е по-лесен за поддържане. Това означава по-малко време, прекарано в отстраняване на грешки, по-малко време, прекарано в коригиране на грешки, и по-малко време, прекарано в псувни под носа.

Но чистият код също е по-лесен за разбиране. Това означава, че когато се върнете към своя код след шест месеца (когато сте забравили какво по дяволите прави), ще можете да разберете нещата много по-бързо. И да не забравяме вашите колеги. Те ще ви благодарят и за писането на чист код, тъй като няма да им се налага да прекарват часове в дешифриране на вашите спагети.

И така, ето ви го, чистият код е ключът към това да станете рокзвезда в кодирането.

Да поговорим за „променливи“

Трябва да говорим за променливи. Защото нека бъдем реални: наименуването на променливи е трудно. Някои разработчици твърдят, че наименуването на нещата е най-трудната част от нашата област. Ти ми кажи.

Но това не означава, че получавате безплатен пропуск да наименувате променливите си на вашата котка или любимия си аниме герой – въпреки че колко готино би било да ви бъде възложена задача за наследен код, който използва имена като 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 (това също прави кода още по-тестваем).

Харесахте ли прочетеното? Обмислете да се абонирате за моя БЕЗПЛАТЕН бюлетин, където споделям моята мъдрост от 2 десетилетия в ИТ индустрията с всички. Присъединете се към „„Бродът на един стар разработчик““!

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

Не забравяйте, че чистият код не е просто набор от правила, а начин на мислене. Става дума за стремеж към съвършенство във всичко, което правите, включително писане на код. Така че следващия път, когато седнете да кодирате, запитайте се: „Това чисто ли е? Мога ли да го направя по-добър?“

И винаги имайте предвид, че писането на чист код не означава само да впечатлите колегите си, а да улесните живота си като разработчик.

Така че, приятно кодиране и нека чистият код бъде с вас!