Як впровадити статичний аналізатор коду в legacy проект та не демотивувати команду

Як впровадити статичний аналізатор коду в legacy проект та не демотивувати команду
Спробувати статичний аналізатор коду легко. А ось, щоб впровадити його, особливо на розробку великого старого проекту, буде потрібно вміння. При неправильному підході аналізатор може додати роботи, уповільнити розробку та демотивувати команду. Давайте коротко поговоримо, як правильно підійти до інтеграції статичного аналізу у процес розробки та почати його використовувати як частину CI/CD.

Запровадження

Нещодавно мою увагу привернула публікація.Getting Started With Static Analysis Without Overwhelming the TeamЗ одного боку, це хороша стаття, з якою варто познайомитися. З іншого боку, як мені здається, в ній так і не прозвучало повної відповіді, як безболісно впровадити статичний аналіз у проект з великою кількістю legacy коду. У статті йдеться, що можна упокоритися з технічним боргом і працювати тільки з новим кодом, але немає відповіді на те, що робити з цим технічним боргом потім.

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

Проблематика

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

Усі статичні аналізатори видають хибні спрацьовування. Такою є особливість методології статичного аналізу коду, і з нею нічого не можна зробити. У випадку це нерозв'язне завдання, що підтверджується теоремою Райса [2]. Не допоможуть і алгоритми машинного навчання3]. Якщо навіть людина не завжди може сказати, чи помилковий той чи інший код, то не варто чекати цього від програми :).

Хибні спрацьовування не є проблемою, якщо статичний аналізатор вже налаштований:

  • Вимкнено неактуальні набори правил;
  • Вимкнено окремі неактуальні діагностики;
  • Якщо ми говоримо про C або C++, то розмічені макроси, що містять специфічні конструкції, через які з'являються непотрібні попередження у кожному місці, де такі макроси використовуються;
  • Розмічено власні функції, що виконують дії аналогічні системним функціям (свій аналог memcpy або printf) [4];
  • Точково за допомогою коментарів відключено помилкові спрацьовування;
  • І так далі.

У такому разі, очікується низький рівень хибних спрацьовувань на рівні порядку 10-15% [5]. Іншими словами, 9 з 10 попереджень аналізатора будуть вказувати на реальну проблему в коді або принаймні на «код із сильним запахом». Погодьтеся, такий сценарій дуже приємний, і аналізатор є справжнім другом програміста.

Як впровадити статичний аналізатор коду в legacy проект та не демотивувати команду
Насправді у великому проекті початкова картина буде зовсім інша. Аналізатор видає сотні чи тисячі попереджень на legacy код. Що із цих попереджень у справі, а що ні, швидко зрозуміти неможливо. Сісти і почати розбиратися з усіма цими попередженнями нераціонально, оскільки основна робота у цьому випадку зупиниться на дні чи тижні. Як правило, команда не може дозволити собі такий сценарій. Також виникне величезна кількість diff-ів, які псують історію змін. А швидке масове виправлення такої кількості фрагментів у коді неминуче виллється в нові помилки та помилки.

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

Звісно, ​​навіть несерйозні помилки однаково залишаються помилками. А іноді за помилкою може ховатися справжня вразливість. Однак, кинути все і днями/тижнями займатися дефектами, які слабо проявляють себе, виглядає сумнівною ідеєю.

Програмісти дивляться, дивляться, дивляться на всі ці попередження на старий код, що працює… І думають: обійдемося без статичного аналізу. Ходімо краще новий корисний функціонал писати.

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

Тут та сама аналогія, що і з попередженнями компілятора. Недарма рекомендують тримати кількість попереджень компілятора на рівні 0. Якщо попереджень 1000, то коли їх стане 1001, на це ніхто не зверне увагу, та й не зрозуміло, де шукати це нове попередження.

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

Впровадження та усунення технічного боргу

Насправді немає нічого складного і страшного у впровадженні статичного аналізу навіть у великий старий проект.

