Видаляйте свій мертвий код



Пост «Видалення коду» Неда Бэтчелдера (Ned Batchelder) недавно з'явився на HN, хоча спочатку він був написаний в 2002 році. Тут я хочу повторити кілька думок Неда, і зайняти більш рішучу, ніж він, позицію: видаляйте код, як тільки ви помічаєте, що він більше не потрібен, без зайвих питань. Я також запропоную деякі поради з окопів, як визначати кандидатів у мертвий код.

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

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

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

Він повинен піти
Тижнів (Бэтчелдер, не Старк) був трохи більш делікатним і тактовним, ніж я в цій статті:
Скажімо, у вас є чудовий клас, який має купу методів. В один прекрасний день ви виявляєте, що якийсь окремий метод більше не викликається. Ви залишите його або видаліть?

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

Якщо ви не такі сміливі як я, пам'ятайте, що системи контролю версій прикриють вас у випадку, якщо вам коли-небудь знову знадобиться цей код.

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

Звичайно, я не кажу про такі речі, як повернення помилкових комітів — ми всі люди, і я роблю так само багато помилок, як і інші. Я маю на увазі, що я ніколи не видаляв об'єкти, надіслані в продакшен, а потім повертав через кілька тижнів або місяців, думаючи: «Ну і справи, схоже код, який я писав рік чи більше тому був дуже хороший, так що поверну-ка я його назад». Кодова база живе і розвивається з плином часу, тому старий код, ймовірно, не в'яжеться з новими ідеями, техніками, фреймворками і стилем використовуваним сьогодні. Я міг би повернутися до старої версії для оновлення, особливо, якщо це якісь тонкі моменти, але я ніколи не повертав код назад масово.

Так що зробіть послугу собі і своїй команді і видаліть мертвий код як тільки помітите його.

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

Додам ще одну гіпотезу список: ми всі можемо просто лінуватися. Безумовно, легше чогось не робити (тобто залишити як є), ніж щось зробити (видалити).

Лінь, зрештою, одна з трьох чеснот програміста. Але лінь, про яку говорить Ларрі Уолл іншого роду: «Якість, яке змушує вас докладати більше зусиль, щоб знизити загальні витрати енергії». З цієї точки зору, видалення мертвого коду це Лінь з великої літери Л — робити щось, що легко зробити зараз, щоб уберегти себе від необхідності робити щось складне в майбутньому. Ми всі повинні намагатися розвивати таку Лінь. Мені подобається думати про неї, як про «дисциплінованої ліні», нашою щоденною звичкою.

Як нам вибратися звідси?
Я проводжу більшу частину свого часу програмуючи на Python, для якого, на жаль, IDE зазвичай не можуть правильно аналізувати повну кодову базу і автоматично знаходити ніколи не викликається код. Але, поєднуючи дисципліну і деякі інструменти для аналізу програми під час виконання (run-time tooling), ми можемо підійти до цієї проблеми з двох сторін.

У простих випадках, гарне почуття коду може допомогти виявити і видалити мертвий код в той час як ви вносите зміни. Уявіть, що ви працюєте над певною функцією і помічаєте, що одна з гілок if/else ніколи не може бути виконана. Я називаю це «dead code in the small»* і його досить легко помітити і видалити, але це вимагає трохи більше зусиль, ніж можна було б витратити.

До тих пір поки ви не виробите звичку помічати це під час всієї вашої звичайної рутинної роботи ви можете додати ще один крок до дій, які ви виконуєте перед комітом: перевірте, чи немає поруч з вашими змінами мертвого коду. Це може статися як раз перед відправкою коду вашим колегам (адже ви проводите code review, правда?) так що їм не доведеться повторювати цей процес, під час перегляду ваших змін.

Інший вид мертвого коду з'являється при видаленні останнього, що використовує його класу або функції, коли ти вносиш зміни, не розуміючи, що це останнє місце, яке її використовує. Це «dead code in the large»*, і його важче виявити в ході звичайного програмування, якщо тільки вам не пощастило володіти эйдетической пам'яттю, або знати кодову базу як свої п'ять пальців.

