Чистий код: причини та наслідки

8 січня
Віктор Свірський, Senior Python Developer / Team Lead, DataArt
Чистий код: причини та наслідки
Скільки програмістів, стільки й визначень, що таке чистий код. Часто, проводячи співбесіду, я чую, що хороший код — це такий, який легко читається. Згоден, але як підказує мій особистий досвід, це лише вершина айсберга.

Перший дзвіночок, який нам повідомляє, що код припиняє бути чистим, — це зростання часу розробки нової функціональності та збільшення регресійного скоупу при щонайменшій зміні у системі. Це наслідок того, що технічний борг накопичується, компоненти в системі дуже тісно пов'язані, автотести відсутні. Причини цього можуть бути:

  • зовнішніми — наприклад, тиск із боку бізнесу та його прагнення отримати новий функціонал якомога швидше;
  • внутрішніми — погано налагоджені процеси розробки та взаємодії всередині команди, відсутність контролю та стандартів розробки або банальний брак компетенції.

Не варто відкладати роботу над якістю коду й архітектури в довгий ящик. Під час спринту важливо вміти розділяти задачі, що мають пряму цінність для бізнесу (задачі з новою функціональністю), та технічні задачі, що мають для бізнесу лише непрямий ефект. Пропорція розподілу залежить від поточного стану проекту, часових рамок і кількості вільних рук.

ЩО ТАКЕ ЧИСТИЙ КОД?

Виходить, щоб сказати, що код чистий і система спроектована грамотно, легкого читання коду недостатньо. Він також повинен мати інші якості:

  • Код легко модифікувати. При правильному проектуванні та архітектурі розширення коду обходиться без особливих часових і технічних витрат. Суті коду не мають бути тісно пов'язаними між собою, код має бути частково абстрактним і самодостатнім. Кожна сутність, якою ми оперуємо при розробці, має відповідати тільки за свою частину функціональності.
  • Код має бути стабільним, передбачуваним, безпечним і надійним. Яким би простим код не був у читанні, він має бути покритий тестами. Хороший код і тести завжди поруч. Причому важлива не лише кількість тестів, а й їхня якість. Із таким кодом не виникає проблем при запуску та налагодженні, він не викликає змін у навколишньому середовищі.
  • Захищений код. При написанні будь-якого коду не можна забувати про загальну безпеку продукту. Рекомендую ознайомитися з базовими принципами безпеки та дотримуватись їх. Якщо йдеться про веб-проекти, рекомендую OWASP.
  • Хороший код — код, якого немає. Це не означає, що весь код має бути написаним в один рядок, а ви — обов'язково пишатися тонкими методами. Це означає, що код не варто дублювати, а частина спільних речей має залишитись на рівні абстракцій. Теоретично, спрощення коду має призвести до зменшення кількості дефектів.
  • Саме читання коду теж важливе. Кожен розробник має власний стиль написання, а рівень читання залежить від нашого досвіду. Всі ми хочемо писати простий, красивий та лаконічний код.

Щоб відійти від суб'єктивної оцінки якості свого коду, и 1961 році було введено термін “запахи коду” або “код з душком”. Це група правил і рекомендацій, що чітко визначають, чи пора робити рефакторинг. “Душок” веде до розпаду коду, і розробники завжди мають прагнути усунути його. Необхідність рефакторингу безпосередньо випливає з наявності запаху коду, та уникаючи причини, ми зможемо уникнути і слідства. Більш докладно про ключові ознаки того, що вам пора зайнятися рефакторингом, можна прочитати у книзі Мартіна Фаулера “Рефакторинг. Поліпшення існуючого коду”.

ЧИ ВАРТО ПИСАТИ ЧИСТИЙ КОД?

Однозначно варто! Але не завжди і не скрізь варто приділяти чистоті забагато уваги. Не варто забувати про доцільність і термін життя вашого коду. Наприклад, якщо перед вами стоїть задача розробки концепції — PoC (Proof of concept), і ви доводите, що обраний стек технологій виконує поставлену задачу, ваш код стане неактуальним вже за тиждень чи два. Не варто витрачати сили на вдосконалення цього функціонала.

Існує думка, що не потрібно стежити за якістю коду або частини системи, які незабаром буде замінено. І це неправильно з декількох причин. Висока якість виконання зробить перехід або інтеграцію з новими частинами більш простими, безшовними та швидкими. Вона напевно спростить життя у тих випадках, коли декілька версій коду доведеться підтримувати одночасно. Кількість регресійних помилок з чистим кодом буде значно менше. Також не варто забувати, що немає нічого більш постійного, ніж тимчасове. Можливо, задачі з поліпшення цієї частини коду ще декілька місяців лежатимуть у беклозі.

ЩО ДОПОМОЖЕ ПОЛІПШИТИ ВАШ КОД?