CI/CD

Причому аналізатор можна одночасно зробити частиною процесу безперервної розробки. Наприклад, у дистрибутиві PVS-Studio є утиліти для зручного перегляду звіту у потрібному вам форматі, та повідомлення розробників, які написали проблемні ділянки коду. Тим, хто детальніше цікавиться запуском PVS-Studio з-під CI/CD систем, рекомендую ознайомитися з відповідним розділом документації та циклом статей:

Але повернемося до питання великої кількості помилкових спрацьовувань на перших етапах застосування інструментів аналізу коду.

Фіксація існуючого технічного боргу та робота з новими попередженнями

Сучасні комерційні статичні аналізатори дозволяють вивчати лише нові попередження, які у новому чи зміненому коді. Реалізації цього механізму різниться, але суть одна. У статичному аналізаторі PVS-Studio ця функціональність реалізована в такий спосіб.

Щоб швидко розпочати використання статичного аналізу, ми пропонуємо користувачам PVS-Studio скористатися механізмом масового придушення попереджень [6]. Загальна ідея наступного. Користувач запустив аналізатор і отримав багато попереджень. Якщо проект, що розробляється багато років, живий, розвивається і приносить гроші, то, швидше за все, звіт не матиме багато попереджень, що вказують на критичні дефекти. Іншими словами, критичні баги так чи інакше вже виправлені дорожчими способами або завдяки фідбеку від клієнтів. Відповідно все, що зараз знаходить аналізатор, можна вважати технічним боргом, який непрактично намагатися усунути відразу.

Можна вказати PVS-Studio вважати ці попередження поки що неактуальними (відкласти технічний борг на потім), і він більше не показуватиме їх. Аналізатор створює спеціальний файл, де зберігає інформацію про поки що нецікаві помилки. І тепер PVS-Studio видаватиме попередження лише на новий або змінений код. Причому все це реалізовано розумно. Якщо, наприклад, на початок файлу з вихідним кодом буде додано порожній рядок, то аналізатор розуміє, що, по суті, нічого не змінилося, і, як і раніше, мовчатиме. Цей файл розмітки можна закласти у систему контролю версій. Файл великий, але це не страшно, тому що часто його закладати немає сенсу.

Тепер усі програмісти бачитимуть попередження, які стосуються лише нового або зміненого коду. Таким чином, аналізатор можна почати використовувати, як то кажуть, з наступного дня. А до технічного обов'язку можна буде повернутися пізніше, і поступово виправляти помилки та налаштовувати аналізатор.

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

Виправлення помилок та рефакторинг

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

if (a = b)

На подібний код лається більшість С++ компіляторів та аналізаторів, оскільки висока ймовірність, що насправді хотіли написати (a == b). Але існує негласна угода, і це зазвичай зазначено в документації, що якщо стоять додаткові дужки, то вважається, що програміст свідомо написав такий код, і лаятись не треба. Наприклад, у документації PVS-Studio до діагностики V559 (CWE-481) явно написано, що наступний рядок буде вважатися коректним і безпечним:

if ((a = b))

Інший приклад. Чи забутий у цьому C++ коді перерву чи ні?

case A:
  foo();
case B:
  bar();
  break;

Аналізатор PVS-Studio видасть попередження V796 (CWE-484). Можливо, це не помилка, і тоді слід підказати аналізатору, додавши атрибут [[fallthrough]] або, наприклад, __attribute__((fallthrough)):

case A:
  foo();
  [[fallthrough]];
case B:
  bar();
  break;

Можна сміливо сказати, що подібні зміни коду не виправляють помилки. Так, це так, але виконується дві корисні дії. По-перше, звіт аналізатора позбавляється помилкових спрацьовувань. По-друге, код стає зрозумілішим для людей, які займаються його супроводом. І це дуже важливо! Заради одного цього вже варто проводити дрібний рефакторинг, щоб код став зрозумілішим і легшим у супроводі. Якщо аналізатору незрозуміло, чи потрібен «break», це буде незрозуміло і колегам-програмістам.

