15 принципов качественного кода: как писать понятно, чисто и эффективно

15 принципов качественного кода: как писать понятно, чисто и эффективно

  • 17 января
  • читать 20 мин
Владимир Шайтан
Владимир Шайтан Senior Full Stack Developer в UKEESS Software House, Преподаватель Компьютерной школы Hillel.

Писать плохой код — как клеить скотчем дыры на корабле. Вроде бы работает, но не надолго. Качественный код — это не магия, а соблюдение чётких принципов. Разберёмся, как использовать их на практике. Добавим более сложные примеры, чтобы всё было максимально понятно.

ПРИНЦИП 1: СОБЛЮДАЙ СТИЛЕВОЕ ПОСОБИЯ

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

✅ Добре:
interface User {
   id: number;
   name: string;
   email: string;
}

function fetchUserData(userId: number): Promise<User> {
   return api.get(`/users/${userId}`);
}

❌ Погано:
interface user{
   Id:number,Name:string,email:string
}
function Fetch_user_data(id:Number){
   return Api.GET("/users/"+id)
}

Практический совет: используй ESLint и Prettier для автоматического форматирования. Не полагайся только на зрение — инструменты сохранят твои нервы.

ПРИНЦИП 2: ИСПОЛЬЗУЙ ПОНЯТИЕ НАЗВАНИЯ

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

✅ Добре:
function generateInvoice(items, taxRate) {
   return items.reduce((total, item) => total + item.price, 0) * (1 + taxRate);
}
❌ Погано:
function inv(items, t) {
   let s = 0;
   items.forEach(i => s += i.p);
   return s * (1 + t);
}

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

✅ Добре:
function createReport({ userId, startDate, endDate, includeDetails }) {
   // Логіка для створення звіту
}

❌ Погано:
function createReport(userId, startDate, endDate, includeDetails) {
   // Логіка для створення звіту
}

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

ПРИНЦИП 3: ПИШИ КОММЕНТАРИИ И ДОКУМЕНТАЦИЮ

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

✅ Добре:
/**
* Fetches user orders and aggregates them by status.
* @param userId - ID of the user
* @returns Promise resolving to an aggregated order summary
*/
async function getUserOrderSummary(userId: number): Promise<OrderSummary> {
   const orders = await api.get(`/users/${userId}/orders`);
   return orders.reduce((summary, order) => {
       summary[order.status] = (summary[order.status] || 0) + 1;
       return summary;
   }, {});
}

❌ Погано:
async function fetchOrders(id) {
   let o = await api.get(`/users/${id}/orders`);
   return o.reduce((s, i) => {
       s[i.status] = (s[i.status] || 0) + 1;
       return s;
   }, {});
}

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

ПРИНЦИП 4: ИЗБЕГАЙ ПОВТОРЕНИЙ

Дублирование кода — это головная боль при поддержке.

✅ Добре:
function validateInput(input, rules) {
   return rules.every(rule => rule(input));
}

const isNotEmpty = (str) => str.trim() !== '';
const isEmail = (str) => /\S+@\S+\.\S+/.test(str);

validateInput('test@example.com', [isNotEmpty, isEmail]);

❌ Погано:
function validateEmail(email) {
   if (email.trim() === '') return false;
   if (!/\S+@\S+\.\S+/.test(email)) return false;
   return true;
}

function validateUsername(username) {
   if (username.trim() === '') return false;
   return true;
}

ПРИНЦИП 5: ОБРАБАТЫВАЙ ОШИБКИ И ИСКЛЮЧЕНИЯ

Обработка ошибок — это не просто try-catch, это план действий на случай проблем.

✅ Добре:
async function fetchData(url) {
   try {
       const response = await fetch(url);
       if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
       return await response.json();
   } catch (error) {
       console.error('Fetch error:', error);
       throw new Error('Unable to fetch data');
   }
}
❌ Погано:
async function fetchData(url) {
   const response = await fetch(url);
   return await response.json(); // Без обробки помилок
}

ПРИНЦИП 6: РАЗБИВАЙ КОД НА ПОНЯТЫЕ БЛОКИ

Функции должны быть атомарными.

✅ Добре:
function processUserData(user) {
   const isValid = validateUser(user);
   if (!isValid) throw new Error('Invalid user data');

   saveUserToDatabase(user);
   sendWelcomeEmail(user.email);
}

function validateUser(user) {
   return user.name && user.email;
}

❌ Погано:
function processUserData(user) {
   if (!user.name || !user.email) {
       throw new Error('Invalid user data');
   }
   saveUserToDatabase(user);
   sendWelcomeEmail(user.email);
}

ПРИНЦИП 7: ИСПОЛЬЗУЙ ГОТОВЫЕ API И БИБЛИОТЕКИ

✅ Добре:
import _ from 'lodash';

const numbers = [1, 2, 3, 4, 5];
const doubled = _.map(numbers, (num) => num * 2);
❌ Погано:
const numbers = [1, 2, 3, 4, 5];
const doubled = [];
for (let i = 0; i < numbers.length; i++) {
   doubled.push(numbers[i] * 2);
}

ПРИНЦИП 8: ИЗБЕГАЙ ЧРЕЗМЕРНОГО ПРОЕКТИРОВАНИЯ

✅ Добре:
class User {
   constructor(name) {
       this.name = name;
   }
}

❌ Погано:
class UserFactory {
   static createUser(name) {
       return new User(name);
   }
}

ПРИНЦИП 9: БУДЬ ПОСЛЕДОВАТЕЛЬНЫМ

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

✅ Добре:
// Обидві функції використовують async/await
async function fetchData(url) {
   const response = await fetch(url);
   return await response.json();
}

async function getUser(id) {
   const response = await fetch(`/user/${id}`);
   return await response.json();
}
❌ Погано:
// Функція з колбеком
function fetchData(url, callback) {
   fetch(url).then((response) => callback(response));
}

// Функція з async/await
async function getUser(id) {
   const response = await fetch(`/user/${id}`);
   return await response.json();
}

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

ПРИНЦИП 10: ДУМАЙ О БЕЗОПАСНОСТИ

Безопасность кода — это не только сложные пароли. Это о том, как ты работаешь с входными данными, API, базами данных и внешними запросами.

✅ Добре:
const userId = req.query.userId;
db.query('SELECT * FROM users WHERE id = ?', [userId]);
❌ Погано:
const userId = req.query.userId;
db.query(`SELECT * FROM users WHERE id = ${userId}`);

Что произошло? В первом примере SQL-запрос уязвим к SQL-инъекциям. Во втором примере используется параметризированный запрос, защищающий базу данных от подобных атак.

Другие практики:

  • всегда валидируй входные данные;
  • используй линтеры для поиска уязвимостей (например, eslint-plugin-security);
  • храни секреты (API-ключи, пароли) в переменной среде.

ПРИНЦИП 11: ИСПОЛЬЗУЙ ЭФФЕКТИВНЫЕ СТРУКТУРЫ ДАННЫХ

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

✅ Добре:
const users = new Map([
   [1, { id: 1, name: 'Alice' }],
   [2, { id: 2, name: 'Bob' }]
]);

function getUserById(id) {
   return users.get(id);
}
❌ Погано:
const users = [
   { id: 1, name: 'Alice' },
   { id: 2, name: 'Bob' }
];


function getUserById(id) {
   return users.find(user => user.id === id);
}

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

ПРИНЦИП 12: ПИШИ ТЕСТЫ

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

Более сложный пример:

✅ Добре:
// Функція для обчислення знижки
function calculateDiscount(price, discount) {
   if (discount > price) throw new Error('Discount cannot exceed price');
   return price - discount;
}

// Тест
test('calculateDiscount correctly calculates discount', () => {
   expect(calculateDiscount(100, 20)).toBe(80);
   expect(() => calculateDiscount(100, 200)).toThrow('Discount cannot exceed price');
});

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

Инструменты:

  • Jest
  • Mocha
  • Vitest

ПРИНЦИП 13: ОБЕСПЕЧИВАЙ ПОРТАТИВНОСТЬ КОДА

Твой код должен работать одинаково хорошо на разных средах и платформах.

✅ Добре:
const path = require('path');
const filePath = path.join(__dirname, 'file.txt');

❌ Погано:
const filePath = 'C:\\Users\\user\\file.txt'; // Windows-подібний шлях

Почему это важно? Функция path.join гарантирует правильный формат пути независимо от операционной системы.

ПРИНЦИП 14: АВТОМАТИЗИРУЙ СБОРКУ

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

Запуск тестів
✅ Добре:
{
   "scripts": {
   "build": "webpack --mode production",
       "start": "node dist/server.js",
       "test": "jest"
}
}

Почему это важно? Один обычный скрипт помогает команде разработчиков запускать все нужные процессы.

ПРИНЦИП 15: ИСПОЛЬЗУЙ КОНТРОЛЬ ВЕРСИЙ

Каждое изменение в коде должно быть зафиксировано и задокументировано.

✅ Добре:
git commit -m "fix: corrected discount calculation logic"

❌ Погано:
git commit -m "update"

Совет: используй GitFlow для организации ветвей и пиши осмысленные комиты.

ВЫВОД

Вот и всё — 15 принципов, которые делают код более чистым, поддерживаемым и понятным. Это не список жёстких правил, а набор советов, которые можно вводить постепенно.

Рекомендуем публикации по теме