Стендап Сьогодні

Що я зробив, що я хочу зробити, і що це все значить.
Повсякденні здобутки в форматі стендапу.
Детальніше в статті

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

13.05.2024

OpenSearch в тестовому оточенні

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


12.05.2024

Anbernic RG353M: ностальгія в кишені

🕹️ Минулого року натрапив на кишенькові консолі-емулятори. В мене (та й мабуть, у цілого прошарку українців) такий пристрій моментально викликає бурхливу ностальгію. З одного боку, вони нагадують кишенькову гру “9999 in 1”, з якою можна було грати в Тетрис в трамваї. З іншого, емулятори пропонують ігри з всіх приставок, які можна було зустріти у друзів чи в компʼютерних клубах — від NES до PlayStation 1. Та, на диво, з карткою памʼяті можна дійсно зібрати “9999 в 1” ігор з усіх поколінь на пристрої, який влазить в кишеню.

Обрати консоль насправді дуже складно, бо їх є безліч. Мене спочатку привабила модель, яка взагалі кріпиться на дармовис та здатна емулювати PSX - бо це просто чорна магія! Але ж на практиці з таким розміром нереально грати. Треба знайти вірний баланс розмірів, ціни, та можливостей саме для тебе. Можу порадити канал RetroGameCorps на YouTube, там є огляди, порівняння та інструкції.

Я зупинився на Anbernic 353M. Розміром вона приблизно з iPhone 15. Якість збірки чудова — краща за Nintendo Switch, наприклад. Софт я відразу переставив на ArkOS, за рекомендацією. Взагалі на таки пристрої стає або Android, або збірка Linux (як-от ArkOS). Далі залишається знайти комплект ігор та насолоджуватись. Головне, що всі емулятори вже готові до використання.

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


11.05.2024

YAGNI

✂️ Трохи рідше ніж DRY або KISS можна почути мантру YAGNI - вам це не знадобиться. Мантра нагадує, що зайві функції ускладнюють проєкт, та на варто витрачати час на те, що не є підтвердженою потребою користувачів.

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

Наведу приклад з проєкту, на який я витратив пару років, але так і не запустив. Це був застосунок на React Native з власним ORM на основі PouchDB; на кожному пристрої — власна база, з синхронізацією через CouchDB. Тут поки все прийнятно. Але… база, як завжди, потребує оказійних змін схеми — міграцій.

Міграції в розподіленій базі — набагато цікавіше, ніж в традиційній; як бути, коли клієнт зі старою версією застосунку отримує нові дані? А коли продовжує писати стару схему в спільну базу? Як уникнути конфліктів?

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

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

Засновник екстремального програмування висловив цей принцип ще екстремальніше: Вам потрібний гетер для змінної. Добре, пишіть. Але не пишіть сетер “бо теж знадобиться”. Не пишіть гетери для інших змінних “бо теж знадобляться.” Кращий спосіб писати код швидко це писати його менше.


10.05.2024

OLTP проти OLAP простими словами

Коли ви обираєте базу, щоб напхати в неї багато даних — давайте зараз не будемо думати про те, що таке “багато” - та ганяти по цих даних аналітику — звіти, статистику, і таке інше — то недовго потрібно, щоб прийти до баз категорії OLAP та вирішити, що це саме те, що тут потрібно. Snowflake, Redshift, Clickhouse - всі вони говорять про великі дані та аналітику.

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

Проблеми виникнуть, коли паралельних запитів буде багато. Бо типова OLAP база загнеться на 10-100 одночасних запитах. (Хмарна OLAP база на кшталт Snowflake буде просто зростати у витратах, тож там легше помітити заздалегідь, що багато запитів краще не робити.)

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


09.05.2024

Як влаштована кросплатформенність у Swift?

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

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

Все це у Swift розвʼязується просто та приємно — блоками умовної компіляції. Виглядають вони як директиви препроцесора в C та інших мовах: #if os(iOS) ... #endif. (Але, на відміну від C, ці блоки є частиною мови.) В блоки умовної компіляції можна заточити майже будь-яку ділянку коду:

MyContentView()
#if os(iOS)
  .navigationBarHidden(true)
#endif

Це чудово підходить для внесення точкових змін та не потребує багато думати про сумісність платформ: щоб скомпілювати застосунок SwiftUI з iOS на macOS, достатньо виключити всі специфічні інструкції. А головна маса стандартної бібліотеки спроєктована семантично універсальною.


08.05.2024

Особливості HTTP клієнтів на iOS

У Swift є практично монопольна бібліотека для HTTP запитів. Це Alamofire. Їй вже майже 10 років (тобто вона пережила всю еволюцію мови Swift з її суттєвими змінами.) Звісно, запити можна робити й стандартною бібліотекою — ба більше, Alamofire тільки її загортає.

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

