07.08.2022

Сергій Немчинський: Що таке передчасна оптимізація? Чому це погано?

Сергій Немчинський
10 хвилин перегляду
Сергій Немчинський: Що таке передчасна оптимізація? Чому це погано?

Розуміння, що таке передчасна оптимізація і чому це погано, мають далеко не всі програмісти. Давайте поговоримо на цю тему.

Передчасна оптимізація – корінь всіх зол

Так сказав Дональд Кнут, автор книги “Мистецтво програмування”. Чому він так вважає? Спочатку розберемося з термінами.

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

Ціна передчасної оптимізації занадто висока

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

Мета написання коду – принести користь бізнесу. Виходячи з цього, оптимізація є передчасною, коли вона ще не потрібна бізнесу. Оптимізація – така сама фіча, як і, наприклад, якась кнопка на екрані, інструмент для користувача, кабінет і ін. Робити її потрібно тільки в тому випадку, коли вона знадобиться бізнесу і він її зажадає.

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

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

Ціна передчасної оптимізації може виявитися космічною.

Новачки хочуть все оптимізувати

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

При цьому я жодного разу не чув подібних розмов від топових програмістів, а спілкувався з багатьма. Жоден з них не починав порівнювати, що якась мова працює повільніше або швидше (хіба що як відповіді на запитання комусь). А якщо така розмова і заходить, то в дусі, що мова працює досить швидко. Ось це “досить швидко” і означає, що далі оптимізувати буде не оптимально. Розробники-початківці дуже люблять тему швидкості роботи. Наприклад, що мова С набагато швидше за Python. Ок, але тільки сайти не будуть писати мовою С, тому що це дуже дорого. Для цього використовують Python або PHP. А далі, коли ринок це запросить, розпочинається робота над швидкістю. І те прискорення йде до «досить швидко», і на скільки саме визначає ринок.

Як виходять успішні проекти?

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

Розповім вам, як із погляду бізнесу з’являються успішні проекти. Спочатку з чого є ліпиться ідея проекту – Proof of concept. Просто, щоб показати потенційному замовнику, що ми можемо це реалізувати. Воно може дуже гальмувати, глючити і взагалі працювати чорт знає як. Але якщо потенційний замовник скаже, що все чудово — це все вивалюється на ринок прямо так, як є, вносяться лише мінімальні зміни.

Час працює проти вас. Потрібно максимально швидко застовпити максимально більшу частину ринку. Тож перші кілька років стартап змушений повністю забити на будь-яку оптимізацію. Я завжди говорю, що якщо у стартапі ви пишите unit test, ви витрачаєте гроші клієнта просто так – не робіть цього. Вам потрібно поспішати, робити якнайшвидше, забуваючи про технічний обов’язок і про все інше. Тому що зараз стоїть питання виживання, а гарний вилизаний код — справа другорядна. Або ви встигаєте вийти на ринок, або ні, і тоді висока якість коду буде до одного місця.

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

Цікаве спостереження. У всіх успішних проектах, у яких я працював, юніт-тестами було покрито менше ніж 10% коду. Чому? Саме через описане вище – проект зростає швидше, ніж ви встигаєте дописати до нього юніт-тести.

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

Як робити правильну оптимізацію програм?

Як оптимізувати, щоб оптимізація не була передчасною? Спочатку ви пишіте програму так, щоб вона працювала. Потім, коли у вас з’явиться можливість, наводите у ній порядок, проводьте рефакторинг (якщо, звичайно, ви не на етапі proof of concept). Після цього запускаєте програму, якщо потрібно, проводите вимір часу роботи, і питаєте бізнес, чи підходить йому така швидкість. Швидше за все, про швидкість навіть питати не доведеться, бо якщо не Ок — вам і так скажуть. Але якщо все ж таки хочете перестрахуватися — запитайте. Але пам’ятайте, що оптимізувати швидкість роботи без такого запиту з боку бізнесу не потрібно.

Якщо ви отримали такий запит від бізнесу, що потрібно робити? Ви, мабуть, скажете – йти та оптимізувати. Ні, ні в якому випадку! Як усе відбувається. Щось гальмує, програміст припускає, в якому шматку коду проблема і хоче його оптимізувати. Але як показує моя практика, у 80% випадків програміст помиляється зі своєю оцінкою. Відповідно, якщо почати оптимізувати відразу, ви оптимізуєте не те, що потрібно і не вирішите проблему. Візьміть за істину — ви не знаєте, де гальмує. Щоб дізнатися про це, потрібно запустити профайлер. Після цього ви вже знатимете місце, яке гальмує (і воно майже напевно буде не тим, про яке ви думали спочатку). І ось саме його ви псуєте. Ви повинні розуміти, що для того, щоб оптимізувати код, його потрібно зробити жахливим. Якщо ви пишете код у розрахунку на те, що він оптимізуватиметься, значить у вас весь код неможливо читати. Якщо ж ви спочатку написали гарний код, який гальмує, то потім оптимізуватимете тільки те місце, яке реально потрібно оптимізувати.

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

Дисклеймер

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

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

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

Завжди ваш Сергій Немчинський