Legacy-код і його покращення за допомогою статичних перевірок
Привіт! Мене звати Олександр, я фронтенд-розробник в Uptech з трирічним досвідом роботи в продуктових та аутсорсингових компаніях. Так склалось, що всі проєкти, у яких я працював, були написані до мене. Відповідно мені доводилось працювати з уже наявною кодовою базою. Тому хочу поділитися думками про те, як спростити собі та іншим роботу з legacy-кодом.
Що таке legacy-код та як з ним працювати
Це код, отриманий від інших розробників або проєктної команди. Він може мати недостатньо документації і тестів (їх узагалі може не існувати на проєкті), проблеми з архітектурою, у ньому можуть бути застарілі версії бібліотек, фреймворків, підходи до розробки.
У більшості випадків legacy-код функціонально працездатний. Але через те, що цей код застарілий, його важко підтримувати, змінювати, вдосконалювати, інтегрувати з іншими системами або масштабувати.
Усе описане може спричинити проблеми з безпекою, продуктивністю, надійністю, доступністю тощо. Навіть маленькі зміни в такому коді тягнуть за собою купу непередбачуваних наслідків і помилок. Запобігти цим проблемам допоможе використання статичних перевірок у коді та блокування доставки певних змін у репозиторій, які розглянемо далі.
Ідея в тому, аби ускладнити процес доставки коду (code delivery) та забороняти пушити рішення з певними недоліками. Це можна зробити через запровадження максимальної кількості автоматизованих систем перевірок, які ускладнюють появу неочевидних помилок.
Варіанти вирішення проблеми
У цьому розділі я пропоную звернути увагу на важливі аспекти, притаманні саме фронтенд-розробці. Деякі підходи є типовими для розробки в цілому.
Dependency update
У більшості пакетів короткий термін життя і постійні зміни, за якими не всі встигають стежити: іноді покращення, а іноді усунення вразливостей та багато іншого.
Рано чи пізно вам доведеться оновлювати пакети. Але процес може бути хаотичним: щось оновили, щось пропустили. І в певний момент ви отримаєте помилку на кшталт «Колесо v2.0 не підохить до рами v.1.0». Ви чухаєте потилицю, вдивляючись у вікно терміналу, паралельно шукаєте пакет на npm, видаляєте, завантажуєте node_modules
. Добре, якщо у вас маленький проєкт. А якщо у вас робочий монорепозиторій? Тільки уявіть, скільки часу ви можете витратити на це.
На допомогу прийде утиліта dependency automatic update
. Наприклад, dependabot або renovate. З ними вам не потрібно більше моніторити нові версії залежностей. Вони зроблять усю роботу за вас і відкриють новий PR, який буде вимагати лише підтвердження та мерджу відповідних змін. Скриншот із прикладом такого PR наведений нижче.
Commit lint
Фокусуючись на написанні коду, інженери ігнорують правила опису комітів. Можливо, хтось подумає, що це ніяк не вплине на кінцевий продукт, але варто пам’ятати, що код ми пишемо не для машин, а для інших людей, яким колись доведеться з ним працювати. Погодьтесь, із таких коміт-меседжів важко зрозуміти, що саме було зроблено в коді:
Для таких випадків існують правила комітів. Деякі команди пишуть їх власноруч, але існують рішення ком’юніті. Ось приклад, як може виглядати історія комітів, якщо дотримуватися цих правил:
Очевидно, що читати такі коміт-меседжі набагато зручніше. Слід зауважити, що для автоматичного контролю GitHub надає інструмент, який дозволяє блокувати або просто повідомляє розробнику про відхилення від правил.
Gitflow
Gitflow — структурований підхід до організації процесу розробки, що допомагає поліпшити роботу з legacy-кодом.
Завдяки йому зменшуються помилки під час злиття гілок, зберігається стабільна версія, а функціональності легко розділяються. Цей підхід спрощує тестування нових функцій та полегшує роботу з різними версіями коду.
Застосування Gitflow особливо корисне для великих команд і для тих, хто постійно залучає нових інженерів, оскільки він допомагає уникнути проблем, описаних тут.
Deploy preview
Кожен деплой нового білду потребує тестування, а отримання змін гілки та запуск проєкту займає доволі багато часу. Крім того, рев’юери коду швидко хочуть переглянути демо з вашими змінами. Рішення цієї проблеми — деплой кожної гілки на окремому середовищі.
Запускати проєкт кожного разу, коли відкриваєш чужий PR, важко. Набагато простіше перейти за посиланням і перевірити зміни в реальному застосунку.
Існують складні рішення, наприклад, Terraform, проте є й набагато простіші в налаштуванні, як-от Netlify, GitHub deploy Preview, Vercel.
Тестування
Тестування фронтенд-застосунків є важливою частиною процесу розробки, оскільки воно допомагає переконатися, що ваш інтерфейс працює правильно, відповідає очікуванням користувачів і має надійну функціональність.
Говорячи про тестування фронтенд-застосунків, неможливо оминути такий термін, як піраміда тестування. В основі такої піраміди лежать швидкі та прості в написанні тести (unit-тести), посередині — інтеграційні тести, а на її вершині містяться складні в налаштуванні та повільні end-to-end тести. Крім цих основних типів тестування, також можуть використовуватися інші підходи: тестування на реальних пристроях (Real Device Testing), тестування на різних браузерах (Cross-Browser Testing) і тестування на різних платформах (Cross-Platform Testing).
Завдяки тестам можна зрозуміти, чого очікувати від конкретного функціоналу.
Дуже рекомендую звернути увагу на курс одного з найвідоміших учасників світового фронтенд-ком’юніті Кента Сі Доддса.
Лінтер і Форматування
Той самий Кент Сі Доддс ввів навіть поняття «трофей тестування», фундаментом якого є статичні перевірки: стиль коду, типізація і відповідність коду певним правилам. Усе це дозволяє отримати чистий читабельний код, зменшити помилки й простіше виявляти їх на етапі розробки або код-рев’ю.
Зазвичай у якості лінтера використовують eslint. У кожному проєкті різні вимоги до стилю і форматування коду. Головне, аби після вибору правил зберігались зрозумілість і читабельність коду для всіх учасників проєкту. Найпопулярнішими та найпростішими в налаштуванні є інструменти Prettier та Stylelint.
Типізація
Типізація у фронтенді використовується для статичної перевірки типів даних під час розробки. Це допомагає виявляти помилки на етапі компіляції та полегшити роботу з кодом у команді, забезпечуючи кращу стабільність і зрозумілість програми.
Найпоширенішими інструментами я б назвав такі:
- TypeScript. Дозволяє оголошувати типи змінних, параметрів функцій, об’єктів, інтерфейсів та інших конструкцій коду. TypeScript перевіряє типи даних під час компіляції, допомагає знайти помилки та надає автодоповнення і контекстну довідку в різних IDE.
- Flow. Інструмент типізації JavaScript, розроблений Facebook. Він дозволяє оголошувати типи змінних, функцій, об’єктів та інших елементів коду за допомогою спеціальних анотацій. Flow використовується для статичної перевірки типів, але це не окрема мова програмування, як, наприклад, TypeScript.
- Бібліотека PropTypes для проєктів, написаних на React. Дозволяє оголошувати типи для props, переданих компонентам у React-застосунках, і документації компонентів. PropTypes забезпечує перевірку типів під час виконання програми.
GitHub Actions
Як же тоді впевнитись, що перевірки виконані? Нам у цьому допоможе GitHub Actions (як найпопулярніше рішення) або інші CI. Їх можна налаштувати так, щоб всі перевірки проходили та блокували кнопку «merge».
Нижче є приклад роботи в репозиторії, де існують усі ці перевірки.
Кожна доставка коду до репозиторію супроводжується перевірками eslint, тестами, commit lint. Це позбавляє розробників від типових помилок і неуважності. Приклад: PR.
На етапі код-рев’ю можна побачити пройдені або не пройдені перевірки. Також рев’юер може погоджувати не тільки код, а й зовнішній вигляд компонентів.
Безперечно, підтримання такої інфраструктури потребує певних ресурсів, але, на мою думку, дотримання чітких правил відповідно до чеклиста перед кнопкою merge значно покращить якість кодової бази проєкту.
Код-рев’ю
Дуже важливу роль у процесі розробки виконує код-рев’ю, що сприяє покращенню загальної якості коду проєкту. Регулярне код-рев’ю допомагає зменшити кількість помилок, робить код зрозумілішим для інших розробників, а також сприяє зберженню консистентності кодової бази. Це гарантує, що всі члени команди будуть дотримуватись єдиних стандартів і найкращих практик написання коду. Відповідно такий код буде легше читати іншим розробникам, розуміти його та підтримувати, а це підвищить ефективність і зменшить шанси появи нових помилок.
Рекомендую прочитати такі статті на тему код-рев’ю:
- Why I (Don’t) Love Doing Code Reviews — автор ділиться своїм досвідом проведення рецензії коду та причинами, чому він не любить робити рецензію.
- Code review: why bother — автор розповідає про проблеми в процесі код-рев’ю.
- Unlearning Toxic Behaviors in a Code Review Culture — авторка розглядає питання токсичної культури, яка може розвиватися у процесі перевірки коду, та пропонує способи, як забезпечити конструктивну та сприятливу для всіх культуру код-рев’ю.
Документація
Документація може бути дуже корисною для проєктів будь-якої складності. Думаю, що всім було би комфортно працювати на проєктах, де існує багато якісної документації.
Щодо формату написання, то це можуть бути як коментарі в коді, так і повноцінні документи в Confluence/Notion/Google Docs/ReadMe та інші.
У контексті роботи з документацією варто також згадати про такий потужний інструмент в арсеналі фронтенд-розробників, як storybook. Він дозволяє створювати, відлагоджувати і документувати окремі компоненти інтерфейсу, незалежно від основної програми або застосунку, та є такою собі «живою» документацією компонентів застосунку. UI-компоненти у вигляді різних сторі можуть розгортатися з різними комбінаціями даних, щоб продемонструвати багато станів або варіацій. У такий спосіб ми можемо покращити співпрацю між розробниками й дизайнерами, спростити тестування і налагодження компонентів перед їх використанням у реальних проєктах.
Ділюсь також прикладом корисної документації саме для JS-коду.
Пропоную звернути увагу на функцію hexToRgbA
у якості прикладу. На перший погляд вона виглядає складною, проте інструмент JSDoc робить її простішою для читання та розуміння.
До слова, в компанії Uptech, де я зараз працюю, переважна більшість проєктів написана на TypeScript, але є написані на JavaScript. Там ми також використовуємо коментарі JSDoc для документування коду. Більше інформації про JSDoc можна знайти тут.
Широкої популярності зараз набирають AI-інструменти, зокрема ChatGPT або GitHub Copilot, що можуть допомогти в написанні документації багатьом розробникам.
За допомогою ChatGPT ви можете вставляти код (приклад такого запиту) та просити доповнити його коментарями або згенерувати коротке резюме про те, що саме робить ваш код. Важливо зауважити, що з міркувань безпеки не варто вставляти код із конфіденційними даними (наприклад, API або AWS keys).
Висновки
Отже, використання описаних методів і практик статичного аналізу — це корисно і допомагає розробникам виявляти й вирішувати проблеми на ранніх етапах розробки. Це сприяє покращенню якості коду, безпеки, продуктивності та зручності підтримки legacy-коду.
Використовуючи ці рекомендації, ви можете зменшити кількість помилок і підвищити загальну надійність застосунків. Це також полегшить онбординг нових інженерів на проєкти, що містять legacy-код.
5 коментарів
Додати коментар Підписатись на коментаріВідписатись від коментарів