Стендап Сьогодні
Що я зробив, що я хочу зробити, і що це все значить.
Повсякденні здобутки в форматі стендапу.
Детальніше в статті
Підписатись на RSS
📢
Канал в Telegram @stendap_sogodni
🦣
@stendap_sogodni@shevtsov.me в Федиверсі
05.09.2025
Світ на тобі не закінчується
Значить, є проблема. З якоїсь причини пару годин був суттєво уповільнений один HTTP сервіс. Треба знайти, чому.
Ускладнення в тому, що це уповільнення ні з чим більше не корелює. Запитів більше не стало. Кількість оброблених байтів теж. Процесор в нормі. Памʼять в нормі. Але щось змусило сервіс відповідати повільно.
Єдине, що хоч трохи вказувало на причину - Sentry підказав, що час витрачається на читання запитів від клієнта. Цікаво! (Інструментація дала дивіденди.) За моїм досвідом, повільне читання завжди пояснюється повільним клієнтом. Може, в нього погане підключення. А може, це цілеспрямована атака.
Ну й давай шукати — якщо це атака — то хто її провів? З яких адрес? Вже ріжу статистику і так, і сяк, а нічого ясного не бачу. Та й взагалі, якщо атака дійсно уповільнила запити для всіх клієнтів, то яким способом? Та чому це не корелює більше ні з якими метриками?
Поки шукав правду в статистиці, спала на думку інша ідея. Вхідні запити до нас обслуговуються ще й сервісом переднього краю, та власне, інфраструктурою провайдера. Тож, думаю, перевірю, чи не було у них інцидентів. Так, про всяк випадок.
Та яке ж було моє здивування, що інцидент був! Та рівно на той час, який ми бачили в метриці. Та складався рівно з того, що мережа була перенавантажена. А значить, запити йшли до нас надто повільно. А ми вже обробляли їх як тільки, так відразу.
Отак дивишся в дані, будуєш собі якісь теорії, а потім за один момент бац — і все зрозуміло.
Гарних вам вихідних без інцидентів!
04.09.2025
Продумай до кінця (чи до початку?)
Одна з цікавих ідей GTD: будь-яка справа ділиться на два етапи: думати та робити. Робити, коли все ясно — достатньо просто (може, не легко.) Мабуть, кожний може згадати час, коли нехай важка, але абсолютно визначена робота була навіть в задоволення. Але в нормі не все ясно. Та ми, замість того, щоб подумати та прояснити, думаємо про справи, та ухиляємося від невизначеності.
Щоб полегшити собі життя, можна продумати справи, а не думати про справи. Це значить — поставити та знайти відповіді на всі запитання потрібні… уважно: для того, щоб зробити наступну дію. Продумати все до кінця під час не те що непрактично, а взагалі неможливо.
Питань виникає багато, деякі очевидні, деякі несподівані. Наприклад: щоб “шукати нові шини” треба хоча б знайти потрібний розмір. А може поговорити з дружиною про бюджет. А може взагалі вони ще не потрібні? Як це перевірити? А який виробник гарний? І так далі. Поки не будеш точно знати — що далі робити.
(До речі, це відчуття оманливе, та його в собі ще потрібно виплекати. Як легкий показник — якщо щось не робиться певний час — спитай себе: чи є обʼєктивні причини відкладати, або “руки не доходять”? В другому разі шукай невизначеність.)
(Ще один гарний критерій - чи можу я доручити цю дію комусь іншому? Якщо ні - значить, не все ще вирішено.)
Звісно, щоб не ходити по колу, всі ці відповіді варто записувати хоч кудись. Тоді буде зʼявлятися визначеність та впевненість. Навіть без всякого GTD можна завести звичку брати аркуш (чи текстовий документ) та проводити інтервʼю сам з собою. А ще й заготувати список питань заздалегідь.
В програмуванні такий підхід особливо потрібний, бо в нас “думати” та “робити” тісно повʼязані. Можна сказати, що на кожному рядку доводиться щось вирішувати. Але в таких дрібних рішеннях немає проблеми. Зате варто продумати: спочатку перелік вимог, потім з нього — загальну архітектуру, потім — план виконання. І на кожному з цих етапів будуть виникати питання, які здатні повністю загальмувати наївну розробку в лоб без продумування. Без цього сидиш тиждень, дивишся в порожній екран та не можеш написати ані рядка. Проблема не в натхненні — а в невизначеності на одному з рівнів. Знайомо?
03.09.2025
Помічник ШІ для прорізного аналізу проєкту
Знайшов вигідну галузь використання агентів (в моєму випадку це все ще Cursor.) Стосується це в першу чергу вебпроєктів, в яких, як знаємо, використовується цілий ворох технологій та мов: Ruby для бекенду, JS для фронтенду, CSS для оформлення, SQL для запитів. Поясню на прикладах.
Є інтеграційний тест — мені не знайомий. Він валиться на натисканні такої-то кнопки. Чому? Зазвичай тут доведеться майстерно грепати, та ще й знати звʼязки між шарами. Натомість виділяю рядок, тисну Cmd+L та задаю запит: describe code flows that are triggered by this action. Cursor знаходить в коді React цю кнопку — навіть якщо вона називається “Continue”, то він бачить її в контексті всього тесту. Сила LLM - в розумінні синонімів. Навіть коли тест називається “Profile”, а компонент - Account, LLM знайде його проміж інших. Звідти описує, що саме робить ця кнопка, які запити на бекенд, і далі розкладає по шарах бекенду, навіть до асинхронних задач. Це надзвичайно потужна можливість для такої доступності.
Або, бачу по CSS що ми використовуємо шрифт, але не можу зрозуміти, де саме. Замість того щоб відстежувати по кроках від CSS - до компонента, від компонента - до ієрархії, і так далі, питаю: which feature spec would load a page that uses the FooBar font? І так само отримую опис сценаріїв, де шрифт може бути залучений, та навіть конкретний приклад з тестів. Знову, це заміняє пів години ретельного пошуку та відкидання зайвого.
Так само можна робити з попередженнями в JS, які незрозуміло звідки зʼявляються в консолі. Або й в журналі з бекенду. І зовсім не обовʼязково для LLM потрібний прямий збіг за текстом.
Я взагалі завжди пишався своєю власною здатністю відстежити такі звʼязки, але ось в чому різниця. Коли я вручну за годину чи пів знаходжу причину помилки, то зможу виправити тільки найбільш гострі з них. А тепер вистачає можливостей ще й на деякі неприємні, але терпимі ситуації - як-от випадково невдалі тести.
02.09.2025
Легкий спосіб пришвидшити збірку у 2 рази
Взагалі знаєте, що є неформальне правило, що збірка коду на CI повинна тривати менше за 10 хвилин? Тоді в розробника менше шансів втратити контекст, поки тести йдуть.
Так, певного успіху можна досягнути тим, щоб зробити збірку паралельною, та це точно варто зробити в першу чергу. Втім, з часом паралельність втрачає дію: коли підготовчі дії займають вагому долю. Тож потім починається важка кропітка робота з прискорення цих дій: десь кеш, десь прибирання зайвого. Все це, на жаль, дає мізерні покращення.
Є й інший шлях: виправляти випадково невдалі тести. Бо дійсно, якщо збірка впала через випадкову невдачу — доведеться запускати її двічі та витрачати у два рази більше часу. А якщо друга спроба теж невдала — то й третій і четвертий раз. Виходить, випадково невдалі тести бʼють по продуктивності значно більше, ніж просто повільна збірка, особливо якщо збірку доводиться перезапускати вручну.
Я тієї думки, що випадково невдалі тести — як і помилки, завжди зʼявляються. Тому й боротьба з ними має системний, а не разовий характер. Як перший крок, почни записувати ті тести, що не вдалися з першої спроби. Тут доведеться відстежувати історію збірок. А можна поступити простіше та записувати всі тести, що не вдалися в головній гілці, бо там вже не повинно бути недоробок. (Правда?)
Є також рішення типу rspec-retry - перезапускати невдалі тести відразу один по одному, на випадок що вони пройдуть. Так, це певна поступка чистоті підходів, але я гадаю, варта того. Заодно можна відразу збирати список тестів, які пройшли з другої спроби.
01.09.2025
Підключення миші з Bluetooth до Mac... без миші!
Маленьке продовження історії з Mac Mini. Взяв з собою стару мишу, щоб не відʼєднувати свою основну. Та тут виявляється, що вона не спарена! А іншої миші немає. Що робити?
Я надіявся, що все працюватиме з клавіатури. Відкрити системні налаштування через Alfred тривіально. Далі намагаюся клавішею Tab добратися до налаштувань Bluetooth… а вона не табулює! Точніше, табулює, але тільки між пошуком та переліком розділів. А у сам розділ, в головну панель не входить.
Я трохи часу побув в шоці. Спробував знайти команди термінала для Bluetooth, але не вийшло. Згадав, що є така штучка, як Mouse Keys - керування мишею з клавіатури — але ж щоб її увімкнути треба так само зайти в налаштування. Хотів залучити AppleScript - та виявилося, що навіть у стандартному діалозі “Дозволити програмі керувати…” табуляція теж не працює!
А потім розшукав, що за замовчуванням на macOS табуляція не проходить по всіх елементах інтерфейсу, а тільки по деякім. На то є налаштування Keyboard navigation… яке, о диво, можливо змінити через термінал! defaults write NSGlobalDomain AppleKeyboardUIMode -int 3 та нарешті я зміг дістатися до потрібних налаштувань та підʼєднати мишу.
Тепер раджу всім перевірити, що у вас цей режим увімкнений, бо хтозна-коли він може знадобитися.
(PS: а ще можна підʼєднатися до Mac Mini вже налаштованим віддаленим доступом з ноутбука чи навіть телефону та тамтешнею мишею зробити все необхідне. Але це вихід для слабаків.)
(PPS networksetup -setairportnetwork en1 SSID PASSWORD, щоб з термінала підʼєднатися до незнайомої мережі. Хоч тут все прямолінійно!)
31.08.2025
Проблеми з i18next
Вчора в підтримку Сінтри надійшов лист про те, що у нас поламані листи підтвердження підписки. По-перше, дякую автору, бо небагато хто б потрудився це зробити. По-друге, чи це ще один випадок недостачі моніторингу та насамперед тестування в проєкті двох людей? Авжеж! Бо помилка сиділа роки з два. (І ні, це далеко не перша підписка за цей час.)
Помилка крилася в неправильному шаблоні для i18next. В нас там цікаве рішення для шаблонів листів. Технічно вони використовують “Handlebars”, але на практиці шаблони стають просто рядками перекладу для i18next.
Причин було відразу дві. Одна прямо зовсім проста — неправильний префікс ключа. Знаєте, в i18next можна написати підписка на $t(subscription_interval.{{interval}}) та підставити туди, наприклад, subscription_interval.monthly. Дуже зручно. Але на це немає перевірок — якщо такого ключа немає в словнику, i18next підставить сам ключ. Що й відбулося, бо я банально переплутав subscription_interval та subscription_period.
Зате рівно тільки що дізнався, що є налаштування відсутніх ключів — в тому числі обробник missingKeyHandler, який міг би кинути помилку.
Друга причина — в моєму форматувальнику дат формат читається з того ж словнику. Дуже зручно (та може варто окремої розповіді.) Але. Словник же ж потребує обраної мови! На фронтенді це працює зрозуміло: у нас є мова користувача, вона встановлена глобально. А на бекенді немає “глобально обраної мови”, бо бекенд надсилає пошту до всіх користувачів. Тому виходило так, що в пошті цей форматувальник не знав мови, використовував хибний формат (знову без помилки…) та видавав сміття.
Розвʼязалося все передачею мови параметром lng: format = i18next.t(format_name, {lng: userLanguage}).
Отак, наче нічого складного, але години дві витратив. До речі, дуже допомогло те, що в мене принаймні були скрипти для генерації тих листів.
30.08.2025
Сучасна спадщина Thief
Після минулого поста про Thief я загадався над питанням — чи є сучасні ігри, які її нагадують? Виявилося, що жанр thieflike практично відсутній. Зокрема виділю, що сучасні ігри дають гравцю або високу мобільність, або можливість битися. А весь смак Thief - в обмеженні й того, й іншого. Та рідко хто відтворює механіки як світла, так і звуку на такому ж рівні, як будь-який Thief.
Зате! (А може — через це!) Як виявилося, фанатська сцена для Thief жива й досі. Причому як мінімум у двох формах.
Є The Dark Mod - це фактично повна реалізація механік Thief на рушії Doom 3 (або id Tech 4, якщо педантично.) З відкритим кодом та абсолютно безплатна! Причому The Dark Mod знаходиться в активній розробці, остання версія вийшла березня цього року. Замість звичайного сюжету тут є можливість завантажувати місії з каталогу, де їх наразі майже 200.
Не впевнений, що без досвіду Thief воно гладко зайде концептуально, але в цілому The Dark Mod, мабуть, найкращий спосіб пограти “в сучасний Thief” з усіма механіками та сучасною графікою.
Але насправді мені в старих іграх заважає керування та зручності, а не власне графіка. Зі старою графікою я готовий жити. Та якщо тобі так само, то для Thief 1 є чудовий мод TFix, який прибирає всі вікові недоліки. Там і рушій оновлений, і широкий екран підтримується, і з керуванням проблем немає. (Аналогічно для Thief 2 є T2Fix. Це взагалі дві дуже схожі гри.)
Та що ще крутіше — на Thief 1 та 2 існує досі жива спільнота фанатських місій! Їхня кількість вражає — на Archive.org є архів у 40 Гб рівнів, що у 20 разів більше за розмір обох оригінальних ігор! Та з TFix їх достатньо закинути в одну теку та запускати спеціальною оболонкою. Взагалі найкращий ресурс для фанатських місій - це форум Through The Looking Glass, але він щось не дуже стабільно працює.
Виходить, Thief приєднується до родини старих ігор, що живі завдяки фанатам — як Quake чи Doom. Дуже приємно.
29.08.2025
Раптові витрати памʼяті та куди дивитися
Довелося цього тижня зʼясовувати, чому застосунок на Rails несподівано почав споживати на кілька гігабайтів памʼяті більше. Поверхнево причина була зрозуміла — бо в цей застосунок зайшла частина іншого застосунку, відбувається таке собі злиття. Та тут й крилася проблема: в проблемний реліз потрапила ціла пачка наче безпечних змін — загальним обсягом десь у 20 тисяч рядків. Ну й що з нею робити?
Перша теорія — що це сам код витрачає стільки памʼяті — мені відразу здалася неправдоподібною. Код взагалі не дуже споживає памʼять, навіть в інтерпретованій мові. Продукти виконання коду, тобто стан — можуть. Але тоді стає питання — де той стан зберігається, бо зазвичай у вебзастосунку весь стан відкидається по закінченню запиту.
Почав перевіряти нові константи та класові змінні — ну, знайшов кілька сотень регулярних виразів. Зробив швиденько скрипт, щоб просто завантажити ці вирази та подивитися витрати памʼяті — а вони мінімальні. Ну і якщо чесно, то прямо гігабайти стану це треба ще вигадати, ніхто такого в Ruby не робить. Втім, треба ж шукати причину…
А варто було з усіх змін відразу подивитися на зміни в залежностях. А тут — в проєкт підтягнувся славетний гем mini_racer - обгортка рушія V8 для Ruby. Відчуваєте вагу? V8 це вам не купка регулярок!
Отже, виявилося, що з деяких причин версія mini_racer була старуватою, та мала проблеми із Ruby 3.4, або може з свіжим сервером Puma - в будь-якому разі, щось заважало цьому гему звільняти старі контексти V8, та вони й засмічували памʼять. Оновленням гему все розвʼязалося.
У таких випадках раптових змін завжди є конкретна причина, залишається її знайти.
28.08.2025
Розробка на iOS для себе
Може хтось хотів би почати розробляти для iOS - просто для себе щось зробити — але зупиняє знаменитий цінник в $99 на рік за програму розробників. Хотів би прояснити кілька питань — бо не все так погано.
Програма розробників потрібна для розповсюдження застосунків через App Store. Без неї не вийде навіть вивантажити в Test Flight - сервіс для бета-тестування. Це дійсно так.
Але, окрім того, є також встановлення ad hoc, та воно не потребує платної програми. Щоб встановити застосунок на свій iPhone, достатньо мати компʼютер з macOS, встановити на нього XCode (безплатно з App Store), та авторизуватися будь-яким обліковим записом. Потім підʼєднуєш до компʼютера айфон, дозволяєш доступ туди-сюди, і все — можна обирати запуск застосунку на iPhone. А запуск — то і є встановлення, бо після відʼєднання застосунок залишається.
Такий застосунок житиме тиждень. Точніше, тиждень — це термін профілю забезпечення, як профіль вибіжить — то застосунок стане недоступним. Щоб повернути його. достатньо ще раз запустити з XCode. (До речі, з платною підпискою цей термін збільшується до року.)
На одному айфоні можна встановити лише три застосунки. Це теж обмеження безплатної версії. Хоча, бачите, для того, щоб зробити застосунок для себе чи просто випробувати середовище цього більше ніж достатньо.
Також застосунки можна встановлювати й на чужі пристрої, якщо вони не проти погодитися “довірити дані цьому компʼютеру”. Не обовʼязково айфону та маку належати одному акаунту. Хоча, мабуть, більше обмеження — це те, що щотижня доведеться встановлювати наново. Але якщо є бажання зробити щось сімейне — то й це цілком можливо.
До речі, за таким принципом працює AltStore та кілька інших “альтернативних майданчиків” для застосунків. Та для них теж не потрібна платна підписка, якщо вище зазначені обмеження влаштовують.
Щоб все це перевірити, довелося встановити XCode дружині, бо в мене-то платна підписка є. Тепер вона теж трохи розробник. А от — для повноти — з дитячим обліковим записом встановлювати застосунки взагалі не дозволено.
27.08.2025
Як зробити проєкт на Go огидним, щоб ніколи більше не писати цією мовою
(Основане, на жаль, на справжньому досвіді.)
Звалити весь код в один пакет. Навіщо нам ці дивні імена з великої літери? Пакети відокремлюють вузли коду. Коли все в одному пакеті — дійсно виходить один великий вузол. І не треба відкладати розбиття на пакети на потім, краще починати новий пакет на кожну потребу, тоді не доведеться його розплутувати.
Використовувати рівночасність без потреби. Я чув, у Go ефективні горутіни — треба брати, щоб було швидше! Памʼятай, що які б вони не були ефективні — а розуміння коду ускладнюють так само. Тому впроваджувати горутіни варто тільки коли послідовне виконання обʼєктивно триває надто довго. Та намагатися відокремити їх від бізнес-логіки, щоб її можна було спокійно зрозуміти та протестувати.
Не писати тестів. В типізованій мові тести не потрібні. Згодний, що типи дозволяють не писати примітивних тестів на узгодженість. Але принаймні типи на рівні Go нічого не кажуть про логіку програми! Тому тести повинні перевіряти логіку виконання, а також — дуже важливо — її документувати.
Використовувати динамічні типи. Типи це чудово. Але я тут читаю JSON, розумієте, тож треба перевіряти кожне поле. Парсер за вас сам перевірить. Динамічні типи та рефлексія — це, певно, найскладніший код в Go, тому їх теж треба виносити в шар абстракції для конкретної потреби, далеко від загального коду. А скоріше — використовувати чужий. Та боже збав вас від “гнучких” методів з динамічними аргументами…
Не слідувати звичаям. Як (моя улюблена особливість JS, Ruby чи Java) робиться тут? Go - це мова з міцними переконаннями. Та це не та мова, яка дозволить вам вигадати власний стиль. Тим паче запозичити стиль з іншої мови, яку ти знаєш. Я не кажу, що звичаї Go найкращі, але принаймні їх доведеться прийняти, а не йти проти них. Думай про це так — без вагань над стилем в тебе буде більше часу на написання корисного коду. Бо це правда.

