Стендап Сьогодні
Що я зробив, що я хочу зробити, і що це все значить.
Повсякденні здобутки в форматі стендапу.
Детальніше в статті
Підписатись на RSS
📢
Канал в Telegram @stendap_sogodni
🦣
@stendap_sogodni@shevtsov.me в Федиверсі
03.10.2025
Дивна природа багаточастинних листів
Якщо довелося тобі надсилати електронну пошту автоматично, то, певно, знаєш, що в листа може бути два “смаки”: просто текст та HTML. А також — вкладення. Тож, як воно там всередині влаштовано?
Для надсилання кількох частин в одному листі використовується тип змісту multipart
. Приблизно, до речі, такий саме, як і при надсиланні вебформи з файлом. Тільки там multipart/form-data
, а в листі буде multipart/alternative
, що значить, що клієнт мусить обрати одну з частин за власними можливостями.
Стоп, скажеш ти, а як тоді вкладення вписуються? А для вкладень вже є multipart/mixed
, який нічого не говорить про природу частин. Але як зробити і текст/HTML, і вкладення? Ооо, тут ми дізнаємося, що всередині multipart/mixed
як одна з частин може сидіти multipart/alternative
. Та й взагалі технічно листи у форматі multipart
містять ціле дерево частин, майже файлову систему!
Багаточастинні листи також використовуються у сповіщеннях про спам чи просто сервісних відповідях, для того є тип multipart/report
. Однією з частин буде оригінальний лист, іншою — зміст відповіді. Взагалі там ціла купа тих типів, про які я навіть не знав. От, наприклад, контейнери для шифрування.
І все це просто щоб показати лист з якимось там логотипом. Звісно, користувачам можна про все це не треба знати, але коли ти розробляєш поштового клієнта чи інфраструктуру — доведеться враховувати всі можливі ускладнення.
02.10.2025
Тимчасові рішення
От є перед вами задача, та й наче зрозуміло, що з нею робити, але знайдений підхід “не масштабується”, чи має ще якесь відоме обмеження. А хочеться справжнє, “постійне” рішення, щоб другий раз до нього не вертатись. Тільки, зазвичай, “постійне” рішення виглядає складнішим та дорожчим. Інколи — залежно від фантазії — набагато. Тож що робити?
Я ось що скажу. Будь-яке рішення є тимчасовим. Може, буквально відомий термін його дії. Може воно буде зупинене неочікуваними обставинами. Може, стане не потрібним. А потім, нарешті, будь-який продукт припинить своє існування.
Тож не треба боятися “тимчасових” рішень. Краще поставити питання — на який термін це рішення нам потрібно? (Та який взагалі “горизонт подій” ми здатні уявити?) Коли знаємо простий, хоч може й обмежений розвʼязок, який стільки “проживе” - то й чудово!
Є й інша проблема — рішення “назавжди” так само може виявитися не таким вічним, як нам хотілося. А поступатися зручністю та ускладнювати собі життя доведеться вже сьогодні. Зате якщо зробити просте рішення зараз, може через рік краще буде зрозуміло що ж його робити далі… на наступний “тимчасовий” термін. :)
01.10.2025
Автовідповідач для Ping
Після нещодавнього досвіду Shortcuts для експорту нагадувань з Apple Reminders - який, до речі, вже тиждень як зберігає мені щоденні резервні копії — зʼявилася ще одна ідея. (А експорт вже є в опублікованому застосунку).
Нагадаю, що я розробляю стохастичний таймтрекер Ping. Він питає у випадкові моменти — чим я займаюсь? З деякими заняттями це дуже зручно, а з іншими — просто ніяк не зручно. Наприклад, за кермом я очевидно нічого не відмічу. А потім залишаються сірі ділянки часу.
Я шукав різні рішення для автотегінгу. Наприклад, час сну та час тренувань Ping вміє читати з Apple Health. А далі що? Далі починалися різні складні імпорти та інтеграції незрозуміло з чим незрозуміло як. Поки я несподівано не згадав про Shortcuts.
Отже, функція “автовідповідач”. Суть проста — можна в будь-який момент зайти в Ping, та вказати теги “на майбутнє”. Далі застосунок буде проставляти ці теги на кожний пінг, аж поки автовідповідач не буде вимкнений.
Сама по собі така функція не дуже зручна. Зате якщо поєднати її з Shortcuts, то вона перетворюється на потужний механізм автоматизації! Наприклад: зробити скрипт “коли сідаєш в машину -> увімкнути автовідповідач” та “коли виходиш -> вимкнути”. І все! Задача вирішена. Так само можна вмикати за режимом фокуса, за локацією та іншими тригерами.
Обмеженням є те, що все ж тригерами можуть бути тільки системні події, а не такі що відбуваються в інших застосунках. Тож Shortcuts не є повним аналогом IFTTT чи Zapier, а шкода.
30.09.2025
Розробка обгорток для API: автоматична чи ручна
Якщо вашому продукту пощастить мати HTTP API, а потім ще й стане питання, що варто не тільки мати “голий” API, а ще й надати обгортки-SDK, то ви станете перед вибором. Або робити кожну обгортку власноруч, або згенерувати автоматично.
Звісно, щоб згенерувати автоматично, доведеться спочатку оголосити схему API. Скоріше за все, це буде схема OpenAPI, але є й інші варіанти. (Пишіть, що пробували.)
Зазвичай схема — це гарний спосіб згенерувати документацію, бо фактично документація — це і є гарно відформатована схема. Але таке ставлення до схеми закладає нам проблеми на майбутнє. Бо ми починаємо робити зміни до схеми, щоб вона мала гарний вигляд, а це не обовʼязково збігається з чистотою.
А саме, коли потім доведеться розробляти SDK для цього вашого API, може здаватися, що це дуже легко зробити — взяти, наприклад, openapi-generator, який вміє генерувати інтеграції для всіх уявних мов. Тільки як зберешся генерувати, виявиться що згенерований код не дуже красивий. Місцями — прямо гидкий.
Тоді, відповідальний програміст скаже: така якість нас не влаштовує, будемо писати власноруч. На початку вийде дійсно гарно (ну, настільки гарно, наскільки зможете написати.) Проте зовсім скоро виявиться, що підтримувати такі SDK ну дуже дорого, та ще й вони природно починають відставати від API. Користувачу SDK завжди важливіша повнота та актуальність, ніж якість коду. Тому, гадаю, в реальності ані в кого немає можливості дбайливо підтримувати ручні SDK.
Повернемося до генераторів… Виявляється, що якість згенерованого коду дуже залежить від якості схеми. Не можу поки дати ніяких конкретних рекомендацій, окрім того, щоб читати згенерований код та знаходити причини його недосконалостей. Найбільше проблем з обʼєднаними типами, тобто oneOf. Або взагалі, може API з самого початку був погано спроєктованим та незручним. Чи зручним рівно для одного внутрішнього випадку.
Як висновок… Можу порадити вкладати більшість зусиль в якість схеми та самого API, а генерацію обгорток залишити автоматизованою.
29.09.2025
Компʼютерні нотаріуси
Щоразу як хтось із LLM-братчиків “дізнається”, що код точніше за природну мову та дає більш очікувані результати, я не можу втримати посмішки. Правда? А я думав, то програмісти просто зговорилися писати, щоб чесним людям незрозуміло було.
Але ні. Головна перевага коду — це його однозначність. В коді завжди визначена послідовність дій, та зрозуміло, що значить кожне слово. Може, заплутано — так. Втім завжди зрозуміло. Та, як відомо кожному програмісту, компʼютер завжди робить те, що йому скажеш. Головне, правильно сказати.
Ми не перші, хто зіткнувся з такою задачею. Програмування постійно споріднюють з математикою, а для мене тут більше спільного з юриспруденцією. Задача юриста — викласти бажання людей в чіткій формі з єдиною інтерпретацією. Передбачити всі можливі випадки та наслідки. Ну а наша задача — хіба не така сама?
Тільки наївний програміст думає про свою працю, як “казати компʼютеру, що треба зробити”. Та ні… нам потрібний цілий договір! Та на зʼясування всіх подробиць йде більше зусиль, ніж на “щасливий випадок”. Так само як і в договорі: я тобі гроші, ти мені роботу — а решта сторінок про що?
Як і в юристів, левова частка того договору буде нудна та однакова для всіх. Абстракція не рятує нас від всієї писанини, принаймні на поточному рівні. А LLM - рятує. Це для них практично ідеальний сценарій використання: заповнювати нудні місця. Тільки ж все одно треба спочатку зрозуміти, що конкретно ти хочеш досягнути, у всіх подробицях, які дійсно важливі для тебе.
26.09.2025
Apple Shortcuts - досвід реалізації
Сьогодні випустив невеличке для мене, значне для користувачів оновлення для Reminders2JSON. (Нагадаю, що це маленька утиліта для експорту всього змісту Apple Reminders у JSON - наприклад, щоб мати резервну копію.) Отже, тепер ту ж саму дію можна запустити через Shortcuts. А значить — легко автоматизувати.
Впровадити Shortcuts виявилося ще простіше, ніж я думав. Достатньо оголосити клас, який реалізує протокол AppIntent. В складі цього протоколу — дати йому людську назву, а також оголосити обробник. І все! Після цього наша дія сама зʼявляється у Shortcuts.
Звісно, на відміну від діалогового режиму, тут я нікуди результат не зберігаю, а просто повертаю рядком. Далі вже користувач сам — хоче у файл збереже, хоче надішле поштою, а може взагалі цей JSON розбере та буде щось робити з ним. На ілюстрації — скрипт, який я вже налаштував собі для щоденного резервного копіювання.
Параметри дії оголошуються декларативно, а потім обробник просто до них звертається.
@Parameter(title: "Include Completed") var includeCompleted: Bool
А сам обробник виглядає десь так:
func perform() async throws -> some ReturnsValue<String> {
let jsonData = try await RemindersExportService.exportReminders(includeCompleted: includeCompleted)
let jsonString = String(data: jsonData, encoding: .utf8) ?? ""
return .result(value: jsonString)
}
Тобто все простіше нікуди. До речі, Data
(тобто двійковий масив) повернути не можна. Що трохи цікаво, бо не знаю, як Shorcuts працюють з дійсно двійковими даними. Може, через файли?
А ще я майже адаптував Reminders2JSON під iOS, але то вже на вихідних.
25.09.2025
DSA для App Store - нарешті пройдено!
Майже рік рівно минув з того дня, як я почав виконувати вимоги для публікації в європейських App Store, а точніше, вимог Digital Services Act. А саме, того, що я повинен публікувати контактну адресу та телефон — щоб обурені користувачі могли написати мені паперового листа. От, нарешті, вчора мені погодили та мої застосунки повернулися в App Store.
З контактним телефоном та тим паче імейлом ніяких проблем немає, як я писав минулого разу. Зате з адресою — проблеми величезні. Справжню адресу я публікувати точно не збирався. Потрібна інша адреса, документально затверджена.
Початковий план був — відкрити абонентську скриню. Укрпошти, звісно, бо іншої офіційної поштової системи в нас немає. На жаль, тут ціла низка проблем. Почнемо з того, що я поки не отримав на неї жодного листа! Хоч і намагався. По-друге, підтримка Укрпошти мало допомагає (і це на місцевому головпоштамті, а не у випадковому відділенні.) За регламентом я можу отримати паперовий договір про оренду. Але його видають не то тільки юрособам, не то тільки в Києві, мені так і не вдалося отримати чітке пояснення. Зрозуміло тільки, що абонентська скриня мені не допоможе.
Дуже хотілося б почути від когось про успішний досвід з цими скринями, та зрозуміти, що ж я упустив.
Якийсь час я шукав варіанти представництва чи посередництва, щоб адреса була не моя, а умовного секретаря десь у Європі. Такої послуги не знайшов.
Тоді, нарешті, здався та пішов прямим шляхом. Спочатку — орендувати офіс. Як виявилося, є така послуга — віртуальний офіс, або юридична адреса. Це справжня фізична адреса, на яку може надходити кореспонденція, але без привʼязаної “житлової площі”. Коштує це кілька тисяч гривень на рік, тобто навіть дешевше, ніж той Apple Developer Account, якщо вже порівнювати.
Головне для мене, що оренда юридичної адреси значить отримання договору, на якому написано, що вона моя. Втім, тут чекав наступний етап квесту: Apple не приймає документи українською. Або принаймні попереджає, що “я беру відповідальність за автоматичний переклад”, але фактично підтримка закривала мій запит без нотаріально завіреного перекладу.
Що ж, йдемо далі прямим шляхом та замовляємо переклад. На то є онлайн-агенції, не потрібно навіть ходити нікуди. Агенція перекладає та навіть надсилає завірену копію Новою Поштою. Хоча мені, звісно, достатньо й PDF, який я й надіслав черговим запитом до Apple.
Якщо думаєте, що того було достатньо, то залишився останній бос: пояснити підтримці, що власне складає з себе документ. Бо якщо я правильно зрозумів, вони не дуже вдивляються. Я навіть подумав, що знову не вдалося… аж от після пояснень мені погодили!
Тепер, сподіваюся, воно того було варте. Потрібно лише відбити витрачені гроші продажами у ЄС. :)
24.09.2025
Чому я не роблю власних проєктів на Ruby on Rails
Хоч я вже… дай порахую… років 16 чи 17 працюю із Ruby on Rails, всі власні проєкти на цьому фреймворку давно згорнулися. Та починати нові я не сподіваюся. Теж саме стосується й Mastodon та будь-яких інших продуктів OSS.
Все через те, що Rails - дуже “важка” технологія, щоб її хостити. Перше, що помітиш — вона потребує купу оперативної памʼяті. Просто, щоб застосунок був запущений. І це відразу збільшує ціну хостингу, навіть якщо застосунком ніхто не користується. Також до Rails доведеться додати базу даних, яка теж любить памʼять.
Для комерційного продукту хостинг Ruby on Rails цілком прийнятний та з масштабуванням ця проблема памʼяті скоріше зникає, бо памʼять іде на базовий стан застосунку, а не на обробку запитів. Зате для розробки як хобі кожний застосунок на Rails буде бити по кишені. Власне, через це я поступово закинув всі свої проєкти на Rails, або переписав.
А ще мені ніколи не подобалося, що для розгортування Rails потрібно завантажити на сервер вихідний код кожної залежності. Хоча ця проблема шириться на всі інтерпретовані мови. Я, особисто, люблю коли проєкт компілюється в один файл.
Треба зазначити, що останнім часом є покращення - Ruby on Rails 8 минулого року зробили кілька кроків до повноцінної підтримки SQLite; позаминулого зʼявилася офіційна підтримка Docker (краще дуже пізно, ніж ніколи.) Але моя думка залишається незмінною.
23.09.2025
Як працюють Apple Shortcuts
Трохи вирішив зʼясувати, як воно все влаштоване. Звідки беруться дії в Shortcuts та як додати власні?
(Якщо ти ніколи не бачив Shortcuts: це система візуального програмування, недалека від Scratch. Там є зручності імперативного програмування — змінні, умови, цикли. Але головним чином програма складається з викликів дій різних застосунків та передавання даних між ними.)
Виявляється, під капотом в Shortcuts сидить фреймворк App Intents
. Це повноцінна модель API для твого застосунку. В прямому сенсі — через Intents можна експортувати виклики функцій з параметрами та навіть із власними типами даних. Та ще й документувати їх. Та відповідальні розробники цим користуються.
Приємно, що App Intents побудований на Swift, бо минула технологія - AppleScript - потребувала неприємних файлів конфігурації, які я так і не опанував. А також, як і більшість сучасних технологій Apple, код App Intents працює на всіх платформах.
Також App Intents неначе можна викликати ще й через Siri та Spotlight, але я, чесно, ніколи цим не користувався та навіть про це не знав.
Затьмарює тільки те, що застосунки не можуть викликати дії інших застосунків напряму. Це дозволено тільки системним інструментам. Як я розумію, задля безпеки — бо моделі дозволів тут немає (та не потрібно, поки кожна дія викликана користувачем.) Зате застосункам доступні готові скрипти Shortcuts. Ба більше, для них навіть є CLI. Але спочатку користувач мусить сам створити цей скрипт.
22.09.2025
Система пріоритизації WiLSoN
Вигадав для себе систему пріоритизації задач на заміну старої Must, Should, Could, Won’t. Ну бо не тільки скорочення не подобається, але ще й така схема оцінювання часто дає хибу: наприклад, в проєкті може бути багато задач Must для остаточного випуску, але не потрібних для MVP. Різниця між Should та Could надто розмита та мало корисна. А Won’t хоч звучить красиво, але рідко застосовується за призначенням, бо шкода відмовлятися від непрактичних, зате привабливих смаколиків.
А головне, для мене, що ця система мало казала про порядок виконання задач. За довгі роки практики я ледве можу згадати випадки, де я робив Must перед Should тільки тому, що це Must. Тому у своїй системі я приділив увагу саме порядку виконання.
Wild - дико — це задачі, які може й звучать цікаво, але зовсім нереалістично. Може, це величезна купа роботи. Може, це щось не дуже цінне. Одним словом, балансу “ціна-вигода” тут немає. Такі задачі можна переглядати раз на квартал, бо згодом нереальне може стати досяжним, та й пріоритети змінюються. (Звісно, зовсім зайві задачі немає сенсу зберігати.)
Later - потім — тут те, що ми точно хочемо зробити, але з якоїсь причини незрозуміло, коли. Знову — може, це великий вклад зусиль, а може, зараз ця задача не в пріоритеті та є щось важливіше. Цей список переглядаю раз на місяць — якщо справи рухаються, то його рано чи пізно треба зробити.
Soon - скоро — ці задачі ми готуємо до виконання, бо час для них настав. Але вони не горять та можуть почекати, поки звільниться ресурс. Власне, наявність цього списку каже, що ресурс в нас не нескінченний, та краще не починати нових справ, доки не закінчиш початі. Втім, має сенс детально планувати ці задачі вже зараз, зокрема щоб більше про них зрозуміти… та можливо, відкласти на потім? Або переглядати раз на тиждень, щоб поступово починати.
Now - зараз — це або те, що ми вже робимо, або те, що мусимо почати негайно. Звісно, хотілося б щоб задачі починалися тільки заплановано, але всі знають, що щодня на тебе звалюється щось нове та негайне. Як писав вище, важливо розрізняти, що дійсно потрібно вже почати, а що почекає своєї черги.
А разом ці категорії складаються в красиву назву WiLSoN. По-перше, порядок літер тут вірний. По-друге, голосні навіть правильно продовжують слова (Wild
та Soon
.) По-третє, Wilson навіть має непоганий тематичний зміст: “народжений волею”. Ще б перекласти влучно українською — та я був би повністю задоволений.