Наприклад: є особливий метод download для завантаження великого файлу прямо на диск (ми отримуємо шлях до файлу.) Це не просто синтаксична зручність: наша програма не отримує зміст файлу та не змушена виділяти на нього памʼять. Коли файл завантажений, можна, наприклад, читати його по частинах.

Або: на iOS можна замовити завантаження в фоні. Йдеться про те, що наш застосунок може взагалі бути закритим та не палити процесор: операційна система сама виконає завантаження. Єдине, що фонове завантаження розраховане на один чи декілька файлів; з сотнями файлів, навіть маленьких, можна очікувати необмежених затримок через особливості планування.

Як на мене, то перекласти обробку HTTP на ОС це винахідливе рішення. Причому Apple справно підтримує нові стандарти: підтримка HTTP/3 зʼявилася ще у 2021.


07.05.2024

Завантаження відео з сайтів

Чи випадала тобі задача завантажити відео з інтернету? Мені чомусь багато разів, першу статтю про це писав ще у 2008… Ось кілька порад.

Для Youtube та багатьох інших сайтів є yt-dlp. Якщо стягнути з командного рядка вас влаштовує, то, може, більше нічого й не потрібно. Але якщо, наприклад, хочемо завантажувати відео прямо у мобільному застосунку?

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

Втім, будь-який хостинг буде використовувати звичайний компонент <video> для відтворення відео. А значить, відеопотік знаходиться за адресою, та нам залишається тільки її дізнатись. Колись відео захищали аплетами Flash та особливими кодеками, але цей час давно минув.

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

Також часто адреса відеопотоку отримується з виклику API. Його можна підглянути в браузері на вкладці “Мережа”. (Взагалі, інструменти розробника в браузері це перше, за що ми беремося.) Тут можемо виявити наступне питання: як авторизуватись? API зовсім без авторизації — рідкість. Але, знову ніякої магії немає: або мусимо відтворити куки, або отримати та передати API токен. Все це можна знайти, підгледівши запити, які робить справжня сторінка.

Нарешті, інколи сервер робить додаткові перевірки: User-Agent досить типова, а також Referer. Тут доведеться поекспериментувати. Для реверс-інжинірінгу API дуже корисний RapidAPI - в ньому легко будувати запити та випробувати альтернативи. Набагато зручніше ніж curl/wget.


06.05.2024

Автокатегоризація постів: підготовка

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

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

Далі мусимо перетворити кожен пост на послідовність слів, тобто токенізувати. Тут ніби нічого важкого. Застряг я на наступному кроці — стемінгу. Стемінг то відкидання від слова змінної частини, щоб алгоритм не плутався між “слово” та “словом” або “застрягнути” та “застряг”. В кожній мові стемінг свій; до того ж немає “стандартного” стемінгу, бо він не призводить слово до базової форми (це надто складно), а навмання відрізає закінчення. Тут потрібна бібліотека саме для української мови.

Поки що знайшов чудовий ресурс про обробку текстів українською мовою. Також колекцію стемерів. Далі буде.


05.05.2024

IAM в AWS та Google: різні речі

Нещодавно зробив коротке занурення в Google Cloud (бо я резидент AWS.) Коли працюєш з Terraform, може створитися уявлення, що всі хмари однакові, тільки ресурси мають різні назви. Виявилося навпаки, що система зі знайомою назвою IAM (“Керування особами та доступом”) працює фундаментально інакше.

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

…А в Google IAM я до кінця не зрозумів, але ніби права мають модель графа авторизації. По-перше, ролей тут немає, тільки користувачі або “сервісні” користувачі. А по-друге, дозволи призначаються звʼязком користувача та ресурсу. Звʼязок цей містить “роль”, тобто визначає дозволені операції.

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

Таке я не люблю. Політика в AWS виражає намір, який інформує її зміст. В Google по переліку звʼязків можна побачити, що існує, але не зрозуміти, чому. На щастя, якщо є Terraform, то намір можна викласти там.


04.05.2024

Будні проєкту на Ruby та Go

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

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

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

Мені, особисто, дуже подобається писати все на Go. Тому я компенсую це пошуком актуальних переваг з боку Ruby. Наприклад, потрібно було написати задачу, що повторюється за графіком. Саму задачу мені дуже легко написати на Go. Але це була б AWS Lambda. А графік запуску довелося б робити на AWS EventBridge. Та ще все це конфігурувати. Та підтримувати.

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

Одним словом, нема такого, що все чітко: тут Ruby, тут Go, нічого не перетинається. Вибір мови стає повсякденним питанням з вагомими наслідками.