Крім виправлення помилок та рефакторингу, можна точково пригнічувати явно хибні попередження аналізатора. Якісь неактуальні діагностики можна вимкнути. Наприклад, хтось вважає безглуздими попередження V550 порівняння значень типу float/double. А хтось відносить їх до важливих і вартих вивчення [7]. Які попередження вважати актуальними, а яким ні, вирішувати лише команді розробників.

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

Також ми завжди допомагаємо нашим клієнтам налаштувати PVS-Studio, якщо виникають якісь складнощі. Більше того, були випадки, коли ми самі усували помилкові попередження та виправляли помилки [8]. Про всяк випадок вирішив згадати, що можливий і такий варіант розширеного співробітництва :).

Метод храповика

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

Як впровадити статичний аналізатор коду в legacy проект та не демотивувати команду

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

Навіть якщо людина захоче трохи схитрувати і вирішить пройти quality gate не завдяки усуненню попереджень у новому коді, а за рахунок покращення старого стороннього коду, це нестрашно. Все одно храповик провертається в один бік, і поступово кількість дефектів зменшуватиметься. Навіть якщо людина не хочеться правити свої власні нові дефекти, їй все одно доведеться щось покращити у сусідньому коді. У якийсь момент легкі способи зменшення кількості попереджень закінчуються, і настає момент, коли виправлятимуться справжні помилки.

Більш детально цю методологію описано в дуже цікавій статті Івана Пономарьова.Впроваджуйте статичний аналіз у процес, а не шукайте за його допомогою баги"З якою я рекомендую ознайомитися кожному, хто цікавиться питаннями підвищення якості коду".

Автор статті має й доповідь на цю тему: "Безперервний статичний аналіз".

Висновок

Сподіваюся, після цієї статті читачі дружелюбніше сприйматимуть інструменти статичного аналізу та захочуть впровадити їх у процес розробки. Якщо у вас залишилися питання, ми завжди готові проконсультувати користувачів нашого статичного аналізатора PVS-Studio та допомогти з його впровадженням.

Існують інші типові сумніви, чи зможе статичний аналіз дійсно бути зручним і корисним. Я постарався розвіяти більшість таких сумнівів у публікації «Причини впровадити у процес розробки статичний аналізатор коду PVS-Studio» [9].

Дякую за увагу, і приходьте завантажити та спробувати аналізатор PVS-Studio.

Додаткові посилання

  1. Андрій Карпов. Як швидко подивитися цікаві попередження, які видає аналізатор PVS-Studio для C та C++ коду?
  2. Wikipedia. Теорема Райса.
  3. Андрій Карпов, Вікторія Ханієва. Використання машинного навчання у статичному аналізі вихідного коду програм.
  4. PVS-Studio. документація. Додаткове налаштування діагностик.
  5. Андрій Карпов. Характеристики аналізатора PVS-Studio на прикладі EFL Core Libraries, 10-15% хибних спрацьовувань.
  6. PVS-Studio. документація. Масове придушення повідомлень аналізатора.
  7. Іван Андряшин. Про те, як ми випробували статичний аналіз на своєму проекті навчального симулятора рентгенендоваскулярної хірургії.
  8. Павло Єрємєєв, Святослав Розмислів. Як команда PVS-Studio покращила код Unreal Engine.
  9. Андрій Карпов. Причини впровадження в процес розробки статичного аналізатора коду PVS-Studio.

Як впровадити статичний аналізатор коду в legacy проект та не демотивувати команду

Якщо хочете поділитися цією статтею з англомовною аудиторією, прошу використати посилання на переклад: Andrey Karpov. How to introduce static code analyzer в legacy project and not to discourage the team.

Джерело: habr.com

Додати коментар або відгук