Чому читабельність коду має значення?

Зрозуміло, що напрошувана (і правильна) відповідь - «Тому що код доводиться не тільки писати, а й читати». Ледь ця відповідь варта цілого посту, але автор їм не обмежується.

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

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

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

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

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

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

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

1. Узгодженість.

Я можу без проблем читати код у практично будь-якому стилі; довелося навчитися адаптуватися. Якщо я вважаю, що мені потрібно читати код, доводиться погоджуватися на те, що його стиль може відрізнятися від мого коханого. Тільки до одного пристосуватися неможливо - до коду, немов би написаному п'ятирічною дитиною, яка щойно переключилася з розкладки QWERTY на Dvorak. Чи вибираєте ви стиль самі або його вам нав'язують - дотримуйтеся його в усьому проекті.

2. Узгодженість.

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

3. Додайте простору.

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

4. Відставити банерні коментарі!

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

5. Не використовуйте магічні числа.

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

6. Вибирайте назви з розумом.

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

7. Не пишіть ланцюжком.

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

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

8. Обмежте розміри рядків і файлів.

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

Почати можна з історичного стандарту - 80 символів у рядку: не дуже вузько і підтримується при більшості моніторів і налаштувань. Якщо вся команда може працювати з рядками довжиною 100 або 120 символів, можна прийняти як стандарт їх; але тут важливо не перестаратися, оскільки занадто довгі рядки погано читаються самі по собі. Подумайте про газети і журнали - які вузькі у них колонки і як вони читаються. Рядки, при читанні яких доводиться рухати не тільки очима, але і головою, створюють додаткову фізичну напругу і уповільнюють роботу.

9. Робіть області видимості явними.

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

10. Коментуйте.

Код, навіть заплутаний і туманний, прекрасно розуміється комп'ютерами. Людям же, навпаки, потрібно розуміти не тільки те, що код робить, але і його призначення коду і причини, з яких він був написаний саме так. Без розуміння певних конструкцій, операцій і значень жоден розробник не може осмислено підтримувати код. Коментарі - відмінний інструмент передачі неочевидних нюансів коду. Поясніть бізнес-вимоги, допущення, вимоги, заплатки та тимчасові рішення. Поговоріть з напарником-розробником через коментарі. Втім, немає правил без винятків - не зловживайте коментарями. Не пишіть «зараз виникне помилка» перед генерацією винятку - команда throw говорить сама за себе; краще поясніть, чому в цьому випадку вже нічого не можна виправити.

На закінчення, зазначу, що деякий код потрібно робити нечитабельним. Так-так, ваші очі вас не обманюють. Код, який побудований на хаках, поганих практиках або тимчасових рішеннях, повинен читатися погано. Мій найкращий приклад - наведення типів. Часто екземпляри об'єктів потрібно приводити до певних типів - це стандартна практика, але не те щоб рекомендована. Запис перетворення в трошки менш читабельному вигляді - хороший спосіб змусити читача зупинитися і переконатися, що це не помилка. Це чимось схоже на текст, виділений курсивом, який складніше читати і який таким чином виділяється. Аналогічно, тимчасові рішення і хакі повинні виділятися. Маркери TODO і FIXME - гарний початок; фактично, якщо десь в коді і повинен бути банерний коментар, то саме тут - нехай відомі проблеми випирають з коду. (Головне - не забути їх виправити! - прим.пер.)

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

COM_SPPAGEBUILDER_NO_ITEMS_FOUND