Більшість програмістів мріють писати код швидко та максимально красиво, причому так, щоб все ідеально працювало з першого разу. Але далеко не кожному вдається зробити код не просто працюючим, а й зрозумілим. Як же досягти успіху в написанні чистого коду? Є два шляхи: самоорганізація та командна робота.

article image

Самоорганізація

Розглянемо кілька можливих способів поліпшити індивідуальну якість коду. Ці рекомендації підійдуть розробнику будь-якого рівня.

  1. Інструменти
    Приділяйте увагу використовуваним інструментам, особливо головному з них — середовищу розробки. Зручний інструмент — це половина успіху. IDE (інтегроване середовище розробки) не лише доповнює код на льоту, а й підказує місця з душком, які варто привести до ладу. Розширивши IDE необхідним плагінами, ви отримуєте швейцарський армійський ніж. Більшу частину роботи ви проводите саме в цьому інструменті — зробіть його максимально зручним і гнучким. Придбайте свій улюблений продукт. Боротьба з ліцензіями скоротить вам частину часу, а офіційна підтримка ніколи не буде зайвою.
  2. Участь у open source проектах
    У командній роботі важлива можливість попрацювати з чужим кодом. Розуміти, читати та дотримуватися стилю не завжди просто. Працюючи з чужим кодом, нерідко вдається дізнатися про нові підходи до розв’язання нетривіальних задач. Читайте та вивчайте код!
  3. Суворі рамки
    Почніть розробку невеликого проекту, який розв’язує конкретну проблему. Самостійно розробіть архітектуру та реалізуйте її. При цьому ви можете встановити собі технічні обмеження. Наприклад, розробка тільки з використанням ООП, цикломатична складність методів не більше 10, дотримання всіх рекомендацій з розробки в даній мові, усвідомлене використання шаблонів проектування тощо. Звикайте працювати в рамках. Адже поруч не завжди буде хтось, хто зможе стежити за якістю вашої роботи.
  4. Розвиток абстрактного мислення
    Читайте та користуйтеся патернами програмування. Вони не прив'язані до конкретної мови і допомагають розв’язувати задачі більш ефективно. У вас буде однакове розуміння дизайну розв’язання задач з іншими розробниками. Ви розумітимете принципи роботи сторонніх інструментів і бібліотек. Розв’язуйте програмістські головоломки. Це чудовий спосіб поліпшити навички програмування та дізнатись про тонкощі обраної мови.
  5. Аналіз рішень
    Не поспішайте розв’язувати задачі в лоб. Ставте запитання старшим розробникам і самим собі. Завжди важливо розуміти причинно-наслідковий зв'язок тих чи інших рішень. Добре розуміючи проблему, ви зможете ефективно її розв’язати. Хорошій розробник — це не ремісник, який пише код, а інженер, який поєднує у своїй роботі прикладні дослідження, планування і проектування.
  6. Читання документації
    Вміти читати документацію не менш важливо, ніж читати код. Наступний крок — навчитися писати документацію.
  7. Практика!
    Будь-який досвід краще, ніж його відсутність.

Командна робота

