Стендап Сьогодні
📢 Канал в Telegram @stendap_sogodni
🦣 @stendap_sogodni@shevtsov.me в Федиверсі

🤖🚫 AI-free content. This post is 100% written by a human, as is everything on my blog. Enjoy!

Пости з тегом #Програмування

14.03.2025

Патерни програмування

Книжка Gang of Four є мало не обовʼязковим читанням для досвідчених програмістів. А з іншого боку — чув й такі думки, що це нудна академічна маячня, яку ніхто на практиці не використовує. Або що це щось зі світу Hello World Enterprise Edition.

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

Втім насправді ці патерни є формалізацією підходів, які й так існували. В них більше спільного з гуманітарними, а не точними науками. Наприклад, у психології є архетипи, а в літературі - 36 драматичних ситуацій. (Але не все у програмуванні таке “мʼяке”; от, алгоритми вимагають жорсткого дотримання.)

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


03.05.2025

Синдром непотрібного узагальнення

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

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

Або приклад, який я й помітив: оцінка тривалості задачі. Теоретично, вона може бути будь-яка. Але фактично, мене цікавить: чи встигну я зробити задачу десь в перерві (5-10 хвилин), або чи встигну за “підхід”, тобто за одну-дві години. А якщо довше, то її вже можна розбивати, бо фактично більшого безперервного відрізка часу в мене не буде. Отже, немає сенсу думати про точну тривалість у хвилинах.

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

Або, парадоксально, навпаки: узагальнення виходить із ліні та небажання дослідити предметну галузь. Так з інтерфейсами трапляється: ми бачимо поля вільного вводу там, де міг бути вибір зі списку чи слайдер. Або з трьох прикладів обчислень вигадуєш формулу… яка вірна тільки для цих прикладів. Бо ніякої формули немає, а є тільки табличка, про яку треба було спитати.

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


14.05.2025

Чому копіпаста — це погано?

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

Копіпаста (а саме, скопійований багато разів код) погана не тільки тим, що вона повторюється. Тоді можна було б її більш-менш ігнорувати. Значно гірше, що копіпаста рідко повторюється дослівно, та ми ніколи не знаємо, чи це так. Доводиться серед нудних повторюваних рядків “знайти 10 відмінностей”. Або — скоріше — просто проґавити.

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

Академічні рішення — всякі DRY та правильне структурування коду — на практиці не завжди вдається використати, бо не весь код можна, умовно, загорнути в функцію чи винести в окремий клас. Тому нагадаю ще про кодогенерацію - а саме, заміну “майже повторюваного” коду на шаблон.


17.05.2025

Якою мовою пишуться мови програмування?

Отримав (від дружини!) таке цікаве запитання. Бо, власне, чи є тут якась драбина мов, та якщо є — то де вона закінчується?

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

Втім, як вийти з цього парадоксу курки та яйця? Тут зазначу, що є два різновиди мов. Компільовані мови перетворюються в машинний код, який вже не залежить від самої мови. А значить, достатньо першу версію компілятора написати іншою мовою, а згодом, маючи скомпільований компілятор, переписати його “рідною”. Так було з С, C++, Go, Rust, Haskell тощо.

Інтерпретовані мови завжди виконуються в контексті іншої програми — інтерпретатора. Тому інтерпретатор доведеться написати компільованою мовою — за власні підтяжки себе не витягнеш. Наприклад, Ruby та Python написані на C - матері всіх мов. Але більша частина інструментів та бібліотек все одно пишеться рідною мовою.

Є ще мови з віртуальною машиною — це дещо посередині. Їхній код компілюється не в машинний, а в код віртуальної машини (байт-код). Та компілятор цей пишеться рідною мовою. А сама віртуальна машина — повноцінною компільованою мовою. Наприклад, машини Erlang та Java написані знову на C.

Отже, виходить так: зрілі компільовані мови написані самі собою, а інтерпретовані — зазвичай на C/C++. Мови із віртуальною машиною - 50 на 50.


20.05.2025

Асемблер та машинний код

В продовження поста про мови, якими пишуться інші мови, все ж залишається питання: а що в самому низу? Які інструкції розуміє компʼютер — що таке машинний код? Та чим він відрізняється від асемблера (мови програмування)?

Це взагалі цікаво, бо машинний код не складається з “найпростіших” інструкцій, тобто він не є “в самому низу” (хоча що це взагалі означає?) Зокрема, інструкції Intel x86 взагалі можуть містити в себе цілу підпрограму, як-от мені колись допомогла інструкція POPCNT для підрахунку бітів. І вона не найскладніша. Але — машинний код це найнижчий рівень, на який може залізти програміст, тому не більше й не менше, він розмежовує програмне та “апаратне”.

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

(Те, що він є “двійковим”, мало що змінює. В компʼютері все “двійкове”. Як є редактор для тексту, так само може бути редактор для машинного коду. І є — наприклад, ImHex це вміє.)

Також в машинному коді немає коментарів, та кожен хто колись виправляв проблеми з package.json знає, наскільки важко без них. Ну але кожен знає, що це теж незручно, але можливо.

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

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


21.05.2025

Тести, дебаг та обережність

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

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

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

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

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

А наприкінці виявилось, що я забув, що go test ./... запускає пакети тестів паралельно. І тільки з опцією -p 1 - ні. Так що на цей раз моя проблема мала дуже просте рішення — яке я не бачив через власні переконання. (І ще через купу інших можливих причин, які довелося відкинути.) 😅