Ось коли нам можуть допомогти інструменти для аналізу програми під час виконання. Magnetic ми використовуємо пакет Неда (так, того ж Неда) coverage.py, щоб допомогти нам приймати рішення про мертвому коді. Зазвичай coverage використовується під час тестування, щоб гарантувати, що ваші тест-кейси правильно виконують досліджуваний код, але ми також використовуємо його в нашому коді «працює як зазвичай», щоб зрозуміти, що використовується, а що ні:
import coverage

cov = coverage.Coverage(
data_file="/path/to/my_program.coverage",
auto_data=True,
cover_pylib=False,
branch=True,
source=["/path/to/my/program"],
)
cov.start()

# ... робимо що-небудь ...

cov.stop()
cov.save()

Тут створюється об'єкт Coverage з декількома опціями, щоб зробити звіт більш зручним. Спочатку ми говоримо йому, де зберігати свої дані (ми будемо використовувати їх пізніше, для створення зручного HTML-звіту, що використовується, а що — ні), і просимо його автоматично відкривати і додавати їх в цей файл з допомогою auto_data=True. Далі ми просимо не турбуватися про обробці стандартної бібліотеки і встановлених пакетів — це не наш код, так що ми можемо припустити, що багато з того що він містить, може не використовуватися. Це не мертвий код, який ми повинні підтримувати, так що можна сміливо ігнорувати його. Ми просимо його обчислити покриття розгалужень (виконуються обидва стану true та false для кожного оператора if). І, нарешті, ми вказуємо де знаходяться наші вихідні, так що він може з'єднати свої знання про те, що використовується не використовується у вихідному коді, для складання звіту.

Після запуску нашої програми, ми можемо створити HTML-звіт:
$ COVERAGE_FILE=/path/to/my_program.coverage coverage html -d /path/to/output

Який виглядає приблизно так:

повний приклад HTML-звіту можна знайти на документації coverage.py

Рядки виділені червоним кольором не викликалися під час виконання програми. Ці рядки (і, можливо, методи) кандидати для видалення як мертвого коду.

Я залишу вам три попередження про використання цього підходу для знаходження і видалення мертвого коду:
  1. Будьте обережні при розгляді підсумків роботи coverage — той факт, що рядок або функція не були виконані протягом одного запуску програми не означає, що вони обов'язково мертві або недоступні взагалі. Ви все одно повинні перевіряти код, щоб визначити, чи дійсно вони абсолютно мертві у вашому додатку.
  2. Обчислення покриття означає, що ваша програма повинна виконати більше роботи, так вона буде повільніше, при запуску в цьому режимі. Я б не рекомендував запускати це в продакшені, але в проміжній середовищі (staging environment) або в цільових сценаріях все повинно бути в порядку. Як завжди, якщо продуктивність є важливим завданням, ви повинні точно виміряти, який вплив робить обчислення покриття, перш ніж запускати його.
  3. Нарешті, не довіряйте звітів про покриття коду під час тестових запусків. Якийсь код може бути мертвий, але тести все одно будуть запускати його; і якийсь код може бути живий, але не запускатися тестами!


Слова на прощання
Перед тобою, любий читачу, я повинен вибачитися. Я опустив важливу частину поста Неда, коли цитував його раніше.
Він говорить:
В даному випадку немає простої відповіді на це питання, тому що це залежить від класу і від методу. [...] Грубий відповідь може бути така: якщо цей клас є частиною фреймворку, то залиште його, якщо він частину програми, то видаліть його.
Якщо ви пишете бібліотеку або фреймворк, а не додаток, то питання мертвого коду стає з одного боку складніше, а з іншого простіше. По суті, ви ніколи не зможете видалити частину публічного API (за винятком несумісності в мажорних версіях). Фактично весь ваш публічний API це живий код, навіть якщо ви самі не використовуєте його. Але за зовнішнім інтерфейсом, все ще може виникати мертвий код, і він повинен бути вилучений.

Видаляйте свій мертвий код!



* — відсилання до парадигм програмування «programming in the small» і «programming in the large» (прим. перекл.)
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.