Більшість задач виконуються в команді. Дуже важливо розділяти відповідальність за якість серед її учасників. Чим більше команда, тим складніше підтримувати продукт у гарному стані. Розглянемо декілька підходів утримання коду у вищевказаних умовах.

  1. Код рев'ю
    Це простий принцип для розуміння і виконання. Принаймні двоє людей, зокрема автор коду, перевіряють код. Під час перевірки коду необхідно враховувати декілька речей:
    • Одна з них — перевірка, чи не порушує код правила угоди про код. Це процес, який можна й потрібно автоматизувати за допомогою статичних аналізаторів у CI (безперервній інтеграції).
    • Інші — це maintainability (легкість у підтримці) коду та обробка помилок, які не можна перевірити автоматично.
    • Нарешті, що не менш важливо, код потрібно перевірити на повноту. Чи містить цей фрагмент коду весь обсяг функції, як було задумано?
  2. Безперервна інтеграція (Continuous integration)
    Суть безперервної інтеграції полягає в тому, що вона дозволяє швидко отримати безліч відгуків про поточний стан коду. Безперервна інтеграція працює, коли ви дотримуєтеся двох простих правил:.
    • Збірка продукту відбувається швидко. Не допускайте повільних збірок. Безперервна інтеграція покращує якість коду, оскільки забезпечує швидкий зворотний зв'язок. Якщо тести не пройдено, збірка не вдасться, ви миттєво отримуєте повідомлення.
    • Ви додаєте у скрипт збірки статичні аналізатори, які перевіряють угоди про кодування, підвищують якість коду та перевіряють безпеку.
  3. Угоди про кодування (Coding сonventions)
    Важливо мати список угод про кодування. Але перш ніж ви почнете складати список, всі в команді мають розуміти важливість цієї угоди. Не розраховуйте, що таку угоду буде ухвалено з першого разу, на вас чекає безліч дискусій.
    Складіть список угод про кодування, в яких ви зазначите, як змінні мають оголошуватись, угоди про імена тощо. Кількість правил, які ви можете додати до цього списку, не обмежена і може варіюватися. Просто робіть те, що працює для вас і вашої команди. Не соромтеся додавати нові правила до списку угод, якщо команді це підходить. Те саме стосується і видалення угод зі списку.
    Після того, як ви отримали свій список угод про кодування, вкрай важливо дотримуватися їх. Найкращий спосіб — перевірити угоди про кодування за допомогою статичних аналізаторів і безперервної інтеграції, оскільки вони не вимагають ручних дій.
    Якісний код може прискорити довгострокову розробку ПО. Його можна використовувати повторно, і розробникам не потрібно витрачати час на виправлення старих помилок. Це також полегшує приєднання нових людей до проекту.
  4. Тести
    Чим менше помилок у коді, тим вище його якість. Ретельне тестування відфільтровує критичні помилки та гарантує, що код працює так, як задумано.
    Наявність чіткої стратегії тестування є важливою, коли доходить до поліпшення якості коду. Як мінімум, ваш код має бути модульним. Ще краще, якщо ви хочете використовувати й інші способи, наприклад, інтеграційне чи регресійне тестування.
    Більшість тестів у програмному проекті мають бути юніт-тестами. Вони дешеві та швидкі. Існує безліч різних інструментів, які можуть допомогти вам у створенні модульних тестів і звітів про покриття коду. Запуск набору тестів і створення звіту про покриття коду можуть виконуватись автоматично за допомогою безперервної інтеграції. Можна навіть зробити збірку невдалою, якщо покриття коду не відповідає необхідному відсотку.
  5. Аналіз помилок
    Наявність помилок у вашому коді, ймовірно, є неминучою. Тому аналіз і спосіб обробки цих помилок є дуже важливими. Якщо ви хочете поліпшити свої навички, важливо вчитися на власних помилках. Коли виникає помилка, проаналізуйте її за допомогою декількох питань:
    • Це помилка з низьким чи високим пріоритетом? Якщо так, вона має бути негайно виправлена. Якщо помилка незначна та дозволяє продукту виконувати задачу без значних проблем, таку помилку можна виправити в наступних ітераціях.
    • Що пішло не так?
    • Чому ми не перевірили це (правильно)?
    • В яких ще містах це відбувається?
    • І найголовніше — як ми можемо запобігти подібному в майбутньому?

    Звісно, є інструменти, що допоможуть вам відстежувати помилки. Ви можете вибрати серед представлених на ринку трекер, що відповідає вашим потребам.
  6. Збір метрик
    Є кілька метрик, які ви можете використовувати для кількісної оцінки якості вашого коду. З такою задачею легко впорається SonarQube. Він з легкістю допоможе вам зібрати всі необхідно важливі метрики.
    • Потенційні помилки
      Кількість і серйозність дефектів — важливі показники загальної якості. Знаходження помилок можна й потрібно автоматизувати, але лише частково. Код рев'ю залишається в силі, щоб визначити більш глибокі помилки в самій логіці коду.
    • Повторення ділянок коду
      Кожна частина знання повинна мати єдине, несуперечливе й авторитетне представлення в рамках системи — принцип DRY (Do not repeat yourself).
    • Метрики складності
      Складність часто вимірюється за допомогою метрики цикломатичної складності. Це показник кількості лінійно незалежних шляхів у коді програми. Існує кореляція між числом цикломатичної складності та частотою дефектів. Теоретично спрощення коду має призвести до зменшення кількості дефектів.
    • Наявність необхідних коментарів
      Лише декількох правильно розставлених рядків з коментарями, коментаря до модуля, класу чи методу вистачить, щоб код став набагато зрозумілішим.
    • Рівень покриття коду тестами
      Він використовується при тестуванні програмного забезпечення та показує відсоток вихідного коду програми, який було виконано у процесі тестування. Задайте планку, нижче якої процентне співвідношення ваших тестів не знижується.

***

Помилки в коді чимось схожі на вуглецевий слід. Цілком уникнути його неможливо, а зайвий вихлоп сам собою не вб'є ані людства, ані природи. Проте зниження негативного ефекту від свого перебування на планеті сьогодні є природною потребою. Приблизно так само написання чистого коду є відповідальністю кожного розробника. Незалежно від того, який саме шлях ви виберете, необхідно прагнути писати працюючий і зрозумілий код.

Добре, якщо вийде не перетворювати чистоту на фетиш, враховуючи термін життя нашого коду та оцінюючи доцільність подальших поліпшень. Головне пам'ятати про людей: користувачів, яких може підвести раптова відмова навіть невеликої частини розробленої нами системи, та інженерів, які мають цю систему підтримувати.