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

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

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

14.10.2025

Правильні помилки в API

Чи знаєте ви, що існує аж цілий RFC про повернення помилок з API? Ось: RFC 9457 - Problem Details for HTTP APIs. Виглядають ці помилки трохи дивно, як на мене:

{
  "type": "https://docs.leonid.codes/no-mana",
  "title": "We require more mana",
  "detail": "Teleportation spell requires 20 mana, but you have 15",
  "spell": "teleportation",
  "mana_required": 20,
  "mana_available": 15
}

Тут type - це URL, який може вказувати на документацію про помилку. Що мені дуже подобається. title - назва помилки, яка є незмінною. А detail - текстове пояснення, яке вже може містити подробиці про конкретний випадок. Нарешті, окрім цих полів можна додавати будь-які власні.

Альтернативою є формат помилок зі специфікації JSON API. Причому назва JSON API може й загальна, але специфікація досить конкретна та суворо обмежує структуру відповідей. Щодо помилок, то вона вимагає на верхньому рівні ключ errors, а в середині — цілий масив обʼєктів помилок — кожна з яких недалека від RFC 9457.

Логіка така, що в такому повідомленні є й частини, які може прочитати людина, зокрема й в журналі, а також є інформація для машинної обробки. Але чого тут немає, так це success: true або status: "ok", які я звик бачити. Знаєте, чому?

Бо найважливіша частина повідомлення про помилку — це статус HTTP. Яких у нас великий вибір. Та вкрай важливо визначити та передати доцільний статус вашої помилки. Тоді зміст відповіді вже несе суто уточнювальний характер.

Є така тенденція ігнорувати статус та дивитися тільки на зміст. Наприклад, на фронтенді так може “простіше” робити. Чув і такі думки, що статуси HTTP тільки для критичних помилок, а якщо сервер “в тямі”, то може вже давати 200-й із success: false, errors: .... Це все від нерозуміння основ протоколу HTTP.

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


13.10.2025

React Native: екосистема на піску

Пів року тому я намагався оновити Сінтру до сучасних версій React Native та всього повʼязаного. Не виходило. Нарешті, на цих вихідних взявся за це серйозно та ціною кількох годин все ж поборов!

Корінна проблема React Native в тому, що його існування є, певною мірою, хаком. Так, в теорії, писати під всі системи на знайомому JS/TS - дуже зручно. Але реалізація проста тільки в парадигмі компʼютера, де кожна мова рівноправна. Принаймні на iOS, це точно не так (про Android тут нічого не знаю.) JavaScript виконувався раніше на рушії, який не розрахований на повноцінні застосунки, та упирався в суттєві гальма.

Тому історія розвитку React Native - це поступовий пошук кращої моделі виконання для застосунків, написаних на JavaScript. Зверни увагу, остання “нова ера” в цій історії розпочалася 5 днів тому.

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

Наприклад, що було з Сінтрою цього разу: React Native Navigation нарешті вийшов із підтримкою… але не найновішого RN, звісно, а тільки версії 0.77, та яка січня 2025. Тепер React Native Firebase поки не підтримує стандартний режим збірки застосунків та потребує статичних бібліотек. На жаль в RNN поки тільки наздоганяють стандартні вимоги, тому в них немає прямої сумісності зі статичною збіркою. На щастя, хтось зробив патч для поточної версії RNN, яка робить її сумісною. Та після застосування цього патчу, нарешті, принаймні зібралися залежності.

О, і ще, все це хоча б запускається на новому XCode 26, бо в цьому й була мета капітальних оновлень. Можна рухатись далі.


10.10.2025

LLM для аналізу логів

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

Довго планував щось із цим зробити — хоч моніторинг налаштувати ж треба? А руки ніяк не доходили, простіше потерпіти.

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

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

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

А проблеми були такі. Спочатку вимкнув “roaming assistant”. Це така функція для Mesh-систем, що роутер викидає тебе з низьким сигналом, щоб ти приєднався до іншого вузла. Але тут інших вузлів немає, тож роутер марно переривав підключення. Потім так само вимкнув “smart connect” - це коли роутер пропонує тобі 2.4GHz чи 5GHz за власним вибором. Бо ті ж проблеми, надто нестабільно виходить. Ще знизив transmit power, бо як виявляється він був викручений на максимум, а це, парадоксально, погано впливає на надійність.

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


09.10.2025

Reminders2JSON на iOS

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

Власне, щоб запустити такий маленький застосунок з macOS на iOS, потрібно мінімум дій. В цій екосистемі дуже зручне рішення для коду на різні платформи. Старе, як C: #if os(macOS) та поїхали. Код SwiftUI на 95% однаковий, проте окремі аспекти зустрічаються різні. Наприклад, на macOS є діалог збереження файлу, а на iOS - поширення (де технічно можна надіслати той файл будь-куди.)

І це вже заслуга SwiftUI, що такі особливості навішуються на спільну загальну структуру застосунку, і це в мові з сильною типізацією.

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

І найскладніше — це було організувати xcodegen та fastlane. Звісно, хотілося зробити красиво та менше повторювати — а запрацювало тільки тоді, коли я просто продублював конфігурацію.

Цікавий досвід — в Apple серйозно якісний апарат перенесення застосунків. Прямо хочеться ще щось перенести.


08.10.2025

Зроби спочатку дурний розвʼязок

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

В письменників є правило: “напиши гидку першу чернетку”. Бо треба з чогось почати. Ніхто не пише ідеально з нуля. Та замість того, щоб чекати натхнення та уявляти твір в голові, швидше буде забути про стандарти, написати хоч щось. Тоді тобі буде від чого відштовхнутися, а це — виявляється — незрівнянно легше.

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

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


07.10.2025

Ознаки дружнього до розробників продукту

Що дозволить мене, як розробнику, довіряти вашому продукту? Любити його та рекомендувати іншим? Хотілося б щоб все вирішувалося крутим маскотом та максимальною підтримкою ШІ, але… Ось на що я звертаю увагу:

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


06.10.2025

Фактори успіху GTD

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

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


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, а шкода.