Введение
В этой статье я хочу обратить внимание на такой аспект разработки как нейминг.
Мы рассмотрим, почему важно уделять внимание названиям переменных и функций, и как грамотный подход к неймингу может сократить время и усилия на поддержку и развитие проекта. Надеюсь, мои мысли окажутся полезными. Буду рада обратной связи в комментариях.
Описание проблемы
Чтобы писать хороший код, мы стремимся следовать различным принципам, таким как SOLID, DRY, KISS, осваиваем паттерны проектирования, анализируем различные архитектуры и подходы к организации кодовой базы. Но зачастую забываем о самой базовой, но критически важной вещи, без которой все эти усилия могут оказаться малоэффективными, – это хороший нейминг.
Код с плохим неймингом – это плохо читаемый код. А плохо читаемый код – это бомба замедленного действия. Вам рано или поздно к этому коду придется вернуться, чтобы исправить ошибки или добавить новый функционал, и тут возможны три сценария:
Вы решите всё отрефакторить. Это увеличит объем задачи и время ее выполнения, что потребует дополнительного тестирования и приведет к новым багам.
Вы решите ничего не рефакторить, а аккуратно и точечно пригородить костыль. Что сделает этот фрагмент кода еще более запутанным и менее привлекательным для дальнейшего вмешательства, а это, в свою очередь, рано или поздно превратит его в легаси.
Вам в целом будет лень глубоко копать, особенно если сроки поджимают, и вы добавите тот код, который посчитаете нужным, не учитывая какие-нибудь редко встречающиеся случаи, которые тестирование может и не покрывать.
А что насчет типизации? Возможно, подумали вы. Все верно, типизация это один из возможных способов улучшения читаемости кода. Однако даже типизация не гарантирует отсутствие ошибок, связанных с неправильным пониманием назначения функций и переменных. Например, когда две функции принимают и возвращают данные одинаковых типов, но одна из них создает новый массив, а другая – модифицирует существующий, это обязательно должно быть отражено в их названии.
Почему время, потраченное на нейминг, стоит того?
Потому что в долгосрочной перспективе время, потраченное на нейминг, сократит время, которое иначе придется потратить на:
Понимание и чтение кода.
Рефакторинг и исправление багов. Если код понятен и выполняет свою задачу, его не нужно рефакторить. Если код не рефакторить, то в нем не появляются новые баги.
Онбординг новых членов команды. Новым разработчикам и так довольно сложно, не стоит усложнять им жизнь еще и неочевидным неймингом.
Коммуникацию в команде. Хороший нейминг облегчает обсуждение кода, так как все разработчики понимают, о чем идет речь, без дополнительных пояснений.
Документацию кода. 🤡
Мотивация
Упрощение поиска
Например: Вы работаете с проектом, в котором множество функций и переменных имеют схожие или слишком общие названия, например, data, info, processRequest. Поиск нужной переменной превращается в настоящую проблему, поскольку такие названия встречаются в десятках мес��.
Хороший нейминг: Если бы разработчики использовали конкретные и уникальные названия, такие как userData, orderInfo, processPayment, то поиск был бы гораздо легче.
Улучшение читаемости
Например: Попробуйте понять, что делает этот код.
function countDays(d1, d2) {
let count = 0;
let current = new Date(d1);
while (current <= d2) {
const day = current.getDay();
if (day !== 0 && day !== 6) {
count++;
}
current.setDate(current.getDate() + 1);
}
return count;
}
const days = countDays(new Date('2024-08-01'), new Date('2024-08-15'));
console.log(days);Хороший нейминг: Если нейминг хороший, то становится понятно, что делает эта функция, просто прочитав код. Эта функция вычисляет, сколько рабочих дней прошло между двумя датами, исключая выходные.
const SUNDAY = 0;
const SATURDAY = 6;
function calculateBusinessDays(startDate, endDate) {
let businessDayCount = 0;
let currentDate = new Date(startDate);
while (currentDate <= endDate) {
const currentDayOfWeek = currentDate.getDay();
if (currentDayOfWeek !== SUNDAY && currentDayOfWeek !== SATURDAY) {
businessDayCount++;
}
currentDate.setDate(currentDate.getDate() + 1);
}
return businessDayCount;
}
const businessDays = calculateBusinessDays(new Date('2024-08-01'), new Date('2024-08-15'));
console.log(businessDays);Предотвращение ошибок
Например: Имеем такой код.
function fetchData(url) {
const [data, setData] = React.useState(null);
React.useEffect(() => {
fetch(url)
.then((response) => response.json())
.then((data) => setData(data))
.catch((error) => console.error(error));
}, [url]);
return data;
}
function MyComponent() {
function handleButtonClick() {
const data = fetchData("https://api.example.com/data");
// Ошибка: вызов хука внутри функции
console.log("Данные:", data);
}
return (
<div>
<button onClick={handleButtonClick}>Отобразить данные</button>
<p>{JSON.stringify(data)}</p>
</div>
);
}
В этом примере fetchData используется внутри функции handleButtonClick, которая вызывается при нажатии кнопки. Поскольку fetchData на самом деле является хуком, его можно вызывать только на верхнем уровне в теле функционального компонента или другого хука. Вызов хука внутри функции-обработчика события (handleButtonClick) нарушает правила использования хуков, и React не сможет правильно обработать состояние. Из-за того что исходя из названия fetchData непонятно, что эта функция является хуком, повышается риск ее неправильного использования.
Хороший нейминг: Переименуем fetchData в useFetchData, после чего этот хук перехочется вызывать внутри функций уже на подсознательном уровне.
function useFetchData(url) {
const [data, setData] = React.useState(null);
React.useEffect(() => {
fetch(url)
.then((response) => response.json())
.then((data) => setData(data))
.catch((error) => console.error(error));
}, [url]);
return data;
}
function MyComponent() {
const data = useFetchData("https://api.example.com/data");
// Правильное использование хука
function handleButtonClick() {
console.log("Данные:", data);
}
return (
<div>
<button onClick={handleButtonClick}>Отобразить данные</button>
<p>{JSON.stringify(data)}</p>
</div>
);
}Облегчение командной работы
Например: Имеем такой код.
// Компонент для отображения списка задач в дашборде
function TaskList({ tasks }) {
return (
<ul>
{tasks.map((task) => (
<li key={task.id}>{task.title}</li>
))}
</ul>
);
}
// Компонент для отображения списка задач в модальном окне
function TaskList({ tasks, onTaskClick }) {
return (
<div className="modal">
<ul>
{tasks.map((task) => (
<li key={task.id} onClick={() => onTaskClick(task)}>
{task.title}
</li>
))}
</ul>
</div>
);
}
На Катю назначили баг с высоким приоритетом. Зная, что недавно в этом месте работал Валера, она сразу идет к нему за советом.
Катя: Слушай, у нас проблема с TaskList. Ты ведь недавно заливал обновление на тестовый стенд, правильно? Может, подскажешь, в чем дело? Мне пришел баг-репорт, что задачи не кликабельны, хотя должны быть.
Валера 🤡: Нет, не должны. Олег зря создал тикет. Напиши ему, чтобы он обсудил это с нашим продактом Артуром и он убрал этот баг из спринта.
Катя связывается с Олегом, Олег – с Артуром, Артур отвечает Олегу, Олег – Кате. После целого дня переписки, Катя, вспоминая Валеру не самыми добрыми словами, все-таки идет исправлять баг, но не в дашборде, как предположил Валера, а в модальном окне, где задачи действительно должны быть кликабельными. Если бы название компонента отражало его назначение, изначальная беседа с Валерой была бы намного продуктивнее и сэкономила бы кучу времени..
Хороший нейминг: Переименуем TaskList в одном месте в TaskListDashboard, и в другом в TaskListModal.
// Компонент для отображения списка задач в дашборде
function TaskListDashboard({ tasks }) {
return (
<ul>
{tasks.map((task) => (
<li key={task.id}>{task.title}</li>
))}
</ul>
);
}
// Компонент для отображения списка задач в модальном окне
function TaskListModal({ tasks, onTaskClick }) {
return (
<div className="modal">
<ul>
{tasks.map((task) => (
<li key={task.id} onClick={() => onTaskClick(task)}>
{task.title}
</li>
))}
</ul>
</div>
);
}Удобное автозаполнение
Например: Вы работаете над крупным проектом и часто используете функции для форматирования дат. В проекте есть несколько утилит, которые помогают с этим, но названы они недостаточно конкретно: formatDate, formatDates, dateFormatter, dateForm. Каждый раз, когда вы пытаетесь вызвать одну из этих функций, автозаполнение в редакторе выдает список из всех похожих названий, что сбивает вас с толку и замедляет работу. Вы постоянно путаетесь, какая именно функция нужна в конкретном случае.
Хороший нейминг: Переименуем эти функции более осмысленно: formatDateForReport, formatDateForDisplay, formatDateForExport. Теперь, когда вы начинаете вводить название функции, автозаполнение сразу подсказывает нужный вариант.
Заключение
Качество нейминга напрямую влияет на качество кода. Чтобы писать качественны�� код, важно придерживаться принципов хорошего нейминга. Но для этого эти принципы должны быть четко и однозначно определены. В своей следующей статье я подробно рассмотрю эти принципы.