Ми не знаємо, як працюють програми. Доведено на практиці
Привіт друзі! Я — Олексій Остапов, 15 років як QA-інженер, автор блогу QA Mania та один з ведучих подкасту «Питання Якості». І сьогодня я збираюсь вчити програмістів програмувати.
Ця стаття — розширена версія мого виступу на олімпіаді з програмування KPI Open 2023.
Ви зараз можете подумати «що цей тестер собі дозволяє? Що він в біса знає про розробку ПЗ, чого не знаємо ми? Кого він тут вчити зібрався?» Давайте спробую дати вичерпну відповідь:
- по-перше, я 15 років займаюсь автоматизацією тестування, тож програмувати вмію і люблю;
- по-друге, як тестер, я маю гарне розуміння якості програмного забезпечення;
- по-третє, я вже
9-й рік займаюсь викладанням, тож чогось можу навчити.
Математика, алгоритми і патерни
Існує хибна думка, що математика (тут і далі мається на увазі як мінімум шкільний курс, включаючи базові операції, рівняння, інтегрування і диференціювання; а також знання алгоритмів і патернів проєктування) переоцінені і здебільшого не потрібні в повсякденній роботі більшості розробників. Прихильники цієї думки дійсно мають непогані аргументи, тож я вирішив виписати аргументи за і проти, перш ніж іти далі.
Проти
- Математика не потрібна, щоб зробити черговий інтернет-магазин.
- Існує безліч фреймворків та бібліотек, розроблених розумними людьми, навіть розумнішими за мене, що вже мають вирішення більшості математичних проблем, тож не треба винаходити велосипед.
- У випадку якоїсь складної задачі завжди можна пошукати відповідь на Stackoverflow чи спитати в ChatGPT.
- Я ж розумний, я завжди можу вивчити, коли треба буде (ха-ха).
За
- Математика дає нам необхідну термінологію, щоб я мав змогу описати проблему, яку намагаюсь вирішити.
- Я можу знати більше ніж один спосіб вирішити одну і ту ж проблему, розуміючи переваги і недоліки кожного підходу.
- Мій улюблений аргумент, який я почув від Ніла Деграсса Тайсона: ми вчимо математику не для того, щоб все життя потім розв’язувати квадратні рівняння чи ошелешити всіх знанням теореми Піфагора — ми вчимось вирішувати проблеми. Наш мозок будує важливі нейронні зв’язки, які допоможуть нам і далі вирішувати різні проблеми і задачі. Важливо, не що ми вчимо, а як!
Одна з причин, чому я люблю олімпіади з програмування, — постановка задачі! Зазвичай вказується не просто написати алгоритм і надати відповідь, а, наприклад, написати програму, що виконує обчислення не більше ніж за Х секунд. Що змушує шукати оптимальні рішення, а не будь-які.
Якщо подумати над прикладами, то я одразу згадую, як я був молодший і ще тільки вчився в університеті. Ми вивчали і програмували на лабораторних роботах алгоритми сортування (бульбашкою, перестановками, вставками і т.д.). Для кожного з них була визначена складність, яку описують як, наприклад, O(n log n). Тоді я не дуже розумів, навіщо стільки алгоритмів, якщо результат ми маємо один! Чом би не взяти найшвидший і насолоджуватись життям? Але реальність завжди складніша і відповідь на моє питання досить очевидна: на швидкість кожного алгоритму напряму впливають якість вхідних даних, їх кількість, потужність заліза, на якому ми виконуємо алгоритм. І кожен раз, виграючи в швидкості, ми точно втрачаємо щось. І найгірше, що часто ми не можемо зрозуміти, що ми втратили аж до моменту, поки фінальні користувачі не знайдуть баг. Деякі алгоритми вимагають багато оперативної пам’яті для роботи, деякі — стають повільними на великих вибірках, деякі погано сортують вибірки з повторами.
Стандарт якості ПЗ
Я помітив, що дуже часто розробники, пишучи код, фокусуються саме на вирішенні задачі. Програма має робити певну функцію — програма робить функцію. Що ж тут поганого? Ми, тестери, знаємо про стандарт якості ПЗ ISO 25010, що допомагає нам дизайнити гарні тести. Цей стандарт визначає 8 характеристик якісного ПЗ. За стандартом функції — це лише 1 характеристика з 8. Хороші досвідчені програмісти часто беруть до уваги ще
Ми з Михайлом Чубом записали низку відео про стандарт якості для навчання тестерів, але я рекомендую до ознайомлення всім. Ось їх повний список зі спрощеними описом:
- Функціональна відповідність — міра, в якій програма виконує очікувані функції.
- Ефективність — міра, в якій програма використовує ресурси протягом певного часу.
- Сумісність — міра, в якій програма може працювати разом з іншими програмами.
- Зручність користування — міра, в якій програмою можуть користуватись окремі користувачі.
- Надійність — міра, в якій програма може виконувати свої функції, незважаючи на помилки.
- Безпека — міра, в якій програма захищає інформацію.
- Підтримуваність — міра, в якій програма може бути оновлена чи виправлена.
- Портативність — міра, в якій програма може бути використана в різних середовищах.
Тобто, розробляючи застосунок, кожен з нас має ставити собі питання не тільки про те, що застосунок має робити, а також (наприклад):
- Скільки ресурсів він має використовувати за одиницю часу?
- Чи може він працювати, умовно, з фаєрволами, антивірусами чи іншими фреймворками?
- Чи будуть користуватись застосунком люди з вадами зору, слуху чи інше?
- Що програма має робити, якщо трапляться помилки?
- Що програма має, а що не має зберігати?
- Як написати код так, щоб легко було протестувати, пофіксити баг чи написати плагін?
- В яких ОС чи браузерах буде працювати мій застосунок?
Теорія змови розробників фреймворків
На початку я вже написав, що існує безліч фреймворків та бібліотек, що дійсно можуть в 2 рядки коду вирішити багато крутих і дійсно складних задач (існує навіть жарт, що якщо взяти будь-яке слово англійської мови, додати до нього .js — майже гарантовано існує бібліотека з такою назвою 😂). Ніхто в здоровому глузді не буде для повсякденних задач писати велосипед. Але за зручність розробки ми платимо досить велику ціну — ми втрачаємо контекст! Багато налаштувань в сучасних фреймворках — неявні (implicit). Деякі версії одних фреймворків можуть бути не сумісні з іншими фреймворками.
Одного разу я писав пет-проєкт, підключив кілька популярних фреймворків, зробив типове рішення, все за гайдом, але запустити свій код не зміг через низку рандомних помилок. Через кілька годин пошуків в інтернеті я виявив, що мені необхідно знизити версію однієї бібліотеки, бо саме вона не сумісна з іншою бібліотекою, хоча це явно не декларувалось ніде. Після чого мені додатково довелось створити конфіг-файл, покласти його в потрібну директорію і прописати явно декілька параметрів. Про що не було ні слова в гайді. На щастя, я знайшов відповідь на Stackoverflow у людей, що мали такі самі проблеми, що і я. Проблема в тому, що я не знайшов відповіді навіть в офіційній документації! Ніби розробники спеціально ховають спеціалізовані кейси, щоб помучити допитливих програмістів.
Тобто, розробляючи застосунок, кожен з нас має пам’ятати:
- Що ми використовуємо?
- На якому залізі це буде працювати?
- На якій ОС?
- Яке буде типове мережеве підключення?
- Яке ПЗ вже буде встановлено там, де буде працювати застосунок?
- Які є явні і неявні налаштування наших бібліотек?
- Які версії фреймворків точно протестовані і сумісні з іншими версіями фреймворків?
Я маю дуже круту байку про контекст. 14 грудня 1966 року, на тестовому запуску ракета з безпілотним кораблем «Союз» не змогла взлетіти зі стартового майданчика — один з двигунів не загорівся. Автоматика зупинила запуск. Персонал космодрому пішов перевіряти стан ракети. І в цей час не заплановано спрацювала система аварійного спасіння (САС) «Союзу» (у випадку аварії корабель з екіпажем має вистрілити від ракети та посадити на парашутах на безпечній відстані). Двигуни САС підпалили горючу рідину, що була розлита на землі, сталася пожежа, одна людина загинула.
Розслідування причин аварії встановило, що ракету обладнано інерційною системою відліку, і комп’ютери порівнюють положення ракети за даними системи та згідно із запланованою траєкторією. Якщо є різниця — вистрілює САС. Під час розробки системи було закладено, що Земля — нерухома (і ракета на землі теж). В той же час Земля рухається і за приблизно півгодини після невдалого старту ракета разом з усією планетою (чималу змінну не врахували) відхилилась від запланованої траєкторії на 8 градусів, що і викликало запуск САС.
Зважайте, що контекст завжди більший ніж ви можете уявити.
Ніхто не знає, як працюють програми
Саме ця думка надихнула мене на виступ і на статтю, і ось нарешті я можу її пояснити одразу на крутому прикладі. Моя команда зараз розробляє кілька нових систем, і ми вирішили спробувати створити їх, використовуючи нові мови програмування, фреймворки і підходи. Але спочатку непогано було б переконатись, що нове рішення дійсно буде працювати краще і вартуватиме витрачених зусиль. Тож розробники створили декілька MVP на різних технологіях і віддали нашій команді на тестування. І неочікувано, рішення на новому модному стеці показало гірші метрики, ніж на старому в однакових умовах використання.
До чого цей приклад? Навіть знаючи теоретичні можливості фреймворків та інструментів, самі розробники не знають, як це буде працювати на практиці, поки не спробують на практиці. Тож немає нічого постидного розробникам визнавати, що вони не знають, як і що працює на 100%, і це ок — кооперуватись з тестерами, щоб краще зрозуміти те, що ми пишемо!
Підсумки
Спробую дуже коротко сформулювати підсумки своєї статті:
- Краще знати математику, ніж не знати (дякую, Кеп😂).
- Програмуючи, майте на увазі стандарт якості ПЗ, це допоможе написати кращий код.
- Ретельно вивчайте фреймворки, які використовуєте, щоб не мати помилок через нерозуміння контексту.
- Завжди тестуйте власний код, щоб на практиці переконатись, що він працює так, як задумано.
- Експериментуйте! Створюйте MVP та порівнюйте ваші рішення, щоб знайти найкраще. І пам’ятайте, що різниця між готовим продуктом і MVP — це якість!
Дякую, що дочитали. Го в коменти доводити, що ви точно знаєте, як і що працює, не те, що цей от тестер. 😀
Усі статті, обговорення, новини про тестування — в одному місці. Підписуйтеся на DOU | QA!
Найкращі коментарі пропустити