Почему важно контролировать качество кода
13 декабря 2022 г.Обычно, когда мы работаем над конкретным программным продуктом, качество кода не является нашей главной заботой. Нам гораздо важнее производительность, функциональность, стабильность его работы и т.д.
Но является ли качество кода фактором, положительно влияющим на вышеуказанные показатели? Мой ответ — да, потому что такой код напрямую связан со следующими качествами:
* читабельность - возможность посмотреть код и быстро понять реализованный алгоритм, и оценить, как программа поведет себя в том или ином случае.
* управляемость - возможность в кратчайшие сроки внести в код требуемые поправки, избегая при этом различных неприятных предсказуемых и непредсказуемых последствий.
Код — это книга, написанная одним автором и дополненная другими авторами. Его будут передавать через разных людей, и то, что читатель вынесет из этой книги, будет зависеть от того, как написан код. Значит, это очень важно, не так ли?
Вещи, определяющие качество кода
Последовательность записи
Как мы показали выше, код — это книга, поэтому он должен быть написан линейным стилем.
<цитата>Линейный код — код, который можно читать сверху вниз, не возвращаясь к ранее прочитанному коду.
Например, идеально линейный фрагмент:
{
doFirst();
doSecond();
doThird();
}
И совсем нелинейно:
{
if (something) {
doFirst();
} else {
doSecond();
if (whatever) {
if (a) {
if (b) {
doThird();
}
}
}
}
}
Попробуем исправить. Здесь мы можем переместить сложные индексы в отдельные функции:
{
const doOnWhatever = () => {
if (a) {
if (b) {
doThird();
}
}
}
if (something) {
doFirst();
} else {
doSecond();
if (whatever) {
doOnWhatever();
}
}
}
Прежде всего, вам нужно как можно проще представить свою логику в виде блок-схемы:
Внимательно пройдитесь по этой схеме и постарайтесь перенести ее в код, избегая очень большой вложенности.
Есть несколько советов о том, как обрабатывать большое вложение:
- Используйте break, continue, return или throw, чтобы избавиться от блока else:
```javascript { делатьВ зависимости() { если) { если (б) { сделатьТретий(); } }
if (something) {
doFirst();
return;
}
doSecond();
if (whatever) {
doOnWhatever();
}
} ```
Было бы неправильно делать вывод, что вы вообще никогда не должны использовать оператор else. Во-первых, контекст не всегда позволяет поставить «break, continue, return или throw». Во-вторых, значение this может быть не столь очевидным, как в примере выше, и простое else будет выглядеть намного проще и понятнее, чем что-либо еще. И, наконец, существуют определенные издержки при использовании множественных возвратов в функциях, из-за которых многие обычно считают такой подход антишаблоном.
2. Объединение вложенных if-s:
```javascript { делатьВ зависимости() { if (a && b) { // здесь мы объединили условия "a" и "b" сделатьТретий(); }
if (something) {
doFirst();
return;
}
doSecond();
if (whatever) {
doOnWhatever();
}
} ```
3. Использование тернарного оператора (a? b: c) вместо if:
```javascript пусть что-то; если) { что-то = б; } еще { что-то = с;
const что-то = a ? б : в;
const что-то = a ? б: аа? CD; ```
4. Устранение дублирования кода:
```javascript const a = новый объект(); сделатьПервый(а); сделатьСекунда(а);
const b = новый объект(); сделать первый (б); сделатьСекунда(б);
const workWithObject(x) { сделатьПервый(х); сделатьвторой(х);
работа с объектом(новый объект()); работать с объектом (новый объект ()); ```
5. Упрощение кода:
```javascript if (obj != null && obj != undefined && obj.something != null && obj.something != undefined && obj.something!= '') { // сделай что-нибудь
if (obj && obj.something) { // сделай что-нибудь } ```
Дело в том, что благодаря неявному приведению к логическому значению проверка if(obj){} будет отфильтровывать: false, null, undefined, 0, ‘‘.
6. Не создавать переменные, без которых можно работать:
```javascript ... постоянная сумма = получитьсумму(); const увеличенная сумма = сумма + 1; сделать что-то (увеличение суммы); ...
сделать что-то (получить сумму() + 1);
```
Эта ситуация также называется «создание переменной для создания переменной». Переменные должны помогать читателям быстро понимать код, а не замедлять их, вынуждая читать ненужный текст.
Вот примеры необходимого именования:
```javascript const PHONE_NUMBER_REGEX = /^[+]?[(]?[0-9]{3}[)]?[-s.]?[0-9]{3}[-s.]?[0-9] {4,6}$/ const str.match(phoneNumberRegex) // это помогает нам понять, что мы ищем номер телефона
const LUCK_ERROR = 10%; const result = luckyPercent - LUCK_ERROR // это помогает нам не работать с магическими числами ```
7. Использование инкапсуляции
Создание личных данных посредством закрытия:
javascript
константа createCounter = () => {
пусть счет = 0;
возвращаться ({
нажмите: () => количество += 1,
getCount: () => считать
});
};
постоянный счетчик = createCounter();
счетчик.щелчок();
счетчик.щелчок();
счетчик.щелчок();
console.log(
счетчик.getCount()
);
Здесь мы использовали метод, который имеет доступ к закрытым данным внутри области видимости (лексического окружения) функции. Эти функции и методы имеют доступ к переменным внутри функции на основе ссылок даже после завершения функции. Эти ссылки активны, поэтому, если состояние изменяется во внутренней функции, изменения распространяются на каждую привилегированную функцию. Другими словами, когда мы вызываем counter.click(), он изменяет значение, которое видит counter.getCount().
Создание личных данных через закрытые поля:
```javascript счетчик класса { # количество = 0
click () {
this.#count += 1;
}
getCount () {
return this.#count.toLocaleString()
}
const myCounter = новый счетчик(); мойСчетчик.щелчок(); мойСчетчик.щелчок(); мойСчетчик.щелчок(); console.log( мойСчетчик.getCount() ); ```
Поля нового класса намного лучше символов подчеркивания, потому что они не зависят от соглашения, а обеспечивают настоящую инкапсуляцию.
Именование
- Использование записи в верблюжьем регистре:
javascript
const getSomeValue = () => {};код>
2. Не используйте транслитерацию, если язык вашей компании/проекта не английский:
javascript
const tovar = {} // пример с русского: tovar = product
3. Избегайте использования абстрактного именования:
```javascript const getProductNames = products.map(item => item.name) // менее читаемый код
const getProductNames = products.map(product => product.name) // более читаемый код ``` 4. Именование констант заглавными буквами:
javascript
константа BANNER_WIDTH = 300
Обычно заглавные буквы используются для именования констант или переменных, когда значение известно до выполнения скрипта и записывается непосредственно в код, например:
javascript
const ДЕНЬ РОЖДЕНИЯ = '18.04.1982';
Используйте заглавные буквы. Если переменная оценивается во время выполнения скрипта, используются строчные буквы:
javascript
const age = someCode(ДЕНЬ РОЖДЕНИЯ);
5. Вызов переменных, говорящих именами:
javascript
const getProducts = () => {};
const addProductToCart = () => {};
Декларативно
Декларативный стиль кодирования имеет ряд преимуществ по сравнению с императивным стилем:
- код легче читать
- код легче поддерживать
- сложные конструкции скрыты за методами и абстракциями
Пример сравнения императивного и декларативного кода:
for(let i = 0; i < textArr.length; i++) {
if(arr[i] === 'Text to console log') {
console.log(arr[i])
}
} // imperative code
textArr.filter(text => text === 'Text to console log').map(text => console.log(text)); // declarative code
Модульность
Хорошей практикой является разделение кода на модули. Такой код повышает удобочитаемость за счет разделения кода на абстракции, что помогает скрыть трудночитаемый код. Кроме того, код легче тестировать и, соответственно, легче находить ошибки.
Итак, давайте посмотрим, как это реализовать, используя предыдущий код:
const createCounter = () => {
let count = 0;
return ({
click: () => count += 1,
getCount: () => count
});
}; // this part of code can be moved into separated file such as helper.ts
const counter = createCounter(); // this can be used inplace where it's needed because it's not nesessary to see how counter was actually created
Людям на самом деле не нужно видеть весь код, управляющий процессом, и вполне нормально скрыть его за говорящим именем. Конечно, иногда может потребоваться дополнить какой-либо функционал или посмотреть, как он работает. Для этого вы можете свободно перейти к конкретному файлу, чтобы изучить его, но это случается не так часто, поэтому нет необходимости хранить его прямо на месте использования.
Стиль кода
Это набор правил/проектов/соглашений, которым должны следовать разработчики. Это можно где-то описать, например, в вики-разделе в Gitlab с примерами, что делать и чего не делать.
Вот пример того, как я реализую это в проекте:
Более того, вы можете взять готовый код стиля и внедрить его в свой проект, например, стиль кода Airbnb.
Репозиторий
Это успешная модель управления репозиторием, когда есть две основные ветки: develop, master и остальные временные ветки.
Временные ветки должны содержать тип изменения, такой как выпуск, функция, исправление или исправление, а также номер задачи задачи:
fetaure/123
bugfix/321
Когда мы начинаем новую задачу, мы выходим из ветки разработки. После прохождения код-ревью мы сливаем его обратно в разработку. Затем мы собираем релизы из ветки разработки, связываем их с веткой релизов и выпускаем все это на master.
Какое сообщение должно быть отправлено, когда мы фиксируем некоторые изменения? «исправлена ошибка», «добавлена функция» — не являются хорошими примерами сообщений.
Качественное сообщение — это когда оно содержит емкое изложение сути изменений.
"added a banner component" // - commit message example
Заключение
Подводя итоги статьи, приведем следующие советы, которым следует следовать для написания качественного кода:
- сохраняйте линейность кода
- уменьшить вложенность
- используйте говорящие имена
- стремиться к декларативности и модульности
- получить стиль кода
- обратите внимание на поток git и сообщения фиксации
- используйте линтеры и средства форматирования в качестве помощников в вашем проекте
